diff --git a/include/WebApi.h b/include/WebApi.h index 34f0036f..40f5643c 100644 --- a/include/WebApi.h +++ b/include/WebApi.h @@ -8,6 +8,7 @@ #include "WebApi_ntp.h" #include "WebApi_sysstatus.h" #include "WebApi_webapp.h" +#include "WebApi_ws_live.h" #include class WebApiClass { @@ -29,6 +30,7 @@ private: WebApiNtpClass _webApiNtp; WebApiSysstatusClass _webApiSysstatus; WebApiWebappClass _webApiWebapp; + WebApiWsLiveClass _webApiWsLive; void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len); diff --git a/include/WebApi_ws_live.h b/include/WebApi_ws_live.h new file mode 100644 index 00000000..9e448117 --- /dev/null +++ b/include/WebApi_ws_live.h @@ -0,0 +1,15 @@ +#pragma once + +#include "ArduinoJson.h" +#include +#include + +class WebApiWsLiveClass { +public: + void init(AsyncWebSocket* ws); + void loop(); + +private: + AsyncWebSocket* _ws; + void addField(JsonDocument& root, uint8_t idx, std::shared_ptr inv, uint8_t channel, uint8_t fieldId); +}; \ No newline at end of file diff --git a/src/WebApi.cpp b/src/WebApi.cpp index e889ceba..2715e2af 100644 --- a/src/WebApi.cpp +++ b/src/WebApi.cpp @@ -28,6 +28,8 @@ void WebApiClass::init() _webApiSysstatus.init(&_server); _webApiWebapp.init(&_server); + _webApiWsLive.init(&_ws); + _server.onNotFound(std::bind(&WebApiClass::onNotFound, this, _1)); _server.begin(); } @@ -43,6 +45,8 @@ void WebApiClass::loop() _webApiSysstatus.loop(); _webApiWebapp.loop(); + _webApiWsLive.loop(); + // see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients _ws.cleanupClients(); } diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp new file mode 100644 index 00000000..66fc39d7 --- /dev/null +++ b/src/WebApi_ws_live.cpp @@ -0,0 +1,65 @@ +#include "WebApi_ws_live.h" +#include "AsyncJson.h" +#include + +void WebApiWsLiveClass::init(AsyncWebSocket* ws) +{ + _ws = ws; +} + +void WebApiWsLiveClass::loop() +{ + EVERY_N_SECONDS(10) + { + // do nothing if no WS client is connected + if (_ws->count() == 0) { + return; + } + + DynamicJsonDocument root(40960); + // Loop all inverters + for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) { + auto inv = Hoymiles.getInverterByPos(i); + + char buffer[sizeof(uint64_t) * 8 + 1]; + sprintf(buffer, "%0lx%08lx", + ((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)), + ((uint32_t)(inv->serial() & 0xFFFFFFFF))); + + root[i]["serial"] = String(buffer); + root[i]["name"] = inv->name(); + + // Loop all channels + for (uint8_t c = 0; c <= inv->getChannelCount(); c++) { + addField(root, i, inv, c, FLD_UDC); + addField(root, i, inv, c, FLD_IDC); + addField(root, i, inv, c, FLD_PDC); + addField(root, i, inv, c, FLD_YD); + addField(root, i, inv, c, FLD_YT); + addField(root, i, inv, c, FLD_UAC); + addField(root, i, inv, c, FLD_IAC); + addField(root, i, inv, c, FLD_PAC); + addField(root, i, inv, c, FLD_F); + addField(root, i, inv, c, FLD_T); + addField(root, i, inv, c, FLD_PCT); + addField(root, i, inv, c, FLD_EFF); + addField(root, i, inv, c, FLD_IRR); + } + } + + size_t len = measureJson(root); + AsyncWebSocketMessageBuffer* buffer = _ws->makeBuffer(len); // creates a buffer (len + 1) for you. + if (buffer) { + serializeJson(root, (char*)buffer->get(), len + 1); + _ws->textAll(buffer); + } + } +} + +void WebApiWsLiveClass::addField(JsonDocument& root, uint8_t idx, std::shared_ptr inv, uint8_t channel, uint8_t fieldId) +{ + if (inv->hasValue(channel, fieldId)) { + root[idx][String(channel)][inv->getName(channel, fieldId)]["v"] = inv->getValue(channel, fieldId); + root[idx][String(channel)][inv->getName(channel, fieldId)]["u"] = inv->getUnit(channel, fieldId); + } +} \ No newline at end of file