ctucx.git: smartied

[nimlang] smarthome server

commit e6e7f78bf6b3d7a968f9a179f8d32565f4fdc30b
parent daaedb68ee030901e1d51e8f20062fa4829b005a
Author: Milan Pässler <me@pbb.lc>
Date: Sun, 21 Jul 2019 11:39:33 +0200

lacrosse
7 files changed, 101 insertions(+), 19 deletions(-)
M
config.json
|
16
++++++++++++++--
A
src/backend_lacrosse.nim
|
51
+++++++++++++++++++++++++++++++++++++++++++++++++++
M
src/backend_powermeter.nim
|
24
++++++++----------------
M
src/backend_relayboard.nim
|
4
++++
M
src/frontend_ws.nim
|
5
+++++
M
src/smartied.nim
|
2
++
M
src/types.nim
|
18
+++++++++++++++++-
diff --git a/config.json b/config.json
@@ -15,6 +15,16 @@
 			"type": "PowerMeter",
 			"model": "SDM120",
 			"address": 60
+		},
+		"lacrosse-21": {
+			"type": "LacrosseTempSensor",
+			"id": "21",
+			"address": 21
+		},
+		"lacrosse-31": {
+			"type": "LacrosseTempSensor",
+			"id": "31",
+			"address": 31
 		}
 	},
 	"clientConfigs": {

@@ -86,7 +96,9 @@
 	"httpPort": 5000,
 	"tcpPort": 5001,
 	"wsPort": 5002,
-	"modbusAddr": "192.168.1.1",
-	"modbusPort": 502,
+	"modbusAddr": "127.0.0.1",
+	"modbusPort": 5502,
+	"lacrosseAddr": "127.0.0.1",
+	"lacrossePort": 2342,
 	"powermeterUpdateIntervalSec": 20
 }
diff --git a/src/backend_lacrosse.nim b/src/backend_lacrosse.nim
@@ -0,0 +1,51 @@
+import asyncdispatch
+import asyncnet
+import types
+import tables
+import util
+import json
+import vars
+import times
+
+proc lacrosseHandleLoop(sock: AsyncSocket) {.async.} =
+  while true:
+    try:
+      let line = await sock.recvLine()
+      if line == "":
+        break
+
+      let msg = parseJson(line).to(LacrosseMessage)
+
+      for key, device in server.config.devices.pairs():
+        if device.type != LacrosseTempSensor:
+          continue
+        if device.id != msg.id:
+          continue
+
+        server.state[key].humidity = msg.hum
+        server.state[key].temperature = msg.temp
+        server.state[key].weakBattery = bool(msg.weakBatt)
+        server.state[key].lastUpdated2 = toUnix(getTime())
+
+        broadcast($(%*server.state))
+    except:
+      let e = getCurrentException()
+      echo("error while updating lacrosse: ", e.msg)
+
+proc lacrosseConnectLoop() {.async.} =
+  while true:
+    try:
+      let sock = await asyncnet.dial(server.config.lacrosseAddr, Port(server.config.lacrossePort))
+      await lacrosseHandleLoop(sock)
+    except:
+      let e = getCurrentException()
+      echo("error while connectiong to lacrosse relay: ", e.msg)
+    await sleepAsync(1000)
+
+proc initBackendLacrosse*() =
+  for key, device in server.config.devices.pairs():
+    if device.type != LacrosseTempSensor:
+      continue
+    server.state[key] = DeviceState(type: LacrosseTempSensor)
+
+  asyncCheck lacrosseConnectLoop()
diff --git a/src/backend_powermeter.nim b/src/backend_powermeter.nim
@@ -25,7 +25,6 @@ proc updatePowermeter(key: string, device: DeviceConfig) {.async.} =
   broadcast($(%*server.state))
 
 proc updatePowermeters() {.async.} =
-  GC_fullCollect()
   echo "updating powermeters"
   for key, device in server.config.devices.pairs():
     if device.type != PowerMeter:

@@ -37,24 +36,17 @@ proc updatePowermeters() {.async.} =
       let e = getCurrentException()
       echo("error while updating powermeter ", key, ": ", e.msg)
 
