Browse Source

0.8.1030019

0.8.1030019
pull/1656/head
tictrick 8 months ago
committed by GitHub
parent
commit
fe1689979f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      src/defines.h
  2. 85
      src/plugins/zeroExport/powermeter.h
  3. 244
      src/plugins/zeroExport/zeroExport.h
  4. 38
      src/utils/DynamicJsonHandler.cpp
  5. 44
      src/utils/DynamicJsonHandler.h

2
src/defines.h

@ -13,7 +13,7 @@
//-------------------------------------
#define VERSION_MAJOR 0
#define VERSION_MINOR 8
#define VERSION_PATCH 1030018
#define VERSION_PATCH 1030019
//-------------------------------------
typedef struct {
uint8_t ch;

85
src/plugins/zeroExport/powermeter.h

@ -19,7 +19,7 @@
#include <list>
#include "plugins/zeroExport/lib/sml.h"
#include "utils/DynamicJsonHandler.h"
typedef struct {
const unsigned char OBIS[6];
void (*Fn)(double &);
@ -46,7 +46,7 @@ class powermeter {
* @param *log
* @returns void
*/
bool setup(IApp *app, zeroExport_t *cfg, PubMqttType *mqtt, JsonObject *log) {
bool setup(IApp *app, zeroExport_t *cfg, PubMqttType *mqtt, DynamicJsonHandler *log) {
mApp = app;
mCfg = cfg;
mMqtt = mqtt;
@ -84,7 +84,7 @@ class powermeter {
switch (mCfg->groups[group].pm_type) {
#if defined(ZEROEXPORT_POWERMETER_SHELLY)
case zeroExportPowermeterType_t::Shelly:
result = getPowermeterWattsShelly(*mLog, group, &power);
result = getPowermeterWattsShelly(group, &power);
break;
#endif
#if defined(ZEROEXPORT_POWERMETER_TASMOTA)
@ -105,12 +105,12 @@ class powermeter {
*/
case zeroExportPowermeterType_t::Tibber:
if (mCfg->groups[group].pm_refresh < 3) mCfg->groups[group].pm_refresh = 3;
result = getPowermeterWattsTibber(*mLog, group, &power);
result = getPowermeterWattsTibber(group, &power);
break;
#endif
#if defined(ZEROEXPORT_POWERMETER_SHRDZM)
case zeroExportPowermeterType_t::Shrdzm:
result = getPowermeterWattsShrdzm(*mLog, group, &power);
result = getPowermeterWattsShrdzm(group, &power);
break;
#endif
}
@ -280,7 +280,7 @@ class powermeter {
zeroExport_t *mCfg;
PubMqttType *mMqtt = nullptr;
JsonObject *mLog;
DynamicJsonHandler* mLog;
IApp *mApp = nullptr;
unsigned long mPreviousTsp = millis();
@ -294,7 +294,7 @@ class powermeter {
/** setHeader
*
*/
void setHeader(HTTPClient *h) {
void setHeader(HTTPClient *h, String auth = "", u8_t realm = NULL) {
h->setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
/// h->setUserAgent("Ahoy-Agent");
/// // TODO: Ahoy-0.8.850024-zero
@ -303,6 +303,43 @@ class powermeter {
h->setTimeout(1000);
h->addHeader("Content-Type", "application/json");
h->addHeader("Accept", "application/json");
/*
Shelly PM Mini Gen3
Shelly Plus 1PM
Shelly Plus 2PM
Shelly Pro 3EM - 120A
Shelly Pro 4PM
Shelly Pro Dual Cover / Shutter PM
Shelly Pro 1PM
Shelly Pro 2PM
Shelly Pro EM - 50
Shelly Qubino Wave 1PM Mini
Shelly Qubino Wave PM Mini
Shelly Qubino Wave Shutter
Shelly Qubino Wave 1PM
Shelly Qubino Wave 2PM
Shelly Qubino Wave Pro 1PM
Shelly Qubino Wave Pro 2PM
Shelly 3EM
Shelly EM + 120A Clamp
*/
/*if (auth != NULL && realm) http.addHeader("WWW-Authenticate", "Digest qop=\"auth\", realm=\"" + shellypro4pm-f008d1d8b8b8 + "\", nonce=\"60dc59c6\", algorithm=SHA-256");
else if (auth != NULL) http.addHeader("Authorization", "Basic " + auth);*/
/*
All Required:
realm: string, device_id of the Shelly device.
username: string, must be set to admin.
nonce: number, random or pseudo-random number to prevent replay attacks, taken from the error message.
cnonce: number, client nonce, random number generated by the client.
response: string, encoding of the string <ha1> + ":" + <nonce> + ":" + <nc> + ":" + <cnonce> + ":" + "auth" + ":" + <ha2> in SHA256.
ha1: string, <user>:<realm>:<password> encoded in SHA256
ha2: string, "dummy_method:dummy_uri" encoded in SHA256
algorithm: string, SHA-256.
*/
}
#if defined(ZEROEXPORT_POWERMETER_SHELLY)
@ -312,13 +349,13 @@ class powermeter {
* @param group
* @returns true/false
*/
bool getPowermeterWattsShelly(JsonObject logObj, uint8_t group, float *power) {
logObj["mod"] = "getPowermeterWattsShelly";
bool getPowermeterWattsShelly(uint8_t group, float *power) {
mLog->addProperty("mod", "getPowermeterWattsShelly");
setHeader(&http);
String url = String("http://") + String(mCfg->groups[group].pm_src) + String("/") + String(mCfg->groups[group].pm_jsonPath);
logObj["HTTP_URL"] = url;
mLog->addProperty("HTTP_URL", url);
http.begin(url);
@ -327,7 +364,7 @@ class powermeter {
DynamicJsonDocument doc(2048);
DeserializationError error = deserializeJson(doc, http.getString());
if (error) {
logObj["err"] = "deserializeJson: " + String(error.c_str());
mLog->addProperty("err", "deserializeJson: " + String(error.c_str()));
return false;
} else {
switch (mCfg->groups[group].pm_target) {
@ -395,7 +432,7 @@ class powermeter {
* @param group
* @returns true/false
*/
bool getPowermeterWattsTasmota(JsonObject logObj, uint8_t group, float *power) {
bool getPowermeterWattsTasmota(DynamicJsonHandler logObj, uint8_t group, float *power) {
logObj["mod"] = "getPowermeterWattsTasmota";
/*
// TODO: nicht komplett
@ -467,7 +504,7 @@ class powermeter {
* @param group
* @returns true/false
*/
bool getPowermeterWattsHichi(JsonObject logObj, uint8_t group, float *power) {
bool getPowermeterWattsHichi(DynamicJsonHandler logObj, uint8_t group, float *power) {
logObj["mod"] = "getPowermeterWattsHichi";
// Hier neuer Code - Anfang
@ -518,17 +555,20 @@ class powermeter {
{{0x01, 0x00, 0x01, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterImport},
{{0x01, 0x00, 0x02, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterExport}};
bool getPowermeterWattsTibber(JsonObject logObj, uint8_t group, float *power) {
/*
Daniel92: https://tibber.com/de/api/lookup/price-overview?postalCode=
Hab ich mal ausgelesen... hintendran die PLZ eingeben
energy/todayHours/<aktuelleStunde>/priceIncludingVat
*/
bool getPowermeterWattsTibber(uint8_t group, float *power) {
bool result = false;
logObj["mod"] = "getPowermeterWattsTibber";
mLog->addProperty("mod", "getPowermeterWattsTibber");
String auth = mCfg->groups[group].pm_pass;
String url = String("http://") + mCfg->groups[group].pm_src + String("/") + String(mCfg->groups[group].pm_jsonPath);
setHeader(&http);
setHeader(&http, auth);
http.begin(url);
http.addHeader("Authorization", "Basic " + auth);
if (http.GET() == HTTP_CODE_OK && http.getSize() > 0) {
String myString = http.getString();
@ -571,8 +611,8 @@ class powermeter {
* @TODO: Username & Passwort wird mittels base64 verschlüsselt. Dies wird für die Authentizierung benötigt. Wichtig diese im WebUI unkenntlich zu machen und base64 im eeprom zu speichern, statt klartext.
* @TODO: Abfrage Interval einbauen. Info: Datei-Size kann auch mal 0-bytes sein?
*/
bool getPowermeterWattsShrdzm(JsonObject logObj, uint8_t group, float *power) {
logObj["mod"] = "getPowermeterWattsShrdzm";
bool getPowermeterWattsShrdzm(uint8_t group, float *power) {
mLog->addProperty("mod", "getPowermeterWattsShrdzm");
setHeader(&http);
@ -587,7 +627,7 @@ class powermeter {
DynamicJsonDocument doc(512);
DeserializationError error = deserializeJson(doc, http.getString());
if (error) {
logObj["err"] = "deserializeJson: " + String(error.c_str());
mLog->addProperty("err", "deserializeJson: " + String(error.c_str()));
return false;
} else {
if (doc.containsKey(F("16.7.0"))) {
@ -606,8 +646,7 @@ class powermeter {
*/
void bufferWrite(float raw, short group) {
mPowermeterBuffer[group][mPowermeterBufferPos[group]] = raw;
mPowermeterBufferPos[group]++;
if (mPowermeterBufferPos[group] >= 5) mPowermeterBufferPos[group] = 0;
mPowermeterBufferPos[group] = (mPowermeterBufferPos[group] + 1) % 5;
}
};

244
src/plugins/zeroExport/zeroExport.h

@ -15,6 +15,8 @@
#include "AsyncJson.h"
#include "powermeter.h"
#include "utils/DynamicJsonHandler.h"
template <class HMSYSTEM>
class ZeroExport {
@ -47,9 +49,16 @@ class ZeroExport {
mApi = api;
mMqtt = mqtt;
mIsInitialized = mPowermeter.setup(mApp, mCfg, mqtt, &mLog);
mIsInitialized = mPowermeter.setup(mApp, mCfg, mqtt, &_log);
}
/*void printJson() {
serializeJson(doc, Serial);
Serial.println();
serializeJsonPretty(doc, Serial);
}*/
/** loop
* Arbeitsschleife
* @param void
@ -83,13 +92,13 @@ class ZeroExport {
zeroExportGroupInverter_t *CfgGroupInv = &CfgGroup->inverters[inv];
Inverter<> *iv = mSys->getInverterByPos(Queue.id);
mLog["g"] = group;
mLog["i"] = inv;
_log.addProperty("g", group);
_log.addProperty("i", inv);
// Check Data->iv
if (!iv->isAvailable()) {
if (mCfg->debug) {
mLog["nA"] = "!isAvailable";
_log.addProperty("nA", "!isAvailable");
sendLog();
}
clearLog();
@ -99,7 +108,7 @@ class ZeroExport {
// Check Data->waitAck
if (CfgGroupInv->waitAck > 0) {
if (mCfg->debug) {
mLog["wA"] = CfgGroupInv->waitAck;
_log.addProperty("wA", CfgGroupInv->waitAck);
sendLog();
}
clearLog();
@ -113,16 +122,19 @@ class ZeroExport {
groupPower += mCfg->groups[group].inverters[inv].power; // Calc Data->groupPower
groupLimit += mCfg->groups[group].inverters[inv].limit; // Calc Data->groupLimit
}
mLog["gP"] = groupPower;
mLog["gL"] = groupLimit;
_log.addProperty("gP", groupPower);
_log.addProperty("gL", groupLimit);
// Batteryprotection
mLog["bEn"] = (uint8_t)CfgGroup->battCfg;
_log.addProperty("bEn", (uint8_t)CfgGroup->battCfg);
switch (CfgGroup->battCfg) {
case zeroExportBatteryCfg::none:
if (CfgGroup->battSwitch != true) {
CfgGroup->battSwitch = true;
mLog["bA"] = "turn on";
_log.addProperty("bA", "turn on");
}
break;
case zeroExportBatteryCfg::invUdc:
@ -131,34 +143,34 @@ class ZeroExport {
if (CfgGroup->battSwitch != true) {
if (CfgGroup->battValue > CfgGroup->battLimitOn) {
CfgGroup->battSwitch = true;
mLog["bA"] = "turn on";
_log.addProperty("bA", "turn on");
}
if ((CfgGroup->battValue > CfgGroup->battLimitOff) && (CfgGroupInv->power > 0)) {
CfgGroup->battSwitch = true;
mLog["bA"] = "turn on";
_log.addProperty("bA", "turn on");
}
} else {
if (CfgGroup->battValue < CfgGroup->battLimitOff) {
CfgGroup->battSwitch = false;
mLog["bA"] = "turn off";
_log.addProperty("bA", "turn off");
}
}
mLog["bU"] = ah::round1(CfgGroup->battValue);
_log.addProperty("bU", ah::round1(CfgGroup->battValue));
break;
default:
if (CfgGroup->battSwitch == true) {
CfgGroup->battSwitch = false;
mLog["bA"] = "turn off";
_log.addProperty("bA", "turn off");
}
break;
}
mLog["bSw"] = CfgGroup->battSwitch;
_log.addProperty("bSw", CfgGroup->battSwitch);
// Controller
// Führungsgröße w in Watt
int16_t w = CfgGroup->setPoint;
mLog["w"] = w;
_log.addProperty("w", w);
// Regelgröße x in Watt
int16_t x = 0.0;
@ -167,16 +179,16 @@ class ZeroExport {
} else {
x = mPowermeter.getDataAVG(group);
}
mLog["x"] = x;
_log.addProperty("x", x);
// Regelabweichung e in Watt
int16_t e = w - x;
mLog["e"] = e;
_log.addProperty("e", e);
// Keine Regelung innerhalb der Toleranzgrenzen
if ((e < CfgGroup->powerTolerance) && (e > -CfgGroup->powerTolerance)) {
e = 0;
mLog["eK"] = e;
_log.addProperty("eK", e);
sendLog();
clearLog();
return;
@ -192,7 +204,7 @@ class ZeroExport {
CfgGroup->eSum += e;
int16_t yI = Ki * Ta * CfgGroup->eSum;
if (Ta == 0) {
mLog["Error"] = "Ta = 0";
_log.addProperty("Error", "Ta = 0");
sendLog();
clearLog();
return;
@ -200,21 +212,21 @@ class ZeroExport {
int16_t yD = Kd * (e - CfgGroup->eOld) / Ta;
if (mCfg->debug) {
mLog["Kp"] = Kp;
mLog["Ki"] = Ki;
mLog["Kd"] = Kd;
mLog["Ta"] = Ta;
mLog["yP"] = yP;
mLog["yI"] = yI;
mLog["eSum"] = CfgGroup->eSum;
mLog["yD"] = yD;
mLog["eOld"] = CfgGroup->eOld;
_log.addProperty("Kp", Kp);
_log.addProperty("Ki", Ki);
_log.addProperty("Kd", Kd);
_log.addProperty("Ta", Ta);
_log.addProperty("yP", yP);
_log.addProperty("yI", yI);
_log.addProperty("eSum", CfgGroup->eSum);
_log.addProperty("yD", yD);
_log.addProperty("eOld", CfgGroup->eOld);
}
CfgGroup->eOld = e;
int16_t y = yP + yI + yD;
mLog["y"] = y;
_log.addProperty("y", y);
// Regelbegrenzung
// TODO: Hier könnte man den maximalen Sprung begrenzen
@ -230,7 +242,7 @@ class ZeroExport {
if (CfgGroupInv->actionTimer == 0) CfgGroupInv->actionTimer = 1;
if (CfgGroupInv->actionTimer > 10) {
CfgGroupInv->action = zeroExportAction_t::doTurnOn;
mLog["do"] = "doTurnOn";
_log.addProperty("do", "doTurnOn");
}
}
if ((CfgGroupInv->turnOff) && (CfgGroupInv->limitNew <= 0) && (CfgGroupInv->power > 0)) {
@ -238,26 +250,28 @@ class ZeroExport {
if (CfgGroupInv->actionTimer == 0) CfgGroupInv->actionTimer = -1;
if (CfgGroupInv->actionTimer < 30) {
CfgGroupInv->action = zeroExportAction_t::doTurnOff;
mLog["do"] = "doTurnOff";
_log.addProperty("do", "doTurnOff");
}
}
if (((CfgGroup->battSwitch == false) || (mCfg->sleep == true) || (CfgGroup->sleep == true)) && (CfgGroupInv->power > 0)) {
CfgGroupInv->action = zeroExportAction_t::doTurnOff;
mLog["do"] = "sleep";
_log.addProperty("do", "sleep");
}
}
mLog["doT"] = CfgGroupInv->action;
_log.addProperty("doT", CfgGroupInv->action);
if (CfgGroupInv->action == zeroExportAction_t::doNone) {
mLog["l"] = CfgGroupInv->limit;
mLog["ln"] = CfgGroupInv->limitNew;
_log.addProperty("l", CfgGroupInv->limit);
_log.addProperty("ln", CfgGroupInv->limitNew);
// groupMax
uint16_t otherIvLimit = groupLimit - CfgGroupInv->limit;
if ((otherIvLimit + CfgGroupInv->limitNew) > CfgGroup->powerMax) {
CfgGroupInv->limitNew = CfgGroup->powerMax - otherIvLimit;
}
if (mCfg->debug) mLog["gPM"] = CfgGroup->powerMax;
if (mCfg->debug) {
_log.addProperty("gPM", CfgGroup->powerMax);
}
// PowerMax
uint16_t powerMax = 100;
@ -291,13 +305,13 @@ class ZeroExport {
// CfgGroupInv->actionTimer = 0;
// TODO: Timer stoppen wenn Limit gesetzt wird.
mLog["lN"] = CfgGroupInv->limitNew;
_log.addProperty("lN", CfgGroupInv->limitNew);
CfgGroupInv->limit = CfgGroupInv->limitNew;
}
// doAction
mLog["a"] = CfgGroupInv->action;
_log.addProperty("a", CfgGroupInv->action);
switch (CfgGroupInv->action) {
case zeroExportAction_t::doRestart:
@ -422,14 +436,20 @@ class ZeroExport {
if (!mCfg->groups[group].inverters[inv].enabled) continue;
if (iv->id == (uint8_t)mCfg->groups[group].inverters[inv].id) {
mLog["g"] = group;
mLog["i"] = inv;
_log.addProperty("g", group);
_log.addProperty("i", inv);
mCfg->groups[group].inverters[inv].waitAck = 0;
mLog["wA"] = mCfg->groups[group].inverters[inv].waitAck;
_log.addProperty("wA", mCfg->groups[group].inverters[inv].waitAck);
// Wenn ein neuer LimitWert da ist. Soll es in group schreiben.
if (iv->actPowerLimit != 0xffff) {
mLog["l"] = mCfg->groups[group].inverters[inv].limit;
_log.addProperty("l", mCfg->groups[group].inverters[inv].limit);
mCfg->groups[group].inverters[inv].limit = iv->actPowerLimit;
mLog["lF"] = mCfg->groups[group].inverters[inv].limit;
_log.addProperty("lF", mCfg->groups[group].inverters[inv].limit);
}
sendLog();
clearLog();
@ -453,10 +473,12 @@ class ZeroExport {
if (!mCfg->groups[group].inverters[inv].enabled) continue;
if (iv->id == mCfg->groups[group].inverters[inv].id) {
mLog["g"] = group;
mLog["i"] = inv;
_log.addProperty("g", group);
_log.addProperty("i", inv);
mCfg->groups[group].inverters[inv].waitAck = 0;
mLog["wA"] = mCfg->groups[group].inverters[inv].waitAck;
_log.addProperty("wA", mCfg->groups[group].inverters[inv].waitAck);
sendLog();
clearLog();
}
@ -479,10 +501,12 @@ class ZeroExport {
if (!mCfg->groups[group].inverters[inv].enabled) continue;
if (iv->id == mCfg->groups[group].inverters[inv].id) {
mLog["g"] = group;
mLog["i"] = inv;
_log.addProperty("g", group);
_log.addProperty("i", inv);
mCfg->groups[group].inverters[inv].waitAck = 0;
mLog["wA"] = mCfg->groups[group].inverters[inv].waitAck;
_log.addProperty("wA", mCfg->groups[group].inverters[inv].waitAck);
mCfg->groups[group].inverters[inv].limit = mCfg->groups[group].inverters[inv].powerMin;
iv->powerLimit[0] = static_cast<uint16_t>(mCfg->groups[group].inverters[inv].limit * 10.0);
@ -518,8 +542,8 @@ class ZeroExport {
if (!CfgGroupInv->enabled) continue;
if (CfgGroupInv->id != iv->id) continue;
mLog["g"] = group;
mLog["i"] = inv;
_log.addProperty("g", group);
_log.addProperty("i", inv);
// TODO: Ist nach eventAckSetLimit verschoben
// if (iv->actPowerLimit != 0xffff) {
@ -531,17 +555,19 @@ class ZeroExport {
// TODO: Es dauert bis getMaxPower übertragen wird.
if (iv->getMaxPower() > 0) {
CfgGroupInv->MaxPower = iv->getMaxPower();
mLog["pM"] = CfgGroupInv->MaxPower;
_log.addProperty("pM", CfgGroupInv->MaxPower);
}
record_t<> *rec;
rec = iv->getRecordStruct(RealTimeRunData_Debug);
if (iv->getLastTs(rec) > (millis() - 15000)) {
CfgGroupInv->power = iv->getChannelFieldValue(CH0, FLD_PAC, rec);
mLog["p"] = CfgGroupInv->power;
_log.addProperty("pM", CfgGroupInv->MaxPower);
CfgGroupInv->dcVoltage = iv->getChannelFieldValue(CH1, FLD_UDC, rec);
mLog["bU"] = ah::round1(CfgGroupInv->dcVoltage);
_log.addProperty("bU", ah::round1(CfgGroupInv->dcVoltage));
// Batterieüberwachung - Überwachung über die DC-Spannung am PV-Eingang 1 des Inverters
if (CfgGroup->battCfg == zeroExportBatteryCfg::invUdc) {
@ -561,16 +587,16 @@ class ZeroExport {
uint16_t powerPercent = 100 / CfgGroupInv->MaxPower * CfgGroupInv->power;
uint16_t delta = abs(limitPercent - powerPercent);
if ((delta > 10) && (CfgGroupInv->power > 0)) {
mLog["delta"] = delta;
_log.addProperty("delta", delta);
unsigned long delay = iv->getLastTs(rec) - CfgGroupInv->actionTimestamp;
mLog["delay"] = delay;
_log.addProperty("delay", delay);
if (delay > 30000) {
CfgGroupInv->action = zeroExportAction_t::doActivePowerContr;
mLog["do"] = "doActivePowerContr";
_log.addProperty("do", "doActivePowerContr");
}
if (delay > 60000) {
CfgGroupInv->action = zeroExportAction_t::doRestart;
mLog["do"] = "doRestart";
_log.addProperty("do", "doRestart");
}
}
}
@ -584,7 +610,6 @@ class ZeroExport {
sendLog();
clearLog();
return;
}
}
@ -634,40 +659,59 @@ class ZeroExport {
if (strcmp(mCfg->groups[group].battTopic, String(topic).c_str())) {
mCfg->groups[group].battValue = (bool)obj["val"];
mLog["k"] = mCfg->groups[group].battTopic;
mLog["v"] = mCfg->groups[group].battValue;
_log.addProperty("k", mCfg->groups[group].battTopic);
_log.addProperty("v", mCfg->groups[group].battValue);
}
}
// "topic":"ctrl/zero"
if (topic.indexOf("ctrl/zero") == -1) return;
if (mCfg->debug) mLog["d"] = obj;
_log.addProperty("d", obj);
if (obj["path"] == "ctrl" && obj["cmd"] == "zero") {
int8_t topicGroup = getGroupFromTopic(topic.c_str());
int8_t topicInverter = getInverterFromTopic(topic.c_str());
if (topicGroup != -1) mLog["g"] = topicGroup;
if (topicInverter == -1) mLog["i"] = topicInverter;
if (topicGroup != -1) {
_log.addProperty("g", topicGroup);
}
if (topicInverter == -1) {
_log.addProperty("i", topicInverter);
}
mLog["k"] = topic;
_log.addProperty("k", topic);
// "topic":"ctrl/zero/enabled"
if (topic.indexOf("ctrl/zero/enabled") != -1) mCfg->enabled = mLog["v"] = (bool)obj["val"];
if (topic.indexOf("ctrl/zero/enabled") != -1) {
_log.addProperty("v", (bool)obj["val"]);
mCfg->enabled = (bool)obj["val"];
}
// "topic":"ctrl/zero/sleep"
else if (topic.indexOf("ctrl/zero/sleep") != -1) mCfg->sleep = mLog["v"] = (bool)obj["val"];
else if (topic.indexOf("ctrl/zero/sleep") != -1) {
_log.addProperty("v", (bool)obj["val"]);
mCfg->sleep = (bool)obj["val"];
}
else if ((topicGroup >= 0) && (topicGroup < ZEROEXPORT_MAX_GROUPS))
{
String stopicGroup = String(topicGroup);
// "topic":"ctrl/zero/groups/+/enabled"
if (topic.endsWith("/enabled")) mCfg->groups[topicGroup].enabled = mLog["v"] = (bool)obj["val"];
if (topic.endsWith("/enabled")) {
_log.addProperty("v", (bool)obj["val"]);
mCfg->groups[topicGroup].enabled = (bool)obj["val"];
}
// "topic":"ctrl/zero/groups/+/sleep"
else if (topic.endsWith("/sleep")) mCfg->groups[topicGroup].sleep = mLog["v"] = (bool)obj["val"];
else if (topic.endsWith("/sleep")) {
_log.addProperty("v", (bool)obj["val"]);
mCfg->groups[topicGroup].sleep = (bool)obj["val"];
}
// Auf Eis gelegt, dafür 2 Gruppen mehr
// 0.8.103008.2
@ -692,36 +736,59 @@ class ZeroExport {
// }
// "topic":"ctrl/zero/groups/+/battery/switch"
else if (topic.endsWith("/battery/switch")) mCfg->groups[topicGroup].battSwitch = mLog["v"] = (bool)obj["val"];
else if (topic.endsWith("/battery/switch")) {
_log.addProperty("v", (bool)obj["val"]);
mCfg->groups[topicGroup].battSwitch = (bool)obj["val"];
}
else if (topic.indexOf("/advanced/") != -1)
{
// "topic":"ctrl/zero/groups/+/advanced/setPoint"
if (topic.endsWith("/setPoint")) mCfg->groups[topicGroup].setPoint = mLog["v"] = (int16_t)obj["val"];
if (topic.endsWith("/setPoint")) {
_log.addProperty("v", (int16_t)obj["val"]);
mCfg->groups[topicGroup].setPoint = (int16_t)obj["val"];
}
// "topic":"ctrl/zero/groups/+/advanced/powerTolerance"
else if (topic.endsWith("/powerTolerance")) mCfg->groups[topicGroup].powerTolerance = mLog["v"] = (uint8_t)obj["val"];
else if (topic.endsWith("/powerTolerance")) {
_log.addProperty("v", (uint8_t)obj["val"]);
mCfg->groups[topicGroup].powerTolerance = (uint8_t)obj["val"];
}
// "topic":"ctrl/zero/groups/+/advanced/powerMax"
else if (topic.endsWith("/powerMax")) mCfg->groups[topicGroup].powerMax = mLog["v"] = (uint16_t)obj["val"];
else if (topic.endsWith("/powerMax")) {
_log.addProperty("v", (uint16_t)obj["val"]);
mCfg->groups[topicGroup].powerMax = (uint16_t)obj["val"];
}
}
else if (topic.indexOf("/inverter/") != -1)
{
if ((topicInverter >= 0) && (topicInverter < ZEROEXPORT_GROUP_MAX_INVERTERS))
{
// "topic":"ctrl/zero/groups/+/inverter/+/enabled"
if (topic.endsWith("/enabled")) mCfg->groups[topicGroup].inverters[topicInverter].enabled = mLog["v"] = (bool)obj["val"];
if (topic.endsWith("/enabled")) {
_log.addProperty("v", (bool)obj["val"]);
mCfg->groups[topicGroup].inverters[topicInverter].enabled = (bool)obj["val"];
}
// "topic":"ctrl/zero/groups/+/inverter/+/powerMin"
else if (topic.endsWith("/powerMin")) mCfg->groups[topicGroup].inverters[topicInverter].powerMin = mLog["v"] = (uint16_t)obj["val"];
else if (topic.endsWith("/powerMin")) {
_log.addProperty("v", (uint16_t)obj["val"]);
mCfg->groups[topicGroup].inverters[topicInverter].powerMin = (uint16_t)obj["val"];
}
// "topic":"ctrl/zero/groups/+/inverter/+/powerMax"
else if (topic.endsWith("/powerMax")) mCfg->groups[topicGroup].inverters[topicInverter].powerMax = mLog["v"] = (uint16_t)obj["val"];
else mLog["k"] = "error";
else if (topic.endsWith("/powerMax")) {
_log.addProperty("v", (uint16_t)obj["val"]);
mCfg->groups[topicGroup].inverters[topicInverter].powerMax = (uint16_t)obj["val"];
}
else
{
_log.addProperty("k", "error");
}
}
}
else {
mLog["k"] = "error";
_log.addProperty("k", "error");
}
}
}
@ -770,7 +837,8 @@ class ZeroExport {
while (*pGroupSection != '/' && digitsCopied < 2) strGroup[digitsCopied++] = *pGroupSection++;
strGroup[digitsCopied] = '\0';
int8_t group = atoi(strGroup);
mLog["getGroupFromTopic"] = group;
_log.addProperty("getGroupFromTopic", "group");
return group;
}
@ -819,7 +887,7 @@ class ZeroExport {
void sendLog(void) {
// Log over Webserial
if (mCfg->log_over_webserial) {
DPRINTLN(DBG_INFO, String("ze: ") + mDocLog.as<String>());
DPRINTLN(DBG_INFO, String("ze: ") + _log.toString());
}
// Log over MQTT
@ -836,12 +904,15 @@ class ZeroExport {
* Löscht den LogSpeicher
*/
void clearLog(void) {
mDocLog.clear();
_log.clear();
}
// private member variables
bool mIsInitialized = false;
// Maximale Größe des JSON-Dokuments
static const size_t max_size = 5000;
IApp *mApp = nullptr;
uint32_t *mTimestamp = nullptr;
zeroExport_t *mCfg = nullptr;
@ -855,15 +926,12 @@ class ZeroExport {
unsigned long mLastRun = 0;
StaticJsonDocument<5000> mDocLog;
JsonObject mLog = mDocLog.to<JsonObject>();
powermeter mPowermeter;
PubMqttType *mMqtt = nullptr;
bool mIsSubscribed = false;
StaticJsonDocument<512> mqttDoc; // DynamicJsonDocument mqttDoc(512);
JsonObject mqttObj = mqttDoc.to<JsonObject>();
DynamicJsonHandler _log;
};
#endif /*__ZEROEXPORT__*/

38
src/utils/DynamicJsonHandler.cpp

@ -0,0 +1,38 @@
#include "DynamicJsonHandler.h"
DynamicJsonHandler::DynamicJsonHandler() : doc(min_size) {
}
DynamicJsonHandler::~DynamicJsonHandler() {
delete &doc;
}
String DynamicJsonHandler::toString() {
String jsonString;
serializeJson(doc, jsonString);
return jsonString;
}
void DynamicJsonHandler::clear() {
doc.clear();
}
size_t DynamicJsonHandler::size() const {
return doc.memoryUsage();
}
void DynamicJsonHandler::resizeDocument(size_t requiredSize) {
// TODO: multiplikator zwei muss ersetzt werden? Kann noch minimal werden.
size_t newCapacity = min(max(requiredSize * 2, min_size), max_size);
DynamicJsonDocument newDoc(newCapacity);
newDoc.set(doc); // Bestehende Daten kopieren
doc = std::move(newDoc);
}
size_t DynamicJsonHandler::min(size_t a, size_t b) {
return (a < b) ? a : b;
}
size_t DynamicJsonHandler::max(size_t a, size_t b) {
return (a > b) ? a : b;
}

44
src/utils/DynamicJsonHandler.h

@ -0,0 +1,44 @@
//-----------------------------------------------------------------------------
// 2022 Ahoy, https://github.com/lumpapu/ahoy
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//-----------------------------------------------------------------------------
#ifndef __DYNAMICJSONHANDLER_H__
#define __DYNAMICJSONHANDLER_H__
#include <Arduino.h>
#include <ArduinoJson.h>
#include <string>
class DynamicJsonHandler {
public:
DynamicJsonHandler();
~DynamicJsonHandler();
template<typename T>
void addProperty(const std::string& key, const T& value);
String toString();
void clear();
size_t size() const;
private:
DynamicJsonDocument doc;
static const size_t min_size = 256;
static const size_t max_size = 5000; // Max RAM : 2 = da es für resizeDocument eng werden könnte?
void resizeDocument(size_t requiredSize);
size_t min(size_t a, size_t b);
size_t max(size_t a, size_t b);
};
template<typename T>
void DynamicJsonHandler::addProperty(const std::string& key, const T& value) {
size_t additionalSize = JSON_OBJECT_SIZE(1) + key.length() + sizeof(value);
if (doc.memoryUsage() + additionalSize > doc.capacity()) {
resizeDocument(doc.memoryUsage() + additionalSize);
}
doc[key] = value;
}
#endif /*__DYNAMICJSONHANDLER_H__*/
Loading…
Cancel
Save