mirror of
https://github.com/lumapu/ahoy.git
synced 2025-06-03 19:21:38 +02:00
0.8.1030019
0.8.1030019
This commit is contained in:
commit
fe1689979f
5 changed files with 303 additions and 114 deletions
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ class powermeter {
|
|||
if (result) {
|
||||
bufferWrite(power, group);
|
||||
mCfg->groups[group].power = power;
|
||||
|
||||
|
||||
// MQTT - Powermeter
|
||||
/// BUG: 002 Anfang - Muss dieser Teil raus? Führt er zu abstürzen wie BUG 001?
|
||||
if (mMqtt->isConnected()) {
|
||||
|
@ -239,7 +239,7 @@ class powermeter {
|
|||
|
||||
bufferWrite(power, group);
|
||||
mCfg->groups[group].power = power; // TODO: join two sites together (PM & MQTT)
|
||||
|
||||
|
||||
// MQTT - Powermeter
|
||||
/// BUG: 001 Anfang - Dieser Teil ist deaktiviert weil er zu abstürzen der DTU führt
|
||||
// if (mCfg->debug) {
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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
Normal file
38
src/utils/DynamicJsonHandler.cpp
Normal file
|
@ -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
Normal file
44
src/utils/DynamicJsonHandler.h
Normal file
|
@ -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…
Add table
Add a link
Reference in a new issue