device API, WIP
diff --git a/xos/api/tenant/cord/subscriber.py b/xos/api/tenant/cord/subscriber.py
index b33c7ad..21f0567 100644
--- a/xos/api/tenant/cord/subscriber.py
+++ b/xos/api/tenant/cord/subscriber.py
@@ -87,6 +87,45 @@
     def save(self, *args, **kwargs):
         super(CordSubscriberNew, self).save(*args, **kwargs)
 
+class CordDevice(object):
+    def __init__(self, d={}, subscriber=None):
+        self.d = d
+        self.subscriber = subscriber
+
+    @property
+    def mac(self):
+        return self.d.get("mac", None)
+
+    @mac.setter
+    def mac(self, value):
+        self.d["mac"] = value
+
+    @property
+    def identity(self):
+        return {"name": self.d.get("name", None)}
+
+    @identity.setter
+    def identity(self, value):
+        self.d["name"] = value.get("name", None)
+
+    @property
+    def features(self):
+        return {"uplink_speed": self.d.get("uplink_speed", None),
+                "downlink_speed": self.d.get("downlink_speed", None)}
+
+    @features.setter
+    def features(self, value):
+        self.d["uplink_speed"] = value.get("uplink_speed", None)
+        self.d["downlink_speed"] = value.get("downlink_speed", None)
+
+    def save(self):
+        if self.subscriber:
+            dev=self.subscriber.find_device(self.mac)
+            if dev:
+                self.subscriber.update_device(mac, **self.d)
+            else:
+                self.subscriber.create_device(**self.d)
+
 # Add some structure to the REST API by subdividing the object into
 # features, identity, and related.
 
@@ -101,6 +140,21 @@
     account_num = serializers.CharField(required=False)
     name = serializers.CharField(required=False)
 
+class DeviceFeatureSerializer(serializers.Serializer):
+    uplink_speed = serializers.IntegerField(required=False)
+    downlink_speed = serializers.IntegerField(required=False)
+
+class DeviceIdentitySerializer(serializers.Serializer):
+    name = serializers.CharField(required=False)
+
+class DeviceSerializer(serializers.Serializer):
+    mac = serializers.CharField(required=True)
+    identity = DeviceIdentitySerializer(required=False)
+    features = DeviceFeatureSerializer(required=False)
+
+    class Meta:
+        fields = ('mac', 'identity', 'features')
+
 class CordSubscriberSerializer(PlusModelSerializer):
         id = ReadOnlyField()
         humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
@@ -132,7 +186,9 @@
     custom_serializers = {"set_features": FeatureSerializer,
                           "set_feature": FeatureSerializer,
                           "set_identities": IdentitySerializer,
-                          "set_identity": IdentitySerializer}
+                          "set_identity": IdentitySerializer,
+                          "get_devices": DeviceSerializer,
+                          "add_device": DeviceSerializer}
 
     @classmethod
     def get_urlpatterns(self, api_path="^"):
@@ -142,6 +198,10 @@
         patterns.append( self.detail_url("identity/$", {"get": "get_identities", "put": "set_identities"}, "identities") )
         patterns.append( self.detail_url("identity/(?P<identity>[a-zA-Z0-9\-_]+)/$", {"get": "get_identity", "put": "set_identity"}, "get_identity") )
 
+        patterns.append( self.detail_url("devices/$", {"get": "get_devices", "post": "add_device"}, "devicees") )
+        patterns.append( self.detail_url("devices/(?P<mac>[a-zA-Z0-9\-_:]+)/$", {"get": "get_device"}, "getset_device") )
+        patterns.append( self.detail_url("devices/(?P<mac>[a-zA-Z0-9\-_:]+)/(?P<feature>[a-zA-Z0-9\-_]+)/$", {"get": "get_device_feature", "put": "set_device_feature"}, "getset_device_feature") )
+
         patterns.append( url(self.api_path + "account_num_lookup/(?P<account_num>[0-9\-]+)/$", self.as_view({"get": "account_num_detail"}), name="account_num_detail") )
 
         patterns.append( url(self.api_path + "ssidmap/(?P<ssid>[0-9\-]+)/$", self.as_view({"get": "ssiddetail"}), name="ssiddetail") )
@@ -208,6 +268,29 @@
         subscriber.save()
         return Response({identity: IdentitySerializer(subscriber.identity).data[identity]})
 
+    def get_devices(self, request, pk=None):
+        subscriber = self.get_object()
+        result = []
+        for device in subscriber.devices:
+            device = CordDevice(device)
+            result.append(DeviceSerializer(device).data)
+        return Response(result)
+
+    def add_device(self, request, pk=None):
+        subscriber = self.get_object()
+        ser = DeviceSerializer(subscriber.devices, data=request.data)
+        ser.is_valid(raise_exception = True)
+        newdevice = CordDevice(subscriber.create_device(ser.validated_data))
+        subscriber.save()
+        return Response(DeviceSerializer(newdevice).data)
+
+    def get_device(self, request, pk=None, mac=None):
+        subscriber = self.get_object()
+        device = subscriber.find_device(mac)
+        if not device:
+            return Response("Failed to find device %s" % mac, status=status.HTTP_404_NOT_FOUND)
+        return Response(DeviceSerializer(CordDevice(device)).data)
+
     def account_num_detail(self, pk=None, account_num=None):
         object_list = CordSubscriberNew.get_tenant_objects().all()
         object_list = [x for x in object_list if x.service_specific_id == account_num]