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()