commit 5dcb4e1245ceae7df282cadcb10c65116ca57af0
parent 8fe522fe9ecaa5aa1aea2841f1555043aba29be1
Author: Leah (ctucx) <leah@ctu.cx>
Date: Thu, 18 Feb 2021 17:07:56 +0100
parent 8fe522fe9ecaa5aa1aea2841f1555043aba29be1
Author: Leah (ctucx) <leah@ctu.cx>
Date: Thu, 18 Feb 2021 17:07:56 +0100
make things more failsafe
9 files changed, 132 insertions(+), 99 deletions(-)
diff --git a/src/devices/modbusPowermeter.nim b/src/devices/modbusPowermeter.nim @@ -44,8 +44,7 @@ proc updatePowermeters () {.async.} = try: await updatePowermeter(key, device) except: - let e = getCurrentException() - echo("error while updating powermeter ", key, ": ", e.msg) + echo "Error[updatePowermeters]:\n", getCurrentExceptionMsg() proc powermetersLoop () {.async.} = await sleepAsync(500)
diff --git a/src/devices/modbusRelayboard.nim b/src/devices/modbusRelayboard.nim @@ -23,8 +23,7 @@ proc updateRelayboards () {.async.} = let data = await mb.asyncReadBits(device.address.get, device.firstRegister, device.count) server.state[key] = DeviceState(type: RelayBoard, relays: data) except: - let e = getCurrentException() - echo("error while updating relayboard ", key, ": ", e.msg) + echo "Error[updateRelayboards]:\n", getCurrentExceptionMsg() broadcastServerState()
diff --git a/src/devices/zigbee2mqttLamp.nim b/src/devices/zigbee2mqttLamp.nim @@ -47,32 +47,35 @@ proc setZigbee2MqttLampColorTemperature* (deviceName: string, colorTemperature: await mqttContext.publish("zigbee2mqtt/" & config.deviceName.get & "/set", "{\"color_temp\": \"" & $colorTemperature & "\"}") proc updateLamp (topic: string, message: string) = - let deviceName = zigbee2mqttDevices[topic] - let recivedData = parseJson(message) - - server.state[deviceName].lastUpdated = some(toUnix(getTime())) + try: + let deviceName = zigbee2mqttDevices[topic] + let recivedData = parseJson(message) - if recivedData.hasKey("linkquality"): - server.state[deviceName].lampLinkquality = recivedData["linkquality"].getInt + server.state[deviceName].lastUpdated = some(toUnix(getTime())) - if recivedData.hasKey("state"): - if recivedData["state"].getStr != "ON": - server.state[deviceName].lampState = false - else: - server.state[deviceName].lampState = true + if recivedData.hasKey("linkquality"): + server.state[deviceName].lampLinkquality = recivedData["linkquality"].getInt - if recivedData.hasKey("brightness"): - server.state[deviceName].lampBrightness = recivedData["brightness"].getInt + if recivedData.hasKey("state"): + if recivedData["state"].getStr != "ON": + server.state[deviceName].lampState = false + else: + server.state[deviceName].lampState = true - if recivedData.hasKey("color"): - server.state[deviceName].lampColorX = recivedData["color"]["x"].getFloat - server.state[deviceName].lampColorY = recivedData["color"]["y"].getFloat + if recivedData.hasKey("brightness"): + server.state[deviceName].lampBrightness = recivedData["brightness"].getInt - if recivedData.hasKey("color_temp"): - server.state[deviceName].lampColorTemperature = recivedData["color_temp"].getInt + if recivedData.hasKey("color"): + server.state[deviceName].lampColorX = recivedData["color"]["x"].getFloat + server.state[deviceName].lampColorY = recivedData["color"]["y"].getFloat - broadcastServerState() + if recivedData.hasKey("color_temp"): + server.state[deviceName].lampColorTemperature = recivedData["color_temp"].getInt + broadcastServerState() + + except: + echo "Error[updateLamp]:\n", getCurrentExceptionMsg() proc initZigbee2MqttLamps* () {.async.} = for key, device in server.config.devices.pairs():
diff --git a/src/devices/zigbee2mqttRelay.nim b/src/devices/zigbee2mqttRelay.nim @@ -23,22 +23,24 @@ proc toggleZigbee2MqttRelayState* (deviceName: string) {.async.} = await mqttContext.publish("zigbee2mqtt/" & config.deviceName.get & "/set", "{\"state\": \"TOGGLE\"}") proc updateRelay (topic: string, message: string) = - let deviceName = zigbee2mqttDevices[topic] - let recivedData = parseJson(message) + try: + let deviceName = zigbee2mqttDevices[topic] + let recivedData = parseJson(message) - server.state[deviceName].lastUpdated = some(toUnix(getTime())) + server.state[deviceName].lastUpdated = some(toUnix(getTime())) - if recivedData.hasKey("linkquality"): - server.state[deviceName].relayLinkquality = recivedData["linkquality"].getInt + 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() + if recivedData.hasKey("state"): + if recivedData["state"].getStr != "ON": + server.state[deviceName].relayState = false + else: + server.state[deviceName].relayState = true + broadcastServerState() + except: + echo "Error[updateRelay]:\n", getCurrentExceptionMsg() proc initZigbee2MqttRelays* () {.async.} = for key, device in server.config.devices.pairs():
diff --git a/src/devices/zigbee2mqttRemote.nim b/src/devices/zigbee2mqttRemote.nim @@ -3,12 +3,16 @@ 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) + try: + 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]) + if config.actions.hasKey(recivedData["action"].getStr): + discard waitFor handleDeviceAction(config.actions[recivedData["action"].getStr]) + + except: + echo "Error[handleRemote]:\n", getCurrentExceptionMsg() proc initZigbee2MqttRemotes* () {.async.} = for key, device in server.config.devices.pairs():
diff --git a/src/frontend.nim b/src/frontend.nim @@ -1,4 +1,4 @@ -import asyncdispatch, asynchttpserver, json, tables, options +import asyncdispatch, asynchttpserver, json, tables, options, sequtils import ws import types, vars, utils, deviceActionHandler @@ -9,7 +9,8 @@ proc processWsClient(req: Request) {.async,gcsafe.} = try: ws = await newWebsocket(req) wsConnections.add(ws) - setupPings(ws, 2) +# setupPings(ws, 2) + echo "new ws connection: " & ws.key except: asyncCheck req.respond(Http404, "404") @@ -29,11 +30,11 @@ proc processWsClient(req: Request) {.async,gcsafe.} = if action.type == SetSubscriptionStateAction: if action.subscribed: - echo "adding client" + echo "adding client: " & ws.key subscribedConnections.add(ws) else: - echo "removing client (todo)" - + echo "removing client: " & ws.key + subscribedConnections.keepIf(proc(x: Websocket): bool = x.key != ws.key) else: response = await handleDeviceAction(action) @@ -44,7 +45,7 @@ proc processWsClient(req: Request) {.async,gcsafe.} = await ws.send($(%*Response(status: Err, data: newJString(e.msg)))) except WebSocketError: - echo "socket closed:", getCurrentExceptionMsg() + echo "Error[processWsClient]:\n", getCurrentExceptionMsg() proc processHttpClient(req: Request) {.async,gcsafe.} = if req.reqMethod == HttpGet: @@ -115,14 +116,10 @@ proc processRequest(req: Request) {.async,gcsafe.} = asyncCheck req.respond(Http404, "404") proc serveFrontend*() {.async.} = - var httpServer = newAsyncHttpServer() - await httpServer.serve(Port(server.config.serverConfig.frontendPort), processRequest) - #send empty paket every 2 seconds to every subscribed client - addTimer(2000, false, proc (fd: AsyncFD): bool {.gcsafe.} = - broadcast("") - ) - - #clean up broken websocket connections every 60 seconds - addTimer(60000, false, proc (fd: AsyncFD): bool {.gcsafe.} = - cleanupConnections() - )- \ No newline at end of file + try: + var httpServer = newAsyncHttpServer() + await httpServer.serve(Port(server.config.serverConfig.frontendPort), processRequest) + + except: + echo "Error[serveFrontend]:\n", getCurrentExceptionMsg() + quit()+ \ No newline at end of file
diff --git a/src/influx.nim b/src/influx.nim @@ -2,49 +2,55 @@ import asyncdispatch, strutils, tables, httpclient, options, json, base64, uri import types, vars proc existsDatabase* (config: InfluxConfig, databaseName: string): Future[bool] {.async.}= - var client = newAsyncHttpClient() + try: + var client = newAsyncHttpClient() + + if config.username.isSome and config.password.isSome: + client.headers["Authorization"] = "Basic " & base64.encode(config.username.get & ":" & config.password.get) - if config.username.isSome and config.password.isSome: - client.headers["Authorization"] = "Basic " & base64.encode(config.username.get & ":" & config.password.get) - - let baseUrl = "http://" & config.host & ":" & $config.port & "/" - let response = await client.getContent(baseUrl & "query?" & encodeQuery({"db": databaseName, "q": "select * FROM test LIMIT 1"})) - let data = parseJson(response) + let baseUrl = "http://" & config.host & ":" & $config.port & "/" + let response = await client.getContent(baseUrl & "query?" & encodeQuery({"db": databaseName, "q": "select * FROM test LIMIT 1"})) + let data = parseJson(response) - client.close() + client.close() - if data["results"][0].hasKey("error"): - return false + if data["results"][0].hasKey("error"): + return false - return true + return true + except: + echo "Error[existsDatabase]:\n", getCurrentExceptionMsg() proc insertDatabase* (config: InfluxConfig, databaseName: string, tableName: string, tags: Table[string, string], fields: Table[string, string], timestamp: int64): Future[bool] {.async.} = - var client = newAsyncHttpClient() + try: + var client = newAsyncHttpClient() + + if config.username.isSome and config.password.isSome: + client.headers["Authorization"] = "Basic " & base64.encode(config.username.get & ":" & config.password.get) - if config.username.isSome and config.password.isSome: - client.headers["Authorization"] = "Basic " & base64.encode(config.username.get & ":" & config.password.get) - - let baseUrl = "http://" & config.host & ":" & $config.port & "/" + let baseUrl = "http://" & config.host & ":" & $config.port & "/" - var tagsCombined: seq[string] - var fieldsCombined: seq[string] + var tagsCombined: seq[string] + var fieldsCombined: seq[string] - for key, value in pairs(tags): - tagsCombined.add(key & "=" & value) + for key, value in pairs(tags): + tagsCombined.add(key & "=" & value) - for key, value in pairs(fields): - fieldsCombined.add(key & "=" & value) + for key, value in pairs(fields): + fieldsCombined.add(key & "=" & value) - let body = tableName & "," & tagsCombined.join(",") & " " & fieldsCombined.join(",") & " " & $timestamp + let body = tableName & "," & tagsCombined.join(",") & " " & fieldsCombined.join(",") & " " & $timestamp - let response = await client.request(baseUrl & "write?db=" & databaseName, httpMethod = HttpPost, body = body) + let response = await client.request(baseUrl & "write?db=" & databaseName, httpMethod = HttpPost, body = body) - client.close() + client.close() - if response.code != Http204: - return false + if response.code != Http204: + return false - return true + return true + except: + echo "Error[insertDatabase]:\n", getCurrentExceptionMsg() proc initInflux* () = let config = server.config.serverConfig.influx.get @@ -61,5 +67,5 @@ proc initInflux* () = quit() except: - let e = getCurrentException() - echo e.msg + echo "Error[initInflux]:\n", getCurrentExceptionMsg() + quit()
diff --git a/src/mqtt.nim b/src/mqtt.nim @@ -4,9 +4,14 @@ import nmqtt proc initMqtt* () {.async.} = - var config = server.config.serverConfig.mqtt.get + try: + var config = server.config.serverConfig.mqtt.get - mqttContext = newMqttCtx("smartied") - mqttContext.set_host(config.host, config.port) + mqttContext = newMqttCtx("smartied") + mqttContext.set_host(config.host, config.port) - await mqttContext.start()- \ No newline at end of file + await mqttContext.start() + + except: + echo "Error[initMqtt]:\n", getCurrentExceptionMsg() + quit()+ \ No newline at end of file
diff --git a/src/utils.nim b/src/utils.nim @@ -2,10 +2,6 @@ import asyncdispatch, asyncnet, sequtils, json, tables, math, options import ws, nmqtt import types, vars, modbus -proc initUtil*() = - lastClientId = 1 - subscribedConnections = @[] - proc addVal* (resp: var string, name: string, key: string, val: string, lastUpdated: int64) = resp = resp & name & "{" resp = resp & "device=\"" & key & "\"" @@ -42,6 +38,15 @@ proc broadcast* (msg: string) = proc broadcastServerState* () = broadcast($(%*server.state)) +proc sendPing* () = + try: + for socket in wsConnections: + if socket.readyState == Open: + asyncCheck socket.send("") + + except WebSocketError: + echo "socket closed:", getCurrentExceptionMsg() + proc isaRound* [T: float32|float64](value: T, places: int = 0): float = if places == 0: result = round(value) @@ -72,4 +77,18 @@ proc closeOpenConnections* () = #close all websocket connections for wsConnection in wsConnections: - wsConnection.close()- \ No newline at end of file + wsConnection.close() + +proc initUtil*() = + lastClientId = 1 + subscribedConnections = @[] + + #send empty paket every 2 seconds to every subscribed client + addTimer(2000, false, proc (fd: AsyncFD): bool {.gcsafe.} = + sendPing() + ) + + #clean up broken websocket connections every 60 seconds + addTimer(60000, false, proc (fd: AsyncFD): bool {.gcsafe.} = + cleanupConnections() + )