Webapi and websocket api for Battery

This commit is contained in:
MalteSchm 2023-04-02 13:00:46 +02:00
parent a0bbf61db2
commit 59c84bcb85
6 changed files with 281 additions and 0 deletions

View File

@ -27,6 +27,8 @@
#include "WebApi_vedirect.h" #include "WebApi_vedirect.h"
#include "WebApi_ws_Huawei.h" #include "WebApi_ws_Huawei.h"
#include "WebApi_Huawei.h" #include "WebApi_Huawei.h"
#include "WebApi_ws_Pylontech.h"
#include "WebApi_Pylontech.h"
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
class WebApiClass { class WebApiClass {
@ -70,6 +72,8 @@ private:
WebApiVedirectClass _webApiVedirect; WebApiVedirectClass _webApiVedirect;
WebApiHuaweiClass _webApiHuaweiClass; WebApiHuaweiClass _webApiHuaweiClass;
WebApiWsHuaweiLiveClass _webApiWsHuaweiLive; WebApiWsHuaweiLiveClass _webApiWsHuaweiLive;
WebApiPylontechClass _webApiPylontechClass;
WebApiWsPylontechLiveClass _webApiWsPylontechLive;
}; };

View File

@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ESPAsyncWebServer.h>
#include <AsyncJson.h>
class WebApiPylontechClass {
public:
void init(AsyncWebServer* server);
void loop();
void getJsonData(JsonObject& root);
private:
void onStatus(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "ArduinoJson.h"
#include <ESPAsyncWebServer.h>
class WebApiWsPylontechLiveClass {
public:
WebApiWsPylontechLiveClass();
void init(AsyncWebServer* server);
void loop();
private:
void generateJsonResponse(JsonVariant& root);
void onLivedataStatus(AsyncWebServerRequest* request);
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
AsyncWebServer* _server;
AsyncWebSocket _ws;
uint32_t _lastWsCleanup = 0;
uint32_t _lastUpdateCheck = 0;
};

View File

@ -43,6 +43,8 @@ void WebApiClass::init()
_webApiVedirect.init(&_server); _webApiVedirect.init(&_server);
_webApiWsHuaweiLive.init(&_server); _webApiWsHuaweiLive.init(&_server);
_webApiHuaweiClass.init(&_server); _webApiHuaweiClass.init(&_server);
_webApiWsPylontechLive.init(&_server);
_webApiPylontechClass.init(&_server);
_server.begin(); _server.begin();
} }
@ -74,6 +76,8 @@ void WebApiClass::loop()
_webApiVedirect.loop(); _webApiVedirect.loop();
_webApiWsHuaweiLive.loop(); _webApiWsHuaweiLive.loop();
_webApiHuaweiClass.loop(); _webApiHuaweiClass.loop();
_webApiWsPylontechLive.loop();
_webApiPylontechClass.loop();
} }
bool WebApiClass::checkCredentials(AsyncWebServerRequest* request) bool WebApiClass::checkCredentials(AsyncWebServerRequest* request)

87
src/WebApi_Pylontech.cpp Normal file
View File

