From e256d16f8d01c3cb8c73505ef5a425e5f53e7911 Mon Sep 17 00:00:00 2001 From: RaffaelW Date: Thu, 17 Apr 2025 22:35:26 +0200 Subject: [PATCH] add WebSocket support for real-time status updates --- data/index.html | 1 + data/status.js | 24 +++----------------- data/websocket.js | 37 ++++++++++++++++++++++++++++++ src/main.cpp | 9 ++++---- src/routes/status.cpp | 6 ++--- src/routes/status.h | 2 +- src/websocket.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++ src/websocket.h | 11 +++++++++ 8 files changed, 112 insertions(+), 30 deletions(-) create mode 100644 data/websocket.js create mode 100644 src/websocket.cpp create mode 100644 src/websocket.h diff --git a/data/index.html b/data/index.html index c120efb..9859fb4 100644 --- a/data/index.html +++ b/data/index.html @@ -13,6 +13,7 @@ +
diff --git a/data/status.js b/data/status.js index de00373..ca42350 100644 --- a/data/status.js +++ b/data/status.js @@ -1,4 +1,5 @@ import { data } from "./load-data.js"; +import { initWebSocket, registerCallback } from "./websocket.js"; const statusDialog = document.querySelector(".dialog-status"); const expandButton = document.querySelector(".expand-status"); @@ -7,24 +8,8 @@ expandButton.addEventListener("click", () => { statusDialog.showModal(); }); -async function loadStatus() { - try { - const res = await fetch("/status"); - if (!res.ok) { - throw new Error( - `Response status: ${res.status}\n${await res.text()}` - ); - } - - const data = await res.json(); - console.log(data); - - return data; - } catch (e) { - console.error(e); - return null; - } -} +registerCallback("status", setStatus); +initWebSocket(); function setStatus(status) { setValue("model", status.chip.model); @@ -70,7 +55,6 @@ function setValue(className, value) { function parseDuration(ms) { const date = new Date(ms); - console.log(date); const time = date.getUTCHours().toString().padStart(2, "0") + ":" + @@ -126,5 +110,3 @@ function selectConnectionIcon(signalStrength) { } return "signal1.svg"; } - -setStatus(await loadStatus()); diff --git a/data/websocket.js b/data/websocket.js new file mode 100644 index 0000000..268d69e --- /dev/null +++ b/data/websocket.js @@ -0,0 +1,37 @@ +const gateway = `ws://${window.location.hostname}/ws`; +let ws; + +let callbacks = {}; + +export function initWebSocket() { + if (ws) return; + + ws = new WebSocket(gateway); + + ws.onopen = () => { + console.info("WebSocket connection opened"); + }; + + ws.onclose = (event) => { + console.info("WebSocket connection closed, reason:", event.reason); + ws = null; + }; + + ws.onerror = (event) => { + console.warn("WebSocket encountered error, closing socket.", event); + ws.close(); + ws = null; + }; + + ws.onmessage = (event) => { + const message = JSON.parse(event.data); + console.log("received websocket data", message); + if (message.type in callbacks) { + callbacks[message.type](message.data); + } + }; +} + +export function registerCallback(type, callback) { + callbacks[type] = callback; +} diff --git a/src/main.cpp b/src/main.cpp index 5ec2d45..08cd9e6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,6 +21,7 @@ #include #include +#include "websocket.h" #include "routes/config.h" #include "routes/networks.h" #include "routes/status.h" @@ -418,9 +419,6 @@ void setup() server.on("/networks", HTTP_GET, [](AsyncWebServerRequest *request) { onGetNetworks(request); }); - server.on("/status", HTTP_GET, [](AsyncWebServerRequest *request) - { onGetStatus(request); }); - server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) { if (request->url() == "/config" && request->method() == HTTP_PUT) { @@ -429,7 +427,8 @@ void setup() ESP.restart(); } }); - delay(1000); + initWebSocket(&server); + server.begin(); Serial.println("Server started!"); @@ -501,4 +500,6 @@ void loop() { transmitDmxToArtnet(dmx2, dmx2_data, universe2); } + + webSocketLoop(); } diff --git a/src/routes/status.cpp b/src/routes/status.cpp index e25d21a..44af29b 100644 --- a/src/routes/status.cpp +++ b/src/routes/status.cpp @@ -19,7 +19,7 @@ int8_t getWiFiStrength() } } -void onGetStatus(AsyncWebServerRequest *request) +JsonDocument buildStatusJson() { JsonDocument doc; @@ -39,7 +39,5 @@ void onGetStatus(AsyncWebServerRequest *request) doc["psram"]["total"] = ESP.getPsramSize(); doc["connection"]["signalStrength"] = getWiFiStrength(); - String jsonString; - serializeJson(doc, jsonString); - request->send(200, "application/json", jsonString); + return doc; } \ No newline at end of file diff --git a/src/routes/status.h b/src/routes/status.h index 03e821b..9df7ac2 100644 --- a/src/routes/status.h +++ b/src/routes/status.h @@ -2,4 +2,4 @@ #include #include -void onGetStatus(AsyncWebServerRequest *request); \ No newline at end of file +JsonDocument buildStatusJson(); diff --git a/src/websocket.cpp b/src/websocket.cpp new file mode 100644 index 0000000..0bb8d1a --- /dev/null +++ b/src/websocket.cpp @@ -0,0 +1,52 @@ +#include "websocket.h" + +AsyncWebSocket ws("/ws"); + +long webSocketLastUpdate = 0; +const int WS_UPDATE_INTERVAL = 10 * 1000; // 10 seconds + +String buildStatusString() +{ + JsonDocument doc; + doc["type"] = "status"; + doc["data"] = buildStatusJson(); + + String jsonString = ""; + serializeJson(doc, jsonString); + return jsonString; +} + +void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) +{ + switch (type) + { + case WS_EVT_CONNECT: + Serial.printf("[WS] Client %u connected from %s\n", client->id(), client->remoteIP().toString().c_str()); + // directly send status to client + ws.text(client->id(), buildStatusString()); + break; + case WS_EVT_DISCONNECT: + Serial.printf("[WS] Client %u disconnected\n", client->id()); + break; + case WS_EVT_DATA: + Serial.printf("[WS] Data received from client %u: %s\n", client->id(), (char *)data); + break; + default: + break; + } +} + +void webSocketLoop() +{ + if (millis() - webSocketLastUpdate > WS_UPDATE_INTERVAL) + { + ws.textAll(buildStatusString()); + webSocketLastUpdate = millis(); + } +} + +void initWebSocket(AsyncWebServer *server) +{ + ws.onEvent(onEvent); + server->addHandler(&ws); +} diff --git a/src/websocket.h b/src/websocket.h new file mode 100644 index 0000000..05d1144 --- /dev/null +++ b/src/websocket.h @@ -0,0 +1,11 @@ +#include +#include "routes/status.h" + +#ifndef WEBSOCKET_H +#define WEBSOCKET_H + +void initWebSocket(AsyncWebServer *server); + +void webSocketLoop(); + +#endif