ctucx.git: lacrosse2mqtt

Publish data from LaCrosse sensors via MQTT

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
72 
73 
74 
75 
76 
77 
78 
79 
80 
81 
82 
83 
84 
85 
86 
87 
88 
89 
90 
91 
92 
93 
94 
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()