-proc timerFunc(fd: AsyncFD): bool {.gcsafe.} =
-  let fut = updatePowermeters()
-  fut.addCallback(proc () =
-      addTimer(int(server.config.powermeterUpdateIntervalSec * 1000), true, timerFunc)
-    )
-  return false
+proc powermetersLoop() {.async.} =
+  await sleepAsync(500)
+  while true:
+    await updatePowermeters()
+    await sleepAsync(int(server.config.powermeterUpdateIntervalSec * 1000))
 
 proc initBackendPowermeter*() =
-  addTimer(1000, true, timerFunc)
   for key, device in server.config.devices.pairs():
     if device.type != PowerMeter:
       continue
 
-    server.state[key] = DeviceState(
-        type: PowerMeter,
-        power: 0f,
-        cosphi: 0f,
-        voltage: 0f,
-        `import`: 0f,
-        frequency: 0f
-      )
+    server.state[key] = DeviceState(type: PowerMeter)
+
+  asyncCheck powermetersLoop()
diff --git a/src/backend_relayboard.nim b/src/backend_relayboard.nim
@@ -18,4 +18,8 @@ proc updateRelayboards() {.async.} =
       echo("error while updating relayboard ", key, ": ", e.msg)
 
 proc initBackendRelayboard*() =
+  for key, device in server.config.devices.pairs():
+    if device.type != RelayBoard:
+      continue
+    server.state[key] = DeviceState(type: RelayBoard)
   asyncCheck updateRelayboards()
diff --git a/src/frontend_ws.nim b/src/frontend_ws.nim
@@ -37,5 +37,10 @@ proc processWsClient(req: Request) {.async.} =
 proc serveWs*() {.async.} =
   registerBroadcastHandler(broadcast)
   wsClients = @[]
+
+  addTimer(1000, false, proc (fd: AsyncFD): bool {.gcsafe.} =
+      broadcast("")
+    )
+
   var httpServer = newAsyncHttpServer()
   await httpServer.serve(Port(server.config.wsPort), processWsClient)
diff --git a/src/smartied.nim b/src/smartied.nim
@@ -4,6 +4,7 @@ import frontend_ws
 import frontend_http
 import backend_powermeter
 import backend_relayboard
+import backend_lacrosse
 import types
 import modbus
 import json

@@ -18,6 +19,7 @@ initUtil()
 initModbus()
 initBackendPowermeter()
 initBackendRelayboard()
+initBackendLacrosse()
 
 asyncCheck serveTcp()
 asyncCheck serveWs()
diff --git a/src/types.nim b/src/types.nim
@@ -5,7 +5,8 @@ import sequtils
 
 type DeviceType* = enum
   PowerMeter,
-  RelayBoard
+  RelayBoard,
+  LacrosseTempSensor
 
 type DeviceConfig* = object
   address*: uint8

@@ -15,6 +16,8 @@ type DeviceConfig* = object
   of RelayBoard:
     firstRegister*: uint8
     count*: uint8
+  of LacrosseTempSensor:
+    id*: string
 
 type Config* = object
   tcpPort*: uint16

@@ -22,6 +25,8 @@ type Config* = object
   httpPort*: uint16
   modbusAddr*: string
   modbusPort*: uint16
+  lacrosseAddr*: string
+  lacrossePort*: uint16
   powermeterUpdateIntervalSec*: uint
   devices*: Table[string, DeviceConfig]
   clientConfigs*: Table[string, JsonNode]

@@ -37,6 +42,11 @@ type DeviceState* = object
     lastUpdated*: int64
   of RelayBoard:
     relays*: seq[bool]
+  of LacrosseTempSensor:
+    humidity*: float32
+    temperature*: float32
+    weakBattery*: bool
+    lastUpdated2*: int64
 
 type Server* = object
   state*: Table[string, DeviceState]

@@ -68,5 +78,11 @@ type Response* = object
   status*: ResponseStatus
   data*: JsonNode
 
+type LacrosseMessage* = object
+  id*: string
+  temp*: float32
+  hum*: float32
+  weakBatt*: int
+
 type modbus* = pointer