commit 9e1f7dbb533ae1db3e63e7900be6d1ffe8ac83c6
Author: Leah (ctucx) <git@ctu.cx>
Date: Thu, 8 Dec 2022 18:13:43 +0100
Author: Leah (ctucx) <git@ctu.cx>
Date: Thu, 8 Dec 2022 18:13:43 +0100
initial commit
9 files changed, 248 insertions(+), 0 deletions(-)
A
|
95
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/config.json b/config.json @@ -0,0 +1,7 @@ +{ + "mqtt": { + "host": "10.0.0.1", + "port": 1883 + }, + "serialDevice": "/dev/ttyUSB0" +}
diff --git a/flake.lock b/flake.lock @@ -0,0 +1,43 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1670543317, + "narHash": "sha256-4mMR56rtxKr+Gwz399jFr4i76SQZxsLWxxyfQlPXRm0=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "7a6a010c3a1d00f8470a5ca888f2f927f1860a19", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-22.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +}
diff --git a/flake.nix b/flake.nix @@ -0,0 +1,54 @@ +{ + description = "Exporter for LaCrosse sensors to mqtt, written in nim"; + + inputs = { + flake-utils.url = "github:numtide/flake-utils"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.11"; + }; + + outputs = { self, nixpkgs, flake-utils }: { + + overlay = final: prev: { + + lacrosse2mqtt = ( + let + nmqtt = final.fetchFromGitHub { + owner = "zevv"; + repo = "nmqtt"; + rev = "v1.0.4"; + sha256 = "1by0xyqz754dny19lf8rpkg42passnj0rs6rk3jr763m1zr803mc"; + }; + + in final.nimPackages.buildNimPackage { + name = "lacrosse2mqtt"; + src = self; + + buildInputs = [ nmqtt ]; + + nimBinOnly = true; + nimRelease = true; + } + ); + + }; + + } // (flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + inherit system; + overlays = [ self.overlay ]; + }; + + in rec { + + packages.default = pkgs.lacrosse2mqtt; + packages.lacrosse2mqtt = pkgs.lacrosse2mqtt; + + apps.default = { + type = "app"; + program = "${pkgs.lacrosse2mqtt}/bin/lacrosse2mqtt"; + }; + + } + )); +}+ \ No newline at end of file
diff --git a/lacrosse2mqtt.nimble b/lacrosse2mqtt.nimble @@ -0,0 +1,15 @@ +# Package + +version = "0.1.0" +author = "Leah(ctucx)" +description = "Exports data from LaCrosse sensors to mqtt" +license = "AGPL-3.0" +srcDir = "./src" +bin = @["lacrosse2mqtt"] + + + +# Dependencies +requires "nim >= 0.20.0" +requires "nmqtt == 1.0.4" +requires "serial >= 1.0.0"+ \ No newline at end of file
diff --git a/package.nix b/package.nix @@ -0,0 +1,19 @@ +{ nimPackages, fetchFromGitHub }: + +let + + nmqtt = fetchFromGitHub { + owner = "zevv"; + repo = "nmqtt"; + rev = "v1.0.4"; + sha256 = "1by0xyqz754dny19lf8rpkg42passnj0rs6rk3jr763m1zr803mc"; + }; + +in nimPackages.buildNimPackage { + name = "lacrosse2mqtt"; + + nimBinOnly = true; + buildInputs = [ nmqtt ]; + + src = ./src; +}
diff --git a/src/lacrosse2mqtt.nim b/src/lacrosse2mqtt.nim @@ -0,0 +1,95 @@ +import std/asyncdispatch +import std/[os, posix, threadpool] +import std/[tables, json, options] + +import nmqtt +import types + +var mqttContext* {.threadvar.} : MqttCtx + +proc CtrlCHook* () {.noconv.} = + echo "Ctrl+C fired! \nStopping now!" + waitFor mqttContext.disconnect() + quit() + +proc asyncReadLine*(file: File): Future[string] = + var fut = newFuture[string]() + var flowVar = spawn file.readLine() + + addTimer(50, false, proc(fd: AsyncFD): bool = + if flowVar.isReady(): + fut.complete(^flowVar) + return true + ) + + return fut + +proc readSerial(serial: string) {.async.} = + let file = open(serial, fmReadWrite) + while true: + try: + let line = await file.asyncReadLine() + let data = parseJson(line) + + let json = %* { + "id": data["id"].getStr, + "temperature": data["temp"].getFloat, + "newBattery": data["newBatt"].getBool, + "weakBattery": data["weakBatt"].getBool + } + + if (data["hum"].getInt != 106): + json.add("humidity", newJInt(data["hum"].getInt)) + + if mqttContext.isConnected: + await mqttContext.publish("lacrosse2mqtt/" & json["id"].getStr, $json, 2, true) + await mqttContext.publish("lacrosse2mqtt/" & json["id"].getStr & "/temperature", $json["temperature"], 2, true) + + if (data["hum"].getInt != 106): + await mqttContext.publish("lacrosse2mqtt/" & json["id"].getStr & "/humidity", $json["humidity"], 2, true) + + await mqttContext.publish("lacrosse2mqtt/" & json["id"].getStr & "/newBattery", $json["newBattery"], 2, true) + await mqttContext.publish("lacrosse2mqtt/" & json["id"].getStr & "/weakBattery", $json["weakBattery"], 2, true) + + except JsonParsingError: + continue + except: + let + e = getCurrentException() + msg = getCurrentExceptionMsg() + + echo "Got exception ", repr(e), " with message ", msg + + +proc main () {.async.} = + setControlCHook(CtrlCHook) + + onSignal(SIGTERM): + echo "Got SIGTERM! \nStopping now!" + waitFor mqttContext.disconnect() + quit() + + var configFile = "./config.json" + + if getEnv("CONFIG_PATH") != "": + configFile = getEnv("CONFIG_PATH") + + if not fileExists(configFile): + echo "Config file not found" + quit() + + let config = parseFile(configFile).to(Config) + + mqttContext = newMqttCtx("lacrosse2mqtt") + mqttContext.set_host(config.mqtt.host, config.mqtt.port) + mqttContext.set_verbosity(1) + + if (config.mqtt.username.isSome and config.mqtt.password.isSome): + mqttContext.set_auth(config.mqtt.username.get, config.mqtt.password.get) + + await mqttContext.start() + + asyncCheck readSerial(config.serialDevice) + +asyncCheck main() +runForever()
diff --git a/src/nim.cfg b/src/nim.cfg @@ -0,0 +1 @@ +--threads:on -d:nimNoLentIterators
diff --git a/src/types.nim b/src/types.nim @@ -0,0 +1,11 @@ +import std/options + +type MqttConfig* = object + host*: string + port*: int + username*: Option[string] + password*: Option[string] + +type Config* = object + mqtt*: MqttConfig + serialDevice*: string