@ -0,0 +1,87 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2022 Thomas Basler and others
*/
#include "WebApi_Pylontech.h"
#include "Battery.h"
#include "Configuration.h"
#include "MessageOutput.h"
#include "WebApi.h"
#include "WebApi_errors.h"
#include <AsyncJson.h>
#include <Hoymiles.h>
void WebApiPylontechClass::init(AsyncWebServer* server)
{
using std::placeholders::_1;
_server = server;
_server->on("/api/battery/livedata", HTTP_GET, std::bind(&WebApiPylontechClass::onStatus, this, _1));
}
void WebApiPylontechClass::loop()
{
}
void WebApiPylontechClass::getJsonData(JsonObject& root) {
root["data_age"] = (millis() - Battery.lastUpdate) / 1000;
root[F("chargeVoltage")]["v"] = Battery.chargeVoltage ;
root[F("chargeVoltage")]["u"] = "V";
root[F("chargeCurrentLimitation")]["v"] = Battery.chargeCurrentLimitation ;
root[F("chargeCurrentLimitation")]["u"] = "A";
root[F("dischargeCurrentLimitation")]["v"] = Battery.dischargeCurrentLimitation ;
root[F("dischargeCurrentLimitation")]["u"] = "A";
root[F("stateOfCharge")]["v"] = Battery.stateOfCharge ;
root[F("stateOfCharge")]["u"] = "%";
root[F("stateOfHealth")]["v"] = Battery.stateOfHealth ;
root[F("stateOfHealth")]["u"] = "%";
root[F("voltage")]["v"] = Battery.voltage;
root[F("voltage")]["u"] = "V";
root[F("current")]["v"] = Battery.current ;
root[F("current")]["u"] = "A";
root[F("temperature")]["v"] = Battery.temperature ;
root[F("temperature")]["u"] = "°C";
// Alarms
root["alarms"][F("dischargeCurrent")] = Battery.alarmOverCurrentDischarge ;
root["alarms"][F("chargeCurrent")] = Battery.alarmOverCurrentCharge ;
root["alarms"][F("lowTemperature")] = Battery.alarmUnderTemperature ;
root["alarms"][F("highTemperature")] = Battery.alarmOverTemperature ;
root["alarms"][F("lowVoltage")] = Battery.alarmUnderVoltage ;
root["alarms"][F("highVoltage")] = Battery.alarmOverVoltage ;
root["alarms"][F("bmsInternal")] = Battery.alarmBmsInternal ;
// Warnings
root["warnings"][F("dischargeCurrent")] = Battery.warningHighCurrentDischarge ;
root["warnings"][F("chargeCurrent")] = Battery.warningHighCurrentCharge ;
root["warnings"][F("lowTemperature")] = Battery.warningLowTemperature ;
root["warnings"][F("highTemperature")] = Battery.warningHighTemperature ;
root["warnings"][F("lowVoltage")] = Battery.warningLowVoltage ;
root["warnings"][F("highVoltage")] = Battery.warningHighVoltage ;
root["warnings"][F("bmsInternal")] = Battery.warningBmsInternal ;
// Misc
root[F("manufacturer")] = Battery.manufacturer ;
root[F("chargeEnabled")] = Battery.chargeEnabled ;
root[F("dischargeEnabled")] = Battery.dischargeEnabled ;
root[F("chargeImmediately")] = Battery.chargeImmediately ;
}
void WebApiPylontechClass::onStatus(AsyncWebServerRequest* request)
{
if (!WebApi.checkCredentialsReadonly(request)) {
return;
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
getJsonData(root);
response->setLength();
request->send(response);
}

146
src/WebApi_ws_Pylontech.cpp Normal file
View File

@ -0,0 +1,146 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2022 Thomas Basler and others
*/
#include "WebApi_ws_Pylontech.h"
#include "AsyncJson.h"
#include "Configuration.h"
#include "Battery.h"
#include "MessageOutput.h"
#include "WebApi.h"
#include "defaults.h"
WebApiWsPylontechLiveClass::WebApiWsPylontechLiveClass()
: _ws("/Pylontechlivedata")
{
}
void WebApiWsPylontechLiveClass::init(AsyncWebServer* server)
{
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
using std::placeholders::_4;
using std::placeholders::_5;
using std::placeholders::_6;
_server = server;
_server->on("/api/batterylivedata/status", HTTP_GET, std::bind(&WebApiWsPylontechLiveClass::onLivedataStatus, this, _1));
_server->addHandler(&_ws);
_ws.onEvent(std::bind(&WebApiWsPylontechLiveClass::onWebsocketEvent, this, _1, _2, _3, _4, _5, _6));
}
void WebApiWsPylontechLiveClass::loop()
{
// see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients
if (millis() - _lastWsCleanup > 1000) {
_ws.cleanupClients();
_lastWsCleanup = millis();
}
// do nothing if no WS client is connected
if (_ws.count() == 0) {
return;
}
if (millis() - _lastUpdateCheck < 1000) {
return;
}
_lastUpdateCheck = millis();
DynamicJsonDocument root(1024);
JsonVariant var = root;
generateJsonResponse(var);
String buffer;
if (buffer) {
serializeJson(root, buffer);
if (Configuration.get().Security_AllowReadonly) {
_ws.setAuthentication("", "");
} else {
_ws.setAuthentication(AUTH_USERNAME, Configuration.get().Security_Password);
}
_ws.textAll(buffer);
}
}
void WebApiWsPylontechLiveClass::generateJsonResponse(JsonVariant& root)
{
root["data_age"] = (millis() - Battery.lastUpdate) / 1000;
root[F("chargeVoltage")]["v"] = Battery.chargeVoltage ;
root[F("chargeVoltage")]["u"] = "V";
root[F("chargeCurrentLimitation")]["v"] = Battery.chargeCurrentLimitation ;
root[F("chargeCurrentLimitation")]["u"] = "A";
root[F("dischargeCurrentLimitation")]["v"] = Battery.dischargeCurrentLimitation ;
root[F("dischargeCurrentLimitation")]["u"] = "A";
root[F("stateOfCharge")]["v"] = Battery.stateOfCharge ;
root[F("stateOfCharge")]["u"] = "%";
root[F("stateOfHealth")]["v"] = Battery.stateOfHealth ;
root[F("stateOfHealth")]["u"] = "%";
root[F("voltage")]["v"] = Battery.voltage;
root[F("voltage")]["u"] = "V";
root[F("current")]["v"] = Battery.current ;
root[F("current")]["u"] = "A";
root[F("temperature")]["v"] = Battery.temperature ;
root[F("temperature")]["u"] = "°C";
// Alarms
root["alarms"][F("dischargeCurrent")] = Battery.alarmOverCurrentDischarge ;
root["alarms"][F("chargeCurrent")] = Battery.alarmOverCurrentCharge ;
root["alarms"][F("lowTemperature")] = Battery.alarmUnderTemperature ;
root["alarms"][F("highTemperature")] = Battery.alarmOverTemperature ;
root["alarms"][F("lowVoltage")] = Battery.alarmUnderVoltage ;
root["alarms"][F("highVoltage")] = Battery.alarmOverVoltage ;
root["alarms"][F("bmsInternal")] = Battery.alarmBmsInternal ;
// Warnings
root["warnings"][F("dischargeCurrent")] = Battery.warningHighCurrentDischarge ;
root["warnings"][F("chargeCurrent")] = Battery.warningHighCurrentCharge ;
root["warnings"][F("lowTemperature")] = Battery.warningLowTemperature ;
root["warnings"][F("highTemperature")] = Battery.warningHighTemperature ;
root["warnings"][F("lowVoltage")] = Battery.warningLowVoltage ;
root["warnings"][F("highVoltage")] = Battery.warningHighVoltage ;
root["warnings"][F("bmsInternal")] = Battery.warningBmsInternal ;
// Misc
root[F("manufacturer")] = Battery.manufacturer ;
root[F("chargeEnabled")] = Battery.chargeEnabled ;
root[F("dischargeEnabled")] = Battery.dischargeEnabled ;
root[F("chargeImmediately")] = Battery.chargeImmediately ;
}
void WebApiWsPylontechLiveClass::onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len)
{
if (type == WS_EVT_CONNECT) {
char str[64];
snprintf(str, sizeof(str), "Websocket: [%s][%u] connect", server->url(), client->id());
Serial.println(str);
MessageOutput.println(str);
} else if (type == WS_EVT_DISCONNECT) {
char str[64];
snprintf(str, sizeof(str), "Websocket: [%s][%u] disconnect", server->url(), client->id());
Serial.println(str);
MessageOutput.println(str);
}
}
void WebApiWsPylontechLiveClass::onLivedataStatus(AsyncWebServerRequest* request)
{
if (!WebApi.checkCredentialsReadonly(request)) {
return;
}
AsyncJsonResponse* response = new AsyncJsonResponse(false, 1024U);
JsonVariant root = response->getRoot().as<JsonVariant>();
generateJsonResponse(root);
response->setLength();
request->send(response);
}