ctucx.git: smartied

[nimlang] smarthome server

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 
import asyncdispatch, asyncnet, sequtils, json, tables, math, options
import ws, nmqtt
import types, vars, modbus

proc addVal* (resp: var string, name: string, key: string, val: string, lastUpdated: int64) =
  resp = resp & name & "{"
  resp = resp & "device=\"" & key & "\""
  resp = resp & "} "
  resp = resp & val
  if lastUpdated != 0:
    resp = resp & " "
    resp = resp & $(lastUpdated * 1000)
  resp = resp & "\n"

proc fmtBool* (b: bool): string =
  if b:
    return "1"
  else:
    return "0"

proc cleanupConnections*() =
#  echo "subscribedConnections: " & $subscribedConnections.len
  subscribedConnections.keepIf(proc(x: Websocket): bool = x.readyState != Closed)
  wsConnections.keepIf(proc(x: Websocket): bool = x.readyState != Closed)
#  echo "subscribedConnections(after clenup): " & $subscribedConnections.len

proc broadcast* (msg: string) =
  cleanupConnections()

  try:
    for socket in subscribedConnections:
      if socket.readyState == Open:
        asyncCheck socket.send(msg)

  except WebSocketError:
    echo "socket closed:", getCurrentExceptionMsg()

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)
  else:
    result = value * pow(10.0, T(places))
    result = floor(result)
    result = result / pow(10.0, T(places))

proc checkAccessToken* (token: Option[string]): bool =
  if not token.isNone:
    if server.config.serverConfig.accessToken != token.get:
      return false
  else:
    if server.config.serverConfig.accessToken != "":
      return false

  return true

proc closeOpenConnections* () =
  #close mqtt connection
  waitFor mqttContext.disconnect()

  #close modbus connection
  deinitModbus()

  #close lacrosse socket
  lacrosseSocket.close()

  #close all websocket connections 
  for wsConnection in wsConnections:
    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()
    )