ctucx.git: nimtradfri

[nimlang] incomplete library to interact with ikea tradfri-gateways

commit a0d4508a39c273990fc3826d1188e0f89fd4e8a3
parent 3f32d9cc93b3117f6ef1c0a9baff2fb28dfde00c
Author: ctucx <c@ctu.cx>
Date: Tue, 15 Sep 2020 14:06:52 +0200

update
9 files changed, 329 insertions(+), 221 deletions(-)
M
coapClient.nim
|
2
+-
D
device.nim
|
212
-------------------------------------------------------------------------------
M
deviceHelpers.nim
|
9
++++++++-
A
devices.nim
|
215
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
groupTypes.nim
|
24
++++++++++++++++++++++++
A
groups.nim
|
55
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
listGroups.nim
|
13
+++++++++++++
M
mappings.nim
|
12
+++++++-----
M
tradfri.nim
|
8
++++++--
diff --git a/coapClient.nim b/coapClient.nim
@@ -12,7 +12,7 @@ proc makeCoapRequest* (host: string, port: int, reqMethod: string, user: string,
     arguments.add($reqPayload)
 
   arguments.add("coaps://" & host & ":" & $port & endpoint)
-
+  
   let reqResult = execProcess("coap-client", args = arguments, options = {poUsePath})
 
   try:
diff --git a/device.nim b/device.nim
@@ -1,212 +0,0 @@
-import json, strutils, options
-
-import coapClient
-import gatewayTypes, deviceTypes
-import mappings, helpers
-
-proc parseDevice (data: JsonNode): TradfriDevice = 
-  let deviceType = TradfriDeviceType(data[ParameterType].getInt)
-  var state:       TradfriDeviceState
-
-  case deviceType:
-    of Remote:
-      state = TradfriDeviceState(
-          kind:                     deviceType,
-          remoteSupported:          false
-        )
-
-    of slaveRemote:
-      state = TradfriDeviceState(
-          kind:                     deviceType,
-          slaveRemoteSupported:     false
-        )
-
-    of Lightbulb:
-      state = TradfriDeviceState(
-          kind:                     deviceType,
-          lightPowered:             parseBool($data[DeviceLightbulb][0][ParameterPowerState].getInt),
-          lightBrightness:          data[DeviceLightbulb][0][ParameterDimmerValue].getInt
-        )
-
-      #get hue and saturation (only for RGB-bulbs)
-      if data[DeviceLightbulb][0].hasKey(ParameterHue):
-        state.lightHue        = some(data[DeviceLightbulb][0][ParameterHue].getInt)
-        state.lightSaturation = some(data[DeviceLightbulb][0][ParameterSaturation].getInt)
-      else:
-        state.lightHue        = none(int)
-        state.lightSaturation = none(int)      
-
-
-      #get color hex-value (for white-spectrum and RGB, but only some presets)
-      if data[DeviceLightbulb][0].hasKey(ParameterColorHex):
-        state.lightColorHex   = some(data[DeviceLightbulb][0][ParameterColorHex].getStr)
-      else:
-        state.lightColorHex   = none(string)
-
-      #get colorX and colorY values (can be used to set any color on RGB bulbs)
-      if data[DeviceLightbulb][0].hasKey(ParameterColorX):
-        state.lightColorX     = some(data[DeviceLightbulb][0][ParameterColorX].getFloat)
-        state.lightColorY     = some(data[DeviceLightbulb][0][ParameterColorY].getFloat)
-      else:
-        state.lightColorX     = none(float)
-        state.lightColorY     = none(float)
-
-
-      #get color-specturm value
-      if data[DeviceLightbulb][0].hasKey(ParameterColorTemperature):
-        state.lightColorTemperature = some(data[DeviceLightbulb][0][ParameterColorTemperature].getInt)
-      else:
-        state.lightColorTemperature = none(int)
-
-      #determine type of bulb
-      if state.lightHue.isSome:
-        state.lightSpectrum = RGB
-      elif state.lightColorTemperature.isSome:
-        state.lightSpectrum = White
-      else:
-        state.lightSpectrum = None
-      
-    of Plug:
-      state = TradfriDeviceState(
-          kind:                    deviceType,
-          plugPowered:             parseBool($data[DevicePlug][0][ParameterPowerState].getInt),
-          plugDimmer:              data[DevicePlug][0][ParameterDimmerValue].getInt
-        )
-
-    of motionSensor:
-      state = TradfriDeviceState(
-          kind:                    deviceType,
-          motionSensorSupported:   false
-        )
-
-    of signalRepeater:
-      state = TradfriDeviceState(
-          kind:                    deviceType,
-          signalRepeaterSupported: false
-        )
-
-    of Blind:
-      state = TradfriDeviceState(
-          kind:                    deviceType,
-          blindPosition:           data["3"][ParameterBlindPosition].getFloat,
-          blindTrigger:            data["3"][ParameterBlindTrigger].getFloat
-        )
-
-    of soundRemote:
-      state = TradfriDeviceState(
-          kind:                    deviceType,
-          soundRemoteSupported:    false
-        )
-
-  return TradfriDevice(
-      `type`:      deviceType,
-      id:          data[ParameterId].getInt,
-      name:        data[ParameterName].getStr,
-      alive:       intToBool(data[ParameterAlive].getInt),
-      createdAt:   data[ParameterCreatedAt].getInt,
-      lastSeen:    data[ParameterLastSeen].getInt,
-      state:       state,
-      info:        TradfriDeviceInfo(
-        manufacturer:    data["3"]["0"].getStr,
-        modelNumber:     data["3"]["1"].getStr,
-        serialNumber:    data["3"]["2"].getStr,
-        firmwareVersion: data["3"]["3"].getStr,
-        power:           TradfriPowerSource(data["3"]["6"].getInt),
-        battery:         data["3"]{"9"}.getInt
-      )
-    )
-
-
-proc operateDevice* (device: TradfriDevice, action: TradfriDeviceAction): bool = 
-  var requestParams = %* {}
-
-  template CheckDeviceType(typeId: TradfriDeviceType) = 
-    if device.`type` != TradfriDeviceType(typeId):
-      raise newException(ValueError, "Wrong action for this Devicetype")
-    
-  case action.kind:
-  of DeviceRename:
-    requestParams.add(ParameterName, %action.deviceName)
-
-  of LightSetPowerState:
-    CheckDeviceType(Lightbulb)
-    
-    requestParams.add(DeviceLightbulb, %* [{
-      ParameterPowerState:          boolToInt(action.lightPowerState),
-      ParameterTransitionTime:      action.transitionTime
-    }])
-
-  of LightSetBrightness:
-    CheckDeviceType(Lightbulb)
-
-    requestParams.add(DeviceLightbulb, %* [{
-      ParameterDimmerValue:         action.lightBrightness,
-      ParameterTransitionTime:      action.transitionTime
-    }])
-
-  of LightSetColorHex:
-    CheckDeviceType(Lightbulb)
-
-    requestParams.add(DeviceLightbulb, %* [{
-      ParameterColorHex:            action.lightColorHex,
-      ParameterTransitionTime:      action.transitionTime
-    }])
-
-  of LightSetColorXY:
-    CheckDeviceType(Lightbulb)
-
-    requestParams.add(DeviceLightbulb, %* [{
-      ParameterColorX:              action.lightColorX,
-      ParameterColorY:              action.lightColorY,
-      ParameterTransitionTime:      action.transitionTime
-    }])
-
-  of LightSetHueSaturation:
-    CheckDeviceType(Lightbulb)
-
-    requestParams.add(DeviceLightbulb, %* [{
-      ParameterHue:                 action.lightHue,
-      ParameterSaturation:          action.lightSaturation,
-      ParameterTransitionTime:      action.transitionTime
-    }])
-
-  of LightSetColorTemperature:
-    CheckDeviceType(Lightbulb)
-
-    requestParams.add(DeviceLightbulb, %* [{
-      ParameterColorTemperature:    action.lightColorTemperature,
-      ParameterTransitionTime:      action.transitionTime
-    }])
-
-  of PlugSetPowerState:
-    CheckDeviceType(Plug)
-
-    requestParams.add(DevicePlug, %* [{
-      ParameterPowerState:          action.plugPowerState,
-    }])
-
-  of PlugSetDimmerValue:
-    CheckDeviceType(Plug)
-
-    requestParams.add(DevicePlug, %* [{
-      ParameterDimmerValue:         action.plugDimmerValue,
-    }])
-
-
-  discard makeCoapRequest(device.gatewayRef.host, device.gatewayRef.port, "put", device.gatewayRef.user, device.gatewayRef.pass, EndpointDevices & $device.id, requestParams)
-
-
-proc getDevice* (gatewayRef: TradfriGatewayRef, deviceId: int): TradfriDevice = 
-  let request = makeCoapRequest(gatewayRef.host, gatewayRef.port, "get", gatewayRef.user, gatewayRef.pass, EndpointDevices & $deviceId, %* {})
-
-  result = parseDevice(request)
-  result.gatewayRef = gatewayRef
-
-  
-proc getDevices* (gatewayRef: TradfriGatewayRef): seq[TradfriDevice] = 
-  let request = makeCoapRequest(gatewayRef.host, gatewayRef.port, "get", gatewayRef.user, gatewayRef.pass, EndpointDevices, %* {})
-
-  result = newSeq[TradfriDevice]()
-
-  for id in request:
-    result.add(getDevice(gatewayRef, id.getInt))
diff --git a/deviceHelpers.nim b/deviceHelpers.nim
@@ -1,5 +1,12 @@
 import colors
-import deviceTypes, helpers, device
+import deviceTypes, helpers, devices
+
+proc renameDevice* (device: TradfriDevice, name: string): bool = 
+  return device.operateDevice(TradfriDeviceAction(
+      kind:            DeviceRename,
+      deviceName:      name
+    ))
+
 
 proc setPowerState* (device: TradfriDevice, state: bool): bool =
   if device.`type` == Lightbulb:
diff --git a/devices.nim b/devices.nim
@@ -0,0 +1,215 @@
+import json, strutils, options, algorithm
+
+import coapClient
+import gatewayTypes, deviceTypes
+import mappings, helpers
+
+proc parseDevice (data: JsonNode): TradfriDevice = 
+  let deviceType = TradfriDeviceType(data[ParameterType].getInt)
+  var state:       TradfriDeviceState
+
+  case deviceType:
+    of Remote:
+      state = TradfriDeviceState(
+          kind:                     deviceType,
+          remoteSupported:          false
+        )
+
+    of slaveRemote:
+      state = TradfriDeviceState(
+          kind:                     deviceType,
+          slaveRemoteSupported:     false
+        )
+
+    of Lightbulb:
+      state = TradfriDeviceState(
+          kind:                     deviceType,
+          lightPowered:             parseBool($data[DeviceLightbulb][0][ParameterPowerState].getInt),
+          lightBrightness:          data[DeviceLightbulb][0][ParameterDimmerValue].getInt
+        )
+
+      #get hue and saturation (only for RGB-bulbs)
+      if data[DeviceLightbulb][0].hasKey(ParameterHue):
+        state.lightHue        = some(data[DeviceLightbulb][0][ParameterHue].getInt)
+        state.lightSaturation = some(data[DeviceLightbulb][0][ParameterSaturation].getInt)
+      else:
+        state.lightHue        = none(int)
+        state.lightSaturation = none(int)      
+
+
+      #get color hex-value (for white-spectrum and RGB, but only some presets)
+      if data[DeviceLightbulb][0].hasKey(ParameterColorHex):
+        state.lightColorHex   = some(data[DeviceLightbulb][0][ParameterColorHex].getStr)
+      else:
+        state.lightColorHex   = none(string)
+
+      #get colorX and colorY values (can be used to set any color on RGB bulbs)
+      if data[DeviceLightbulb][0].hasKey(ParameterColorX):
+        state.lightColorX     = some(data[DeviceLightbulb][0][ParameterColorX].getFloat)
+        state.lightColorY     = some(data[DeviceLightbulb][0][ParameterColorY].getFloat)
+      else:
+        state.lightColorX     = none(float)
+        state.lightColorY     = none(float)
+
+
+      #get color-specturm value
+      if data[DeviceLightbulb][0].hasKey(ParameterColorTemperature):
+        state.lightColorTemperature = some(data[DeviceLightbulb][0][ParameterColorTemperature].getInt)
+      else:
+        state.lightColorTemperature = none(int)
+
+      #determine type of bulb
+      if state.lightHue.isSome:
+        state.lightSpectrum = RGB
+      elif state.lightColorTemperature.isSome:
+        state.lightSpectrum = White
+      else:
+        state.lightSpectrum = None
+      
+    of Plug:
+      state = TradfriDeviceState(
+          kind:                    deviceType,
+          plugPowered:             parseBool($data[DevicePlug][0][ParameterPowerState].getInt),
+          plugDimmer:              data[DevicePlug][0][ParameterDimmerValue].getInt
+        )
+
+    of motionSensor:
+      state = TradfriDeviceState(
+          kind:                    deviceType,
+          motionSensorSupported:   false
+        )
+
+    of signalRepeater:
+      state = TradfriDeviceState(
+          kind:                    deviceType,
+          signalRepeaterSupported: false
+        )
+
+    of Blind:
+      state = TradfriDeviceState(
+          kind:                    deviceType,
+          blindPosition:           data["3"][ParameterBlindPosition].getFloat,
+          blindTrigger:            data["3"][ParameterBlindTrigger].getFloat
+        )
+
+    of soundRemote:
+      state = TradfriDeviceState(
+          kind:                    deviceType,
+          soundRemoteSupported:    false
+        )
+
+  return TradfriDevice(
+      `type`:      deviceType,
+      id:          data[ParameterId].getInt,
+      name:        data[ParameterName].getStr,
+      alive:       intToBool(data[ParameterAlive].getInt),
+      createdAt:   data[ParameterCreatedAt].getInt,
+      lastSeen:    data[ParameterLastSeen].getInt,
+      state:       state,
+      info:        TradfriDeviceInfo(
+        manufacturer:    data["3"]["0"].getStr,
+        modelNumber:     data["3"]["1"].getStr,
+        serialNumber:    data["3"]["2"].getStr,
+        firmwareVersion: data["3"]["3"].getStr,
+        power:           TradfriPowerSource(data["3"]["6"].getInt),
+        battery:         data["3"]{"9"}.getInt
+      )
+    )
+
+
+proc operateDevice* (device: TradfriDevice, action: TradfriDeviceAction): bool = 
+  var requestParams = %* {}
+
+  template CheckDeviceType(typeId: TradfriDeviceType) = 
+    if device.`type` != TradfriDeviceType(typeId):
+      raise newException(ValueError, "Wrong action for this Devicetype")
+    
+  case action.kind:
+  of DeviceRename:
+    requestParams.add(ParameterName, %action.deviceName)
+
+  of LightSetPowerState:
+    CheckDeviceType(Lightbulb)
+    
+    requestParams.add(DeviceLightbulb, %* [{
+      ParameterPowerState:          boolToInt(action.lightPowerState),
+      ParameterTransitionTime:      action.transitionTime
+    }])
+
+  of LightSetBrightness:
+    CheckDeviceType(Lightbulb)
+
+    requestParams.add(DeviceLightbulb, %* [{
+      ParameterDimmerValue:         action.lightBrightness,
+      ParameterTransitionTime:      action.transitionTime
+    }])
+
+  of LightSetColorHex:
+    CheckDeviceType(Lightbulb)
+
+    requestParams.add(DeviceLightbulb, %* [{
+      ParameterColorHex:            action.lightColorHex,
+      ParameterTransitionTime:      action.transitionTime
+    }])
+
+  of LightSetColorXY:
+    CheckDeviceType(Lightbulb)
+
+    requestParams.add(DeviceLightbulb, %* [{
+      ParameterColorX:              action.lightColorX,
+      ParameterColorY:              action.lightColorY,
+      ParameterTransitionTime:      action.transitionTime
+    }])
+
+  of LightSetHueSaturation:
+    CheckDeviceType(Lightbulb)
+
+    requestParams.add(DeviceLightbulb, %* [{
+      ParameterHue:                 action.lightHue,
+      ParameterSaturation:          action.lightSaturation,
+      ParameterTransitionTime:      action.transitionTime
+    }])
+
+  of LightSetColorTemperature:
+    CheckDeviceType(Lightbulb)
+
+    requestParams.add(DeviceLightbulb, %* [{
+      ParameterColorTemperature:    action.lightColorTemperature,
+      ParameterTransitionTime:      action.transitionTime
+    }])
+
+  of PlugSetPowerState:
+    CheckDeviceType(Plug)
+
+    requestParams.add(DevicePlug, %* [{
+      ParameterPowerState:          boolToInt(action.plugPowerState),
+    }])
+
+  of PlugSetDimmerValue:
+    CheckDeviceType(Plug)
+
+    requestParams.add(DevicePlug, %* [{
+      ParameterDimmerValue:         action.plugDimmerValue,
+    }])
+
+
+  discard makeCoapRequest(device.gatewayRef.host, device.gatewayRef.port, "put", device.gatewayRef.user, device.gatewayRef.pass, EndpointDevices & $device.id, requestParams)
+
+
+proc getDevice* (gatewayRef: TradfriGatewayRef, deviceId: int): TradfriDevice = 
+  let request = makeCoapRequest(gatewayRef.host, gatewayRef.port, "get", gatewayRef.user, gatewayRef.pass, EndpointDevices & $deviceId, %* {})
+
+  result = parseDevice(request)
+  result.gatewayRef = gatewayRef
+
+  
+proc getDevices* (gatewayRef: TradfriGatewayRef): seq[TradfriDevice] = 
+  let request = makeCoapRequest(gatewayRef.host, gatewayRef.port, "get", gatewayRef.user, gatewayRef.pass, EndpointDevices, %* {})
+
+  result = newSeq[TradfriDevice]()
+
+  for id in request:
+    result.add(getDevice(gatewayRef, id.getInt))
+
+  result.sort do (x, y: TradfriDevice) -> int:
+    result = cmp(x.id, y.id)
diff --git a/groupTypes.nim b/groupTypes.nim
@@ -0,0 +1,24 @@
+import gatewayTypes, deviceTypes
+
+type
+  TradfriGroupActionType* = enum
+    GroupRename, GroupSetPowerState
+
+  TradfriGroup* = object
+    gatewayRef*:           TradfriGatewayRef
+    `type`*:               int
+    name*:                 string
+    createdAt*:            int 
+    id*:                   int
+    powerState*:           bool
+    dimmerValue*:          int
+    sceneId*:              int
+    devices*:              seq[TradfriDevice]
+
+  TradfriGroupAction* = object
+    case kind*: TradfriGroupActionType
+    of GroupRename:
+      groupName*:          string
+
+    of GroupSetPowerState:
+      groupPowerState*:    bool
diff --git a/groups.nim b/groups.nim
@@ -0,0 +1,55 @@
+import json, strutils, sequtils, options, algorithm
+
+import coapClient
+import gatewayTypes, groupTypes, deviceTypes
+import mappings, helpers, devices
+
+proc operateGroup* (group: TradfriGroup, action: TradfriGroupAction): bool = 
+  var requestParams = %* {}
+
+  case action.kind:
+  of GroupRename:
+    requestParams.add(ParameterName, %action.groupName)
+
+  of GroupSetPowerState:
+    requestParams.add(DeviceLightbulb, %* [{
+      ParameterPowerState:          boolToInt(action.groupPowerState),
+    }])
+
+
+  discard makeCoapRequest(group.gatewayRef.host, group.gatewayRef.port, "put", group.gatewayRef.user, group.gatewayRef.pass, EndpointGroups & $group.id, requestParams)
+
+
+proc getGroup* (gatewayRef: TradfriGatewayRef, groupId: int): TradfriGroup = 
+  let request = makeCoapRequest(gatewayRef.host, gatewayRef.port, "get", gatewayRef.user, gatewayRef.pass, EndpointGroups & $groupId, %* {})
+
+  var devices = newSeq[TradfriDevice]()
+  for deviceId in request[ParameterDeviceIds]["15002"]["9003"]:
+    devices.add(gatewayRef.getDevice(deviceId.getInt))
+
+  devices.sort do (x, y: TradfriDevice) -> int:
+    result = cmp(x.id, y.id)
+
+  result = TradfriGroup(
+    gatewayRef:   gatewayRef,
+    name:         request[ParameterName].getStr,
+    createdAt:    request[ParameterCreatedAt].getInt,
+    id:           request[ParameterId].getInt,
+    powerState:   intToBool(request[ParameterPowerState].getInt),
+    dimmerValue:  request[ParameterDimmerValue].getInt,
+    sceneId:      request[ParameterSceneId].getInt,
+    devices:      devices
+  )
+
+
+proc getGroups* (gatewayRef: TradfriGatewayRef): seq[TradfriGroup] = 
+  let request = makeCoapRequest(gatewayRef.host, gatewayRef.port, "get", gatewayRef.user, gatewayRef.pass, EndpointGroups, %* {})
+
+  result = newSeq[TradfriGroup]()
+
+  for id in request:
+    result.add(getGroup(gatewayRef, id.getInt))
+
+  result.sort do (x, y: TradfriGroup) -> int:
+    result = cmp(x.id, y.id)
+
diff --git a/listGroups.nim b/listGroups.nim
@@ -0,0 +1,13 @@
+import json, os, strutils
+import tradfri
+
+let tradfriGateway = newTradfriGateway(
+    host = "192.168.100.225",
+    port = 5684,
+    user = "ctucx",
+    pass = "JrSGx6WkAVJUl53b"
+  )
+
+let groups = %* tradfriGateway.getGroups()
+
+echo pretty groups
diff --git a/mappings.nim b/mappings.nim
@@ -17,12 +17,13 @@ const DeviceBlind*                                   = "15015"
 const ParameterName*                                 = "9001"
 const ParameterCreatedAt*                            = "9002"
 const ParameterId*                                   = "9003"
+const ParameterDeviceIds*                            = "9018"
 const ParameterAlive*                                = "9019"
 const ParameterLastSeen*                             = "9020"
-const ParameterType*                                 = "5750"
+const ParameterSceneId*                              = "9039"
 
-const ParameterPowerState*                           = "5850"
-const ParameterDimmerValue*                          = "5851"
+const ParameterBlindTrigger*                         = "5523"
+const ParameterBlindPosition*                        = "5536"
 
 const ParameterColorHex*                             = "5706"
 const ParameterHue*                                  = "5707"

@@ -31,9 +32,10 @@ const ParameterColorX*                               = "5709"
 const ParameterColorY*                               = "5710"
 const ParameterColorTemperature*                     = "5711"
 const ParameterTransitionTime*                       = "5712"
+const ParameterType*                                 = "5750"
 
-const ParameterBlindTrigger*                         = "5523"
-const ParameterBlindPosition*                        = "5536"
+const ParameterPowerState*                           = "5850"
+const ParameterDimmerValue*                          = "5851"
 
 const ParameterGatewayNtpServerUrl*                  = "9023"
 const ParameterGatewayVersion*                       = "9029"
diff --git a/tradfri.nim b/tradfri.nim
@@ -1,6 +1,6 @@
-import gatewayTypes, deviceTypes, mappings, helpers
 import gateway
-import device, deviceHelpers
+import devices, deviceHelpers
+import groups
 
 #gateway related stuff
 export newTradfriGateway

@@ -18,3 +18,7 @@ export setBrightness
 export setColorHex
 export setColorXY
 export setColorXYfromHex
+
+#group related stuff
+export getGroups
+export getGroup