commit 63b29fec04dc5ab42172f55d277c42168eee9932
parent 439b9ee1933af48fd2f8283676c1d7bab83549b6
Author: Leah (ctucx) <leah@ctu.cx>
Date: Thu, 18 Feb 2021 11:38:36 +0100
parent 439b9ee1933af48fd2f8283676c1d7bab83549b6
Author: Leah (ctucx) <leah@ctu.cx>
Date: Thu, 18 Feb 2021 11:38:36 +0100
implemented new devices: zigbee2mqttRemote, zigbee2mqttRelay
10 files changed, 252 insertions(+), 140 deletions(-)
D
|
77
-----------------------------------------------------------------------------
A
|
93
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/config.json b/config.json @@ -25,17 +25,28 @@ "tradfri-lamp1": { "type": "Zigbee2MqttLamp", "lampType": "RGB", - "friendlyName": "ikea_lamp_rgb" + "deviceName": "ikea_lamp_rgb" }, "tradfri-lamp2": { "type": "Zigbee2MqttLamp", "lampType": "WhiteSpectrum", - "friendlyName": "ikea_lamp_whitespectrum" + "deviceName": "ikea_lamp_whitespectrum" }, "tradfri-lamp3": { "type": "Zigbee2MqttLamp", "lampType": "Switchable", - "friendlyName": "ikea_lamp_switchable" + "deviceName": "ikea_lamp_switchable" + }, + "tradfri-relay1": { + "type": "Zigbee2MqttRelay", + "deviceName": "ikea_control_outlet" + }, + "tradfri-remote1": { + "type": "Zigbee2MqttRemote", + "deviceName": "ikea_remote", + "actions": { + "toggle": {"type": "SwitchStateAction", "deviceName": "modbus-10", "relay": 0, "toggle": true} + } }, "lacrosse-raum": { "type": "LacrosseTempSensor", @@ -68,9 +79,9 @@ { "name": "Küche", "device": "modbus-10", "relay": 1 }, { "name": "Bett", "device": "modbus-10", "relay": 3 }, { "name": "Bad", "device": "modbus-20", "relay": 0 }, - { "name": "Decke: RGB", "device": "tradfri-lamp1", "relay": 0}, - { "name": "Decke: Weiß-Spektrum", "device": "tradfri-lamp2", "relay": 0}, - { "name": "Decke: Schaltbar", "device": "tradfri-lamp3", "relay": 0} + { "name": "Decke: RGB", "device": "tradfri-lamp1", "relay": 0 }, + { "name": "Decke: Weiß-Spektrum", "device": "tradfri-lamp2", "relay": 0 }, + { "name": "Decke: Schaltbar", "device": "tradfri-lamp3", "relay": 0 } ] }, { @@ -80,7 +91,8 @@ "type": "switches", "switches": [ { "name": "Lüfter", "device": "modbus-20", "relay": 3 }, - { "name": "Lüfter (leise)", "device": "modbus-20", "relay": 2 } + { "name": "Lüfter (leise)", "device": "modbus-20", "relay": 2 }, + { "name": "Tradfri Steckdose", "device": "tradfri-relay1", "relay": 0 }, ] }, {
diff --git a/src/actionHandler.nim b/src/actionHandler.nim @@ -1,77 +0,0 @@ -import asyncdispatch, json, tables, options -import types, vars, utils -import devices/[modbusRelayboard, zigbee2mqttLamp] - -proc handleAction*(action: Action): Future[JsonNode] {.async.} = - case action.type - of SwitchStateAction: - let config = server.config.devices[action.deviceName] - - if action.state.isNone and action.toggle.isNone: - return JsonNode() - - if action.state.isSome and action.toggle.isSome: - return JsonNode() - - if action.state.isSome: - let state = action.state.get - - if config.type == RelayBoard: - if action.relay.isNone: - return JsonNode() - else: - await setRelay(action.deviceName, action.relay.get, state) - broadcastServerState() - return JsonNode() - - if config.type == Zigbee2MqttLamp: - await setLampState(action.deviceName, state) - return JsonNode() - - if action.toggle.isSome: - if config.type == RelayBoard: - if action.relay.isNone: - return JsonNode() - else: - await toggleRelay(action.deviceName, action.relay.get) - broadcastServerState() - return JsonNode() - - if config.type == Zigbee2MqttLamp: - await toggleLamp(action.deviceName) - return JsonNode() - - of SetBrightnessAction: - let config = server.config.devices[action.deviceName] - - if config.type != Zigbee2MqttLamp: - return JsonNode() - - await setLampBrightness(action.deviceName, action.brightness) - return JsonNode() - - of SetColorXYAction: - let config = server.config.devices[action.deviceName] - - if config.type != Zigbee2MqttLamp: - return JsonNode() - - await setLampColor(action.deviceName, action.colorX, action.colorY) - return JsonNode() - - of SetColorTemperatureAction: - let config = server.config.devices[action.deviceName] - - if config.type != Zigbee2MqttLamp: - return JsonNode() - - await setLampColorTemperature(action.deviceName, action.colorTemperature) - return JsonNode() - - of GetClientConfigAction: - let clientConfig: JsonNode = server.config.clientConfigs[action.configName] - return clientConfig - - else: - return JsonNode() -
diff --git a/src/deviceActionHandler.nim b/src/deviceActionHandler.nim @@ -0,0 +1,93 @@ +import asyncdispatch, json, tables, options +import types, vars, utils +import devices/[modbusRelayboard, zigbee2mqttLamp, zigbee2mqttRelay] + +proc handleDeviceAction*(action: Action): Future[JsonNode] {.async.} = + case action.type + of SwitchStateAction: + let config = server.config.devices[action.deviceName] + + if action.state.isNone and action.toggle.isNone: + return JsonNode() + + if action.state.isSome and action.toggle.isSome: + return JsonNode() + + if action.state.isSome: + let state = action.state.get + + case config.type + of RelayBoard: + if action.relay.isNone: + return JsonNode() + else: + await setModbusRelayState(action.deviceName, action.relay.get, state) + broadcastServerState() + return JsonNode() + + of Zigbee2MqttLamp: + await setZigbee2MqttLampState(action.deviceName, state) + return JsonNode() + + of Zigbee2MqttRelay: + await setZigbee2MqttRelayState(action.deviceName, state) + return JsonNode() + + else: + return JsonNode() + + if action.toggle.isSome: + case config.type + of RelayBoard: + if action.relay.isNone: + return JsonNode() + else: + await toggleModbusRelayState(action.deviceName, action.relay.get) + broadcastServerState() + return JsonNode() + + of Zigbee2MqttLamp: + await toggleZigbee2MqttLampState(action.deviceName) + return JsonNode() + + of Zigbee2MqttRelay: + await toggleZigbee2MqttRelayState(action.deviceName) + return JsonNode() + + else: + return JsonNode() + + of SetBrightnessAction: + let config = server.config.devices[action.deviceName] + + if config.type != Zigbee2MqttLamp: + return JsonNode() + + await setZigbee2MqttLampBrightness(action.deviceName, action.brightness) + return JsonNode() + + of SetColorXYAction: + let config = server.config.devices[action.deviceName] + + if config.type != Zigbee2MqttLamp: + return JsonNode() + + await setZigbee2MqttLampColor(action.deviceName, action.colorX, action.colorY) + return JsonNode() + + of SetColorTemperatureAction: + let config = server.config.devices[action.deviceName] + + if config.type != Zigbee2MqttLamp: + return JsonNode() + + await setZigbee2MqttLampColorTemperature(action.deviceName, action.colorTemperature) + return JsonNode() + + of GetClientConfigAction: + let clientConfig: JsonNode = server.config.clientConfigs[action.configName] + return clientConfig + + else: + return JsonNode() +
diff --git a/src/devices/modbusRelayboard.nim b/src/devices/modbusRelayboard.nim @@ -1,19 +1,19 @@ import asyncdispatch, tables, options import ../types, ../vars, ../utils, ../modbus -proc setRelay*(relayboard: string, relay: uint8, value: bool) {.async.} = +proc setModbusRelayState* (relayboard: string, relay: uint8, value: bool) {.async.} = let config = server.config.devices[relayboard] server.state[relayboard].relays[relay] = value await mb.asyncWriteBit(config.address.get, config.firstRegister + relay, server.state[relayboard].relays[relay]) -proc toggleRelay*(relayboard: string, relay: uint8) {.async.} = +proc toggleModbusRelayState* (relayboard: string, relay: uint8) {.async.} = let config = server.config.devices[relayboard] server.state[relayboard].relays[relay] = not server.state[relayboard].relays[relay] await mb.asyncWriteBit(config.address.get, config.firstRegister + relay, server.state[relayboard].relays[relay]) -proc updateRelayboards() {.async.} = +proc updateRelayboards () {.async.} = echo "Get relayboard state" for key, device in server.config.devices.pairs(): if device.type != RelayBoard: @@ -28,7 +28,7 @@ proc updateRelayboards() {.async.} = broadcastServerState() -proc initModbusRelayboards*() = +proc initModbusRelayboards* () = for key, device in server.config.devices.pairs(): if device.type != RelayBoard: continue
diff --git a/src/devices/zigbee2mqttLamp.nim b/src/devices/zigbee2mqttLamp.nim @@ -2,7 +2,7 @@ import asyncdispatch, strutils, json, tables, options, times import ../types, ../vars, ../utils import nmqtt -proc setLampState* (deviceName: string, value: bool) {.async.} = +proc setZigbee2MqttLampState* (deviceName: string, value: bool) {.async.} = let config = server.config.devices[deviceName] var sendState = "OFF" @@ -12,42 +12,41 @@ proc setLampState* (deviceName: string, value: bool) {.async.} = if value == true: sendState = "ON" - await mqttContext.publish("zigbee2mqtt/" & config.friendlyName & "/set", "{\"state\": \"" & sendState & "\"}") + await mqttContext.publish("zigbee2mqtt/" & config.deviceName.get & "/set", "{\"state\": \"" & sendState & "\"}") -proc toggleLamp* (deviceName: string) {.async.} = +proc toggleZigbee2MqttLampState* (deviceName: string) {.async.} = let config = server.config.devices[deviceName] if config.type != Zigbee2MqttLamp: return - await mqttContext.publish("zigbee2mqtt/" & config.friendlyName & "/set", "{\"state\": \"TOGGLE\"}") + await mqttContext.publish("zigbee2mqtt/" & config.deviceName.get & "/set", "{\"state\": \"TOGGLE\"}") -proc setLampBrightness* (deviceName: string, brightness: uint8) {.async.} = +proc setZigbee2MqttLampBrightness* (deviceName: string, brightness: uint8) {.async.} = let config = server.config.devices[deviceName] if config.type != Zigbee2MqttLamp: return - await mqttContext.publish("zigbee2mqtt/" & config.friendlyName & "/set", "{\"brightness\": \"" & $brightness & "\"}") + await mqttContext.publish("zigbee2mqtt/" & config.deviceName.get & "/set", "{\"brightness\": \"" & $brightness & "\"}") -proc setLampColor* (deviceName: string, colorX: float, colorY: float) {.async.} = +proc setZigbee2MqttLampColor* (deviceName: string, colorX: float, colorY: float) {.async.} = let config = server.config.devices[deviceName] if config.type != Zigbee2MqttLamp: return - await mqttContext.publish("zigbee2mqtt/" & config.friendlyName & "/set", "{\"color\": {\"X\": \"" & $colorX & "\", \"Y\": \"" & $colorY & "\"}}") + await mqttContext.publish("zigbee2mqtt/" & config.deviceName.get & "/set", "{\"color\": {\"X\": \"" & $colorX & "\", \"Y\": \"" & $colorY & "\"}}") -proc setLampColorTemperature* (deviceName: string, colorTemperature: int) {.async.} = +proc setZigbee2MqttLampColorTemperature* (deviceName: string, colorTemperature: int) {.async.} = let config = server.config.devices[deviceName] if config.type != Zigbee2MqttLamp: return - await mqttContext.publish("zigbee2mqtt/" & config.friendlyName & "/set", "{\"color_temp\": \"" & $colorTemperature & "\"}") + await mqttContext.publish("zigbee2mqtt/" & config.deviceName.get & "/set", "{\"color_temp\": \"" & $colorTemperature & "\"}") proc updateLamp (topic: string, message: string) = - echo message let deviceName = zigbee2mqttDevices[topic] let recivedData = parseJson(message) @@ -57,10 +56,10 @@ proc updateLamp (topic: string, message: string) = server.state[deviceName].lampLinkquality = recivedData["linkquality"].getInt if recivedData.hasKey("state"): - if recivedData["state"].getStr == "ON": - server.state[deviceName].lampState = true + if recivedData["state"].getStr != "ON": + server.state[deviceName].lampState = false else: - server.state[deviceName].lampState = false + server.state[deviceName].lampState = true if recivedData.hasKey("brightness"): server.state[deviceName].lampBrightness = recivedData["brightness"].getInt @@ -79,7 +78,7 @@ proc initZigbee2MqttLamps* () {.async.} = for key, device in server.config.devices.pairs(): if device.type != Zigbee2MqttLamp: continue - zigbee2mqttDevices["zigbee2mqtt/" & device.friendlyName] = key + zigbee2mqttDevices["zigbee2mqtt/" & device.deviceName.get] = key server.state[key] = DeviceState(type: Zigbee2MqttLamp, lampType: device.lampType) - await mqttContext.subscribe("zigbee2mqtt/" & device.friendlyName, 2, updateLamp)- \ No newline at end of file + await mqttContext.subscribe("zigbee2mqtt/" & device.deviceName.get, 2, updateLamp)+ \ No newline at end of file
diff --git a/src/devices/zigbee2mqttRelay.nim b/src/devices/zigbee2mqttRelay.nim @@ -0,0 +1,50 @@ +import asyncdispatch, strutils, json, tables, options, times +import ../types, ../vars, ../utils +import nmqtt + +proc setZigbee2MqttRelayState* (deviceName: string, value: bool) {.async.} = + let config = server.config.devices[deviceName] + var sendState = "OFF" + + if config.type != Zigbee2MqttRelay: + return + + if value == true: + sendState = "ON" + + await mqttContext.publish("zigbee2mqtt/" & config.deviceName.get & "/set", "{\"state\": \"" & sendState & "\"}") + +proc toggleZigbee2MqttRelayState* (deviceName: string) {.async.} = + let config = server.config.devices[deviceName] + + if config.type != Zigbee2MqttRelay: + return + + await mqttContext.publish("zigbee2mqtt/" & config.deviceName.get & "/set", "{\"state\": \"TOGGLE\"}") + +proc updateRelay (topic: string, message: string) = + let deviceName = zigbee2mqttDevices[topic] + let recivedData = parseJson(message) + + server.state[deviceName].lastUpdated = some(toUnix(getTime())) + + if recivedData.hasKey("linkquality"): + server.state[deviceName].relayLinkquality = recivedData["linkquality"].getInt + + if recivedData.hasKey("state"): + if recivedData["state"].getStr != "ON": + server.state[deviceName].relayState = false + else: + server.state[deviceName].relayState = true + + broadcastServerState() + + +proc initZigbee2MqttRelays* () {.async.} = + for key, device in server.config.devices.pairs(): + if device.type != Zigbee2MqttRelay: continue + + zigbee2mqttDevices["zigbee2mqtt/" & device.deviceName.get] = key + server.state[key] = DeviceState(type: Zigbee2MqttRelay) + + await mqttContext.subscribe("zigbee2mqtt/" & device.deviceName.get, 2, updateRelay)+ \ No newline at end of file
diff --git a/src/devices/zigbee2mqttRemote.nim b/src/devices/zigbee2mqttRemote.nim @@ -0,0 +1,19 @@ +import asyncdispatch, strutils, json, tables, options +import ../types, ../vars, ../deviceActionHandler +import nmqtt + +proc handleRemote (topic: string, message: string) = + let deviceName = zigbee2mqttDevices[topic] + let config = server.config.devices[deviceName] + let recivedData = parseJson(message) + + if config.actions.hasKey(recivedData["action"].getStr): + discard waitFor handleDeviceAction(config.actions[recivedData["action"].getStr]) + +proc initZigbee2MqttRemotes* () {.async.} = + for key, device in server.config.devices.pairs(): + if device.type != Zigbee2MqttRemote: continue + + zigbee2mqttDevices["zigbee2mqtt/" & device.deviceName.get] = key + + await mqttContext.subscribe("zigbee2mqtt/" & device.deviceName.get, 2, handleRemote)+ \ No newline at end of file
diff --git a/src/frontend.nim b/src/frontend.nim @@ -1,6 +1,6 @@ import asyncdispatch, asynchttpserver, json, tables, options import ws -import types, vars, utils, actionHandler +import types, vars, utils, deviceActionHandler proc processWsClient(req: Request) {.async,gcsafe.} = var ws: WebSocket @@ -35,7 +35,7 @@ proc processWsClient(req: Request) {.async,gcsafe.} = echo "removing client (todo)" else: - response = await handleAction(action) + response = await handleDeviceAction(action) await ws.send($(%*Response(status: Ok, data: response))) @@ -60,7 +60,7 @@ proc processHttpClient(req: Request) {.async,gcsafe.} = if not checkAccessToken(action.accessToken): raise newException(OsError, "invalid accessToken") - await req.respond(Http200, $(%*Response(status: Ok, data: await handleAction(action)))) + await req.respond(Http200, $(%*Response(status: Ok, data: await handleDeviceAction(action)))) except: let e = getCurrentException()
diff --git a/src/smartied.nim b/src/smartied.nim @@ -1,7 +1,7 @@ import asyncdispatch, json, tables, os import types, modbus, mqtt, influx, vars, utils, options import frontend -import devices/[modbusPowermeter, modbusRelayboard, lacrosseSensors, zigbee2mqttLamp] +import devices/[modbusPowermeter, modbusRelayboard, lacrosseSensors, zigbee2mqttLamp, zigbee2mqttRelay, zigbee2mqttRemote] proc main() {.async.} = var configFile = "./config.json" @@ -34,6 +34,8 @@ proc main() {.async.} = if server.config.serverConfig.mqtt.isSome(): asyncCheck initMqtt() asyncCheck initZigbee2MqttLamps() + asyncCheck initZigbee2MqttRelays() + asyncCheck initZigbee2MqttRemotes() asyncCheck serveFrontend()
diff --git a/src/types.nim b/src/types.nim @@ -1,10 +1,40 @@ import json, tables, options +type ActionType* = enum + SwitchStateAction, + SetBrightnessAction, + SetColorXYAction, + SetColorTemperatureAction, + GetClientConfigAction, + SetSubscriptionStateAction + +type Action* = object + accessToken*: Option[string] + deviceName*: string + case type*: ActionType + of SwitchStateAction: + relay*: Option[uint8] + state*: Option[bool] + toggle*: Option[bool] + of SetBrightnessAction: + brightness*: uint8 + of SetColorXYAction: + colorX*: float + colorY*: float + of SetColorTemperatureAction: + colorTemperature*: int + of GetClientConfigAction: + configName*: string + of SetSubscriptionStateAction: + subscribed*: bool + type DeviceType* = enum PowerMeter, RelayBoard, LacrosseTempSensor, - Zigbee2MqttLamp + Zigbee2MqttLamp, + Zigbee2MqttRelay, + Zigbee2MqttRemote type Zigbee2MqttLampType* = enum RGB, @@ -34,38 +64,15 @@ type DeviceState* = object lampColorY*: float lampColorTemperature*: int lampLinkquality*: int - - -type ActionType* = enum - SwitchStateAction, - SetBrightnessAction, - SetColorXYAction, - SetColorTemperatureAction, - GetClientConfigAction, - SetSubscriptionStateAction - -type Action* = object - accessToken*: Option[string] - deviceName*: string - case type*: ActionType - of SwitchStateAction: - relay*: Option[uint8] - state*: Option[bool] - toggle*: Option[bool] - of SetBrightnessAction: - brightness*: uint8 - of SetColorXYAction: - colorX*: float - colorY*: float - of SetColorTemperatureAction: - colorTemperature*: int - of GetClientConfigAction: - configName*: string - of SetSubscriptionStateAction: - subscribed*: bool + of Zigbee2MqttRelay: + relayState*: bool + relayLinkquality*: int + else: + unusedValue: Option[bool] type DeviceConfig* = object address*: Option[uint8] + deviceName*: Option[string] case type*: DeviceType of PowerMeter: model*: string @@ -75,8 +82,13 @@ type DeviceConfig* = object of LacrosseTempSensor: id*: string of Zigbee2MqttLamp: - friendlyName*: string lampType*: Zigbee2MqttLampType + of Zigbee2MqttRemote: + actions*: Table[string, Action] + else: + unusedValue: Option[bool] + + type ModbusConfig* = object host*: string