From fa94909d05eb7a2c24d79ca4923e5b99d0253d1c Mon Sep 17 00:00:00 2001 From: Patrick Amrhein Date: Fri, 1 Mar 2024 23:28:32 +0100 Subject: [PATCH] 0.8.850032-zero --- src/app.cpp | 9 + src/config/settings.h | 18 +- src/defines.h | 2 +- src/plugins/zeroExport/zeroExport.h | 251 ++++++++++++++++++++++------ src/web/html/setup.html | 86 ++++------ 5 files changed, 259 insertions(+), 107 deletions(-) diff --git a/src/app.cpp b/src/app.cpp index 4ec21c9b..80ea0387 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -172,6 +172,15 @@ void app::loop(void) { if (mMqttEnabled && mNetworkConnected) mMqtt.loop(); #endif + + // Plugin ZeroExport + #if defined(PLUGIN_ZEROEXPORT) + if(mConfig->nrf.enabled || mConfig->cmt.enabled) { + mZeroExport.loop(); + } + #endif + // Plugin ZeroExport - Ende + yield(); } diff --git a/src/config/settings.h b/src/config/settings.h index 50a21ab0..fde3dd68 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -198,6 +198,10 @@ typedef struct { #define ZEROEXPORT_DEF_INV_WAITINGTIME_MS 10000 #if defined(PLUGIN_ZEROEXPORT) +enum class zeroExportState : uint8_t { + RESET, GETPOWERMETER, GETINVERTERPOWER, FINISH +}; + typedef enum { None = 0, Shelly = 1, @@ -268,7 +272,12 @@ typedef struct { uint16_t powerMax; - uint16_t startTimestamp; + zeroExportState state; + unsigned long lastRun; + float pmPower; + float pmPowerL1; + float pmPowerL2; + float pmPowerL3; // uint16_t power; // Aktueller Verbrauch // uint16_t powerLimitAkt; // Aktuelles Limit // uint16_t powerHyst; // Hysterese @@ -629,6 +638,13 @@ class settings { mCfg.plugin.zeroExport.groups[group].refresh = 10; mCfg.plugin.zeroExport.groups[group].powerTolerance = 10; mCfg.plugin.zeroExport.groups[group].powerMax = 600; +// + mCfg.plugin.zeroExport.groups[group].state = zeroExportState::RESET; + mCfg.plugin.zeroExport.groups[group].lastRun = 0; + mCfg.plugin.zeroExport.groups[group].pmPower = 0; + mCfg.plugin.zeroExport.groups[group].pmPowerL1 = 0; + mCfg.plugin.zeroExport.groups[group].pmPowerL2 = 0; + mCfg.plugin.zeroExport.groups[group].pmPowerL3 = 0; } #warning("Defaultsettings") diff --git a/src/defines.h b/src/defines.h index 054e884c..7d9725a0 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 8 -#define VERSION_PATCH 850023 +#define VERSION_PATCH 850032 //------------------------------------- typedef struct { diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index f3528763..3542011a 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -57,19 +57,71 @@ class ZeroExport { continue; } -// if (millis() - mCfg->groups[group].startTimestamp < mCfg->groups[group].refresh) { -// mCfg->groups[group].startTimestamp = mCfg->groups[group].startTimestamp + mCfg->groups[group].refresh; + switch (mCfg->groups[group].state) { + case zeroExportState::RESET: +//DBGPRINT(F("State::RESET: ")); +//DBGPRINTLN(String(group)); + mCfg->groups[group].lastRun = millis(); + // Weiter zum nächsten State + mCfg->groups[group].state = zeroExportState::GETPOWERMETER; + break; + case zeroExportState::GETPOWERMETER: + if ((millis() - mCfg->groups[group].lastRun) > (mCfg->groups[group].refresh * 1000UL)) { +//DBGPRINT(F("State::GETPOWERMETER:")); +//DBGPRINTLN(String(group)); + if (getPowermeterWatts(group)) { + // Weiter zum nächsten State + mCfg->groups[group].state = zeroExportState::GETINVERTERPOWER; + } else { + // Wartezeit wenn Keine Daten vom Powermeter + mCfg->groups[group].lastRun = (millis() - (mCfg->groups[group].refresh * 100UL)); + } + } + break; + case zeroExportState::GETINVERTERPOWER: + if ((millis() - mCfg->groups[group].lastRun) > (mCfg->groups[group].refresh * 1000UL)) { +//DBGPRINT(F("State::GETINVERTERPOWER:")); +//DBGPRINTLN(String(group)); + if (getInverterPowerWatts(group)) { + // Weiter zum nächsten State + mCfg->groups[group].state = zeroExportState::FINISH; + } else { + // Wartezeit wenn Keine Daten vom Powermeter + mCfg->groups[group].lastRun = (millis() - (mCfg->groups[group].refresh * 100UL)); + } + } + break; /* - if (getPowermeterWatts(group)) { - mCfg->groups[group].powermeter.nextRun = millis() + mCfg->groups[group].powermeter.interval; - mCfg->groups[group].powermeter.error = 0; -DBGPRINTLN(String("Powermeter: ") + String(mCfg->groups[group].powermeter.power) + String(" W")); - } else { - mCfg->groups[group].powermeter.error++; - continue; - } + case 4: + // + mCfg->groups[group].state = zeroExportState::RESET; + break; + case 5: + // + mCfg->groups[group].state = zeroExportState::RESET; + break; */ -// } + default: + // +//DBGPRINT(F("State::?: ")); +//DBGPRINTLN(String(group)); + mCfg->groups[group].state = zeroExportState::RESET; + break; + } + + + + + + + + + + + + + + /* if (!mCfg->groups[group].powermeter.error) { DBGPRINTLN(String("ok verarbeiten.")); @@ -270,65 +322,149 @@ DBGPRINTLN(String("nok Notmodus.")); bool getPowermeterWatts(uint8_t group) { bool ret = false; -// switch (mCfg->groups[group].powermeter.type) { -// case 1: -// ret = getPowermeterWattsTibber(); -// break; +DBGPRINT(String("getPowermeterWatts: ")); +DBGPRINTLN(String(group)); + switch (mCfg->groups[group].pm_type) { + case 1: + ret = getPowermeterWattsShelly(group); + break; // case 2: -// ret = getPowermeterWattsShelly(); +// ret = getPowermeterWattsTasmota(group); // break; // case 3: -// ret = getPowermeterWattsTasmota(); +// ret = getPowermeterWattsMqtt(group); // break; // case 4: -// ret = getPowermeterWattsMqtt(); +// ret = getPowermeterWattsHichi(group); +// break; +// case 5: +// ret = getPowermeterWattsTibber(group); // break; /// default: /// ret = false; /// break; -// } + } return ret; } - int getPowermeterWattsTibber(void) { - // TODO: - return 0; - } - - int getPowermeterWattsShelly(void) { -/* + int getPowermeterWattsShelly(uint8_t group) { + bool ret = false; HTTPClient http; - char url[100] = "http://"; - strcat(url, mCfg->monitor_url); - strcat(url, "/status"); - http.begin(url); - - if (http.GET() == 200) +// httpClient.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); + httpClient.setUserAgent("Ahoy-Agent"); +// TODO: Ahoy-0.8.850024-zero +// httpClient.setConnectTimeout(1000); +// httpClient.setTimeout(1000); +// httpClient.addHeader("Content-Type", "application/json"); +// httpClient.addHeader("Accept", "application/json"); + http.begin(mCfg->groups[group].pm_url); + if (http.GET() == HTTP_CODE_OK) { + // Parsing DynamicJsonDocument doc(2048); DeserializationError error = deserializeJson(doc, http.getString()); if (error) { - Serial.print("deserializeJson() failed: "); - Serial.println(error.c_str()); - return 0; +DBGPRINT(String("deserializeJson() failed: ")); +DBGPRINTLN(String(error.c_str())); + return ret; } - float total_power = doc["total_power"]; - int Shelly_Power = (int)(total_power + .5); - return Shelly_Power; -*/ - /* - String url = "http://" + String(SHELLY_IP) + "/status"; - ParsedData = http.get(url).json(); - int Watts = ParsedData['total_power'].toInt(); - return Watts; - */ -// } -// http.end(); + // Shelly 3EM + if (doc.containsKey(F("total_power"))) { + mCfg->groups[group].pmPower = doc["total_power"]; + ret = true; + // Shelly pro 3EM + } else if (doc.containsKey(F("em:0"))) { + mCfg->groups[group].pmPower = doc["em:0"]["total_act_power"]; + ret = true; + // Keine Daten + } else { + mCfg->groups[group].pmPower = 0; + } - return 0; + // Shelly 3EM + if (doc.containsKey(F("emeters"))) { + mCfg->groups[group].pmPowerL1 = doc["emeters"][0]["power"]; + ret = true; + // Shelly pro 3EM + } else if (doc.containsKey(F("em:0"))) { + mCfg->groups[group].pmPowerL1 = doc["em:0"]["a_act_power"]; + ret = true; + // Shelly plus1pm plus2pm + } else if (doc.containsKey(F("switch:0"))) { + mCfg->groups[group].pmPowerL1 = doc["switch:0"]["apower"]; + mCfg->groups[group].pmPower += mCfg->groups[group].pmPowerL1; + ret = true; + // Shelly Alternative + } else if (doc.containsKey(F("apower"))) { + mCfg->groups[group].pmPowerL1 = doc["apower"]; + mCfg->groups[group].pmPower += mCfg->groups[group].pmPowerL1; + ret = true; + // Keine Daten + } else { + mCfg->groups[group].pmPowerL1 = 0; + } +DBGPRINT(String("pmPowerL1: ")); +DBGPRINTLN(String(mCfg->groups[group].pmPowerL1)); + + // Shelly 3EM + if (doc.containsKey(F("emeters"))) { + mCfg->groups[group].pmPowerL2 = doc["emeters"][1]["power"]; + ret = true; + // Shelly pro 3EM + } else if (doc.containsKey(F("em:0"))) { + mCfg->groups[group].pmPowerL2 = doc["em:0"]["b_act_power"]; + ret = true; + // Shelly plus1pm plus2pm + } else if (doc.containsKey(F("switch:1"))) { + mCfg->groups[group].pmPowerL2 = doc["switch.1"]["apower"]; + mCfg->groups[group].pmPower += mCfg->groups[group].pmPowerL2; + ret = true; +// // Shelly Alternative +// } else if (doc.containsKey(F("apower"))) { +// mCfg->groups[group].pmPowerL2 = doc["apower"]; +// mCfg->groups[group].pmPower += mCfg->groups[group].pmPowerL2; +// ret = true; + // Keine Daten + } else { + mCfg->groups[group].pmPowerL2 = 0; + } +DBGPRINT(String("pmPowerL2: ")); +DBGPRINTLN(String(mCfg->groups[group].pmPowerL2)); + + // Shelly 3EM + if (doc.containsKey(F("emeters"))) { + mCfg->groups[group].pmPowerL3 = doc["emeters"][2]["power"]; + ret = true; + // Shelly pro 3EM + } else if (doc.containsKey(F("em:0"))) { + mCfg->groups[group].pmPowerL3 = doc["em:0"]["c_act_power"]; + ret = true; + // Shelly plus1pm plus2pm + } else if (doc.containsKey(F("switch:2"))) { + mCfg->groups[group].pmPowerL3 = doc["switch:2"]["apower"]; + mCfg->groups[group].pmPower += mCfg->groups[group].pmPowerL3; + ret = true; +// // Shelly Alternative +// } else if (doc.containsKey(F("apower"))) { +// mCfg->groups[group].pmPowerL3 = doc["apower"]; +// mCfg->groups[group].pmPower += mCfg->groups[group].pmPowerL3; +// ret = true; + // Keine Daten + } else { + mCfg->groups[group].pmPowerL3 = 0; + } +DBGPRINT(String("pmPowerL3: ")); +DBGPRINTLN(String(mCfg->groups[group].pmPowerL3)); + +DBGPRINT(String("pmPower: ")); +DBGPRINTLN(String(mCfg->groups[group].pmPower)); + } + http.end(); + + return ret; } int getPowermeterWattsTasmota(void) { @@ -369,11 +505,30 @@ DBGPRINTLN(String("nok Notmodus.")); return 0; } + int getPowermeterWattsHichi(void) { + // TODO: + return 0; + } + + int getPowermeterWattsTibber(void) { + // TODO: + return 0; + } + + bool getInverterPowerWatts(uint8_t group) { + bool ret = false; +DBGPRINT(String("getInverterPowerWatts: ")); +DBGPRINTLN(String(group)); +// switch (mCfg->groups[group].pm_type) { + return ret; + } + + diff --git a/src/web/html/setup.html b/src/web/html/setup.html index 7feeb02e..756a2f02 100644 --- a/src/web/html/setup.html +++ b/src/web/html/setup.html @@ -1249,14 +1249,20 @@ divRow("{#ZE_GROUP_TAB_POWERMETER_TYPE}", ml("select", {name: "pm_type", class: "text", id: "pm_type"}, null), ), - divRow("{#ZE_GROUP_TAB_POWERMETER_URL}", + divRow("{#ZE_GROUP_TAB_POWERMETER_URL}", [ ml("input", {name: "pm_url", class: "text", type: "text", value: obj.pm_url, maxlength: "100"}, null), + ml("p", {}, "(3em) - http://IP/status"), + ml("p", {}, "(pro3em) - http://IP/rpc/Shelly.GetStatus"), + ml("p", {}, "(plus1pm) - http://IP/rpc/Shelly.GetStatus"), + ml("p", {}, "(plus2pm) - http://IP/rpc/Shelly.GetStatus"), + ml("p", {}, "(plus1pmAlternative) - http://IP/rpc/switch.GetStatus?id=0"), + ml("p", {}, "(plus2pmAlternative) - http://IP/rpc/switch.GetStatus?id=0"), ml("p", {}, "A JSON-Format is required to work properly.
HICHI: http://IP_Address/cm?cmnd=status%208"), - ), - divRow("{#ZE_GROUP_TAB_POWERMETER_JSONPATH}", + ]), + divRow("{#ZE_GROUP_TAB_POWERMETER_JSONPATH}", [ ml("input", {name: "pm_jsonPath", class: "text", type: "text", value: obj.pm_jsonPath}, null), ml("p", {}, "Only for HICHI needed!"), - ), + ]), divRow("{#ZE_GROUP_TAB_POWERMETER_USER}", ml("input", {name: "pm_user", class: "text", type: "text", value: obj.pm_user}, null), ), @@ -1299,7 +1305,6 @@ e.appendChild(opt("3", "Mqtt")); e.appendChild(opt("4", "Hichi")); e.appendChild(opt("5", "Tibber")); - //e.selectedIndex = obj.pm_type; for (var i = 0; i < e.options.length; i++) { if (e.options[i].value == obj.pm_type) { e.selectedIndex = i; @@ -1317,11 +1322,9 @@ var e = document.getElementById("invId"+inv); selDelAllOpt(e); e.appendChild(opt("-1", "---")); -// TODO: Verhindert die Funktion des selects egal ob -1 oder "-1" for (var i = 0; i < ivObj.inverter.length; i++) { e.appendChild(opt((ivObj.inverter[i].id), (ivObj.inverter[i].name))); } - //e.selectedIndex = (obj.inverters[inv].id); for (var i = 0; i < (e.length); i++) { if (e.options[i].value == obj.inverters[inv].id) { e.selectedIndex = i; @@ -1340,7 +1343,6 @@ e.appendChild(opt("4", "L1 + Sum")); e.appendChild(opt("5", "L2 + Sum")); e.appendChild(opt("6", "L3 + Sum")); - //e.selectedIndex = (obj.inverters[inv].target); for (var i = 0; i < e.options.length; i++) { if (e.options[i].value == obj.inverters[inv].target) { e.selectedIndex = i; @@ -1380,10 +1382,8 @@ for(var inv = 0; inv < o.invMax; inv++) { var q = new Object(); q.enabled = document.getElementById("invEnabled"+inv).checked; - //q.id = document.getElementById("invId"+inv).selectedIndex+1; var e = document.getElementById("invId"+inv); q.id = e.options[e.selectedIndex].value; - //q.target = document.getElementById("invTarget"+inv).selectedIndex; var e = document.getElementById("invTarget"+inv); q.target = e.options[e.selectedIndex].value; q.twoPercent = document.getElementById("invTwoPercent"+inv).checked; @@ -1422,6 +1422,7 @@ modal("{#ZE_GROUP_DELETE_MODAL}: " + obj.name, html); function del() { +// TODO: Es wäre gut, wenn die Defaultwerte nicht hier sondern wie in der settings.h gesetzt würden. var o = new Object(); o.cmd = "ze_save_group"; // General @@ -1435,20 +1436,27 @@ o.pm_user = ""; o.pm_pass = ""; // Inverters -// o.ser = 0; -// o.ch = []; -// for(let i = 0; i < 6; i++) { -// var q = new Object(); -// q.pwr = 0; -// q.name = ""; -// q.yld = 0; -// o.ch.push(q); -// } + o.invMax = obj.inverters.length; + o.inverters = []; + for(var inv = 0; inv < o.invMax; inv++) { + var q = new Object(); + q.enabled = false; + var e = document.getElementById("invId"+inv); + q.id = -1; + var e = document.getElementById("invTarget"+inv); + q.target = -1; + q.twoPercent = false; + q.powerMax = 0; + o.inverters.push(q); + } + // Battery + o.battEnabled = false; + o.battVoltageOn = 0; + o.battVoltageOff = 0; // Advanced o.refresh = 10; o.powerTolerance = 10; o.powerMax = 600; -// TODO: Default aus Settings.h laden // Global getAjax("/api/setup", cb, "POST", JSON.stringify(o)); } @@ -1510,7 +1518,7 @@ ])); } -//#warning("groups") +// TODO: Das Add sollte anders / überhaupt gelöst werden var add = new Object(); add.enabled = true; add.id = obj.groups.length; @@ -1530,43 +1538,7 @@ // ivGlob(obj); - - -//#warning("groups") -// document.getElementsByName("en_zeroexport")[0].checked = obj["en_zeroexport"]; -// document.getElementsByName("two_percent")[0].checked = obj["two_percent"]; - -// document.getElementsByName("query_device")[0].checked = (obj["query_device"] == 1); -// document.getElementsByName("query_device")[1].checked = (obj["query_device"] == 2); -// document.getElementsByName("query_device")[2].checked = (obj["query_device"] == 3); - -// getAjax("/api/inverter/list", parseZeroExportIv); - -// for(var i of [["monitor_url", "monitor_url"], ["power_avg", "power_avg"], ["count_avg", "count_avg"], ["json_path", "json_path"], ["max_power", "max_power"], ["query_device", "query_device"]]) -// for(var i of [["monitor_url", "monitor_url"], ["power_avg", "power_avg"], ["count_avg", "count_avg"], ["json_path", "json_path"], ["max_power", "max_power"]]) -// if(null != obj[i[1]]) -// document.getElementsByName(i[0])[0].value = obj[i[1]]; - -// document.getElementsByName("total_power")[0].innerHTML = "Total: " + obj["total_power"].toFixed(2) + "W"; -// document.getElementById("Inv_ID").selectedIndex = obj["Iv"]; - } -/* - function parseZeroExportGroupInverterId(root) { - e = document.getElementById(""); - selDelAllOpt(e); - for (it of root.inverter) { - e.appendChild(opt(it.id, it.name)); - } - } - - function parseZeroExportGroupIverterTarget(root) { - e = document.getElementById(""); - selDelAllOpt(e); - for (it of root.inverter) { - e.appendChild(opt(it.id, it.name)); - } } -*/ // Plugin ZeroExport - Ende /*ENDIF_PLUGIN_ZEROEXPORT*/