Webapi and websocket api for Battery
This commit is contained in:
parent
a0bbf61db2
commit
59c84bcb85
@ -27,6 +27,8 @@
|
||||
#include "WebApi_vedirect.h"
|
||||
#include "WebApi_ws_Huawei.h"
|
||||
#include "WebApi_Huawei.h"
|
||||
#include "WebApi_ws_Pylontech.h"
|
||||
#include "WebApi_Pylontech.h"
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
class WebApiClass {
|
||||
@ -70,6 +72,8 @@ private:
|
||||
WebApiVedirectClass _webApiVedirect;
|
||||
WebApiHuaweiClass _webApiHuaweiClass;
|
||||
WebApiWsHuaweiLiveClass _webApiWsHuaweiLive;
|
||||
WebApiPylontechClass _webApiPylontechClass;
|
||||
WebApiWsPylontechLiveClass _webApiWsPylontechLive;
|
||||
|
||||
};
|
||||
|
||||
|
||||
17
include/WebApi_Pylontech.h
Normal file
17
include/WebApi_Pylontech.h
Normal 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;
|
||||
};
|
||||
23
include/WebApi_ws_Pylontech.h
Normal file
23
include/WebApi_ws_Pylontech.h
Normal 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;
|
||||
};
|
||||
@ -43,6 +43,8 @@ void WebApiClass::init()
|
||||
_webApiVedirect.init(&_server);
|
||||
_webApiWsHuaweiLive.init(&_server);
|
||||
_webApiHuaweiClass.init(&_server);
|
||||
_webApiWsPylontechLive.init(&_server);
|
||||
_webApiPylontechClass.init(&_server);
|
||||
|
||||
_server.begin();
|
||||
}
|
||||
@ -74,6 +76,8 @@ void WebApiClass::loop()
|
||||
_webApiVedirect.loop();
|
||||
_webApiWsHuaweiLive.loop();
|
||||
_webApiHuaweiClass.loop();
|
||||
_webApiWsPylontechLive.loop();
|
||||
_webApiPylontechClass.loop();
|
||||
}
|
||||
|
||||
bool WebApiClass::checkCredentials(AsyncWebServerRequest* request)
|
||||
|
||||
87
src/WebApi_Pylontech.cpp
Normal file
87
src/WebApi_Pylontech.cpp
Normal 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
146
src/WebApi_ws_Pylontech.cpp
Normal 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);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user