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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126 import asyncdispatch, asynchttpserver, json, tables, options, sequtils
import ws
import types, vars, utils, deviceActionHandler
proc processWsClient(req: Request) {.async,gcsafe.} =
var ws: WebSocket
cleanupConnections()
try:
ws = await newWebsocket(req)
wsConnections.add(ws)
# setupPings(ws, 2)
echo "new ws connection: " & ws.key
except:
asyncCheck req.respond(Http404, "404")
return
try:
while ws.readyState == Open:
let req = await ws.receiveStrPacket()
if req != "":
try:
let action = parseJson(req).to(Action)
var response = JsonNode()
if not checkAccessToken(action.accessToken):
raise newException(OsError, "invalid accessToken")
if action.type == SetSubscriptionStateAction:
if action.subscribed:
echo "adding client: " & ws.key
subscribedConnections.add(ws)
await ws.send($(%*server.state))
else:
echo "removing client: " & ws.key
subscribedConnections.keepIf(proc(x: Websocket): bool = x.key != ws.key)
else:
response = await handleDeviceAction(action)
await ws.send($(%*Response(status: Ok, data: response)))
except:
let e = getCurrentException()
await ws.send($(%*Response(status: Err, data: newJString(e.msg))))
except WebSocketError:
echo "Error[processWsClient]:\n", getCurrentExceptionMsg()
proc processHttpClient(req: Request) {.async,gcsafe.} =
if req.reqMethod == HttpGet:
if req.headers.hasKey("Authorization") and req.headers["Authorization"] == "Bearer " & server.config.serverConfig.accessToken:
await req.respond(Http200, $(%* server.state))
else:
await req.respond(Http401, "401 Unauthorized")
elif req.reqMethod == HttpPost:
try:
let action = parseJson(req.body).to(Action)
if not checkAccessToken(action.accessToken):
raise newException(OsError, "invalid accessToken")
await req.respond(Http200, $(%*Response(status: Ok, data: await handleDeviceAction(action))))
except:
let e = getCurrentException()
await req.respond(Http500, $(%*Response(status: Err, data: newJString(e.msg))))
else:
await req.respond(Http405, "405 Method Not Allowed")
proc processPrometheusClient(req: Request) {.async,gcsafe.} =
if req.reqMethod == HttpGet:
if req.headers.hasKey("Authorization") and req.headers["Authorization"] == "Bearer " & server.config.serverConfig.accessToken:
var resp = ""
for key, device in server.config.devices.pairs():
if device.type == PowerMeter:
if server.state[key].frequency == 0:
continue
let lastUpdated = server.state[key].lastUpdated.get
resp.addVal("powermeter_import", key, $(server.state[key].import), lastUpdated)
resp.addVal("powermeter_cosphi", key, $(server.state[key].cosphi), lastUpdated)
resp.addVal("powermeter_power", key, $(server.state[key].power), lastUpdated)
resp.addVal("powermeter_frequency", key, $(server.state[key].frequency), lastUpdated)
resp.addVal("powermeter_voltage", key, $(server.state[key].voltage), lastUpdated)
if device.type == RelayBoard:
for i, val in server.state[key].relays:
resp.addVal("relayboard_relay", key & "\",relay=\"" & $(i) & "\",name=\"" & $(i), fmtBool(val), 0)
if device.type == LacrosseTempSensor:
let lastUpdated = server.state[key].lastUpdated.get
resp.addVal("lacrossetempsensor_temperature", key, $(server.state[key].temperature), lastUpdated)
resp.addVal("lacrossetempsensor_weakbattery", key, fmtBool(server.state[key].weakBattery), lastUpdated)
if server.state[key].humidity < 0:
continue
resp.addVal("lacrossetempsensor_humidity", key, $(server.state[key].humidity), lastUpdated)
await req.respond(Http200, resp)
else:
await req.respond(Http401, "401 Unauthorized")
else:
await req.respond(Http405, "405 Method Not Allowed")
proc processRequest(req: Request) {.async,gcsafe.} =
case req.url.path
of "/":
await processHttpClient(req)
of "/ws":
await processWsClient(req)
of "/metrics":
await processPrometheusClient(req)
else:
asyncCheck req.respond(Http404, "404")
proc serveFrontend*() {.async.} =
try:
var httpServer = newAsyncHttpServer()
await httpServer.serve(Port(server.config.serverConfig.frontendPort), processRequest)
except:
echo "Error[serveFrontend]:\n", getCurrentExceptionMsg()
quit()