diff --git a/src/app.cpp b/src/app.cpp index 29972bab..b0401a22 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -495,7 +495,10 @@ void app::tickSend(void) { yield(); updateLed(); + + #if defined(ESP32) zeroexport(); + #endif } //----------------------------------------------------------------------------- @@ -622,12 +625,14 @@ void app::updateLed(void) { } } //----------------------------------------------------------------------------- +#if defined(ESP32) void app::zeroexport() { if (!mConfig->plugin.zexport.enabled) return; DynamicJsonDocument doc(512); JsonObject object = doc.to(); + object["path"] = "ctrl"; object["id"] = 0; object["val"] = round(mzExport.sum()); @@ -638,4 +643,5 @@ void app::zeroexport() { DPRINTLN(DBG_INFO, data);*/ mApi.ctrlRequest(object); -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/app.h b/src/app.h index 3989f6bd..0d0f89e6 100644 --- a/src/app.h +++ b/src/app.h @@ -283,7 +283,10 @@ class app : public IApp, public ah::Scheduler { void setupLed(); void updateLed(); + + #if defined(ESP32) void zeroexport(); + #endif void tickReboot(void) { DPRINTLN(DBG_INFO, F("Rebooting...")); diff --git a/src/config/settings.h b/src/config/settings.h index 0a34ed1f..5e6b1ef3 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -134,6 +134,8 @@ typedef struct { uint16_t interval; } cfgMqtt_t; +/* Zero Export section */ +#if defined(ESP32) typedef struct { float power; float pf; @@ -143,13 +145,12 @@ typedef struct { uint16_t total; uint16_t total_returned; } cfgShellyEM3_t; - typedef struct { char monitor_ip[ZEXPORT_ADDR_LEN]; bool enabled; cfgShellyEM3_t PHASE[3]; } cfgzeroExport_t; - +#endif typedef struct { bool enabled; @@ -190,7 +191,9 @@ typedef struct { typedef struct { display_t display; + #if defined(ESP32) cfgzeroExport_t zexport; + #endif } plugins_t; typedef struct { @@ -293,12 +296,12 @@ class settings { if(root.containsKey(F("nrf"))) jsonNrf(root[F("nrf")]); #if defined(ESP32) if(root.containsKey(F("cmt"))) jsonCmt(root[F("cmt")]); + if(root.containsKey(F("zeroExport"))) jsonzeroExport(root[F("zeroExport")]); #endif if(root.containsKey(F("ntp"))) jsonNtp(root[F("ntp")]); if(root.containsKey(F("sun"))) jsonSun(root[F("sun")]); if(root.containsKey(F("serial"))) jsonSerial(root[F("serial")]); if(root.containsKey(F("mqtt"))) jsonMqtt(root[F("mqtt")]); - if(root.containsKey(F("zeroExport"))) jsonzeroExport(root[F("zeroExport")]); if(root.containsKey(F("led"))) jsonLed(root[F("led")]); if(root.containsKey(F("plugin"))) jsonPlugin(root[F("plugin")]); if(root.containsKey(F("inst"))) jsonInst(root[F("inst")]); @@ -321,6 +324,7 @@ class settings { jsonNrf(root.createNestedObject(F("nrf")), true); #if defined(ESP32) jsonCmt(root.createNestedObject(F("cmt")), true); + jsonzeroExport(root.createNestedObject(F("zeroExport")), true); #endif jsonNtp(root.createNestedObject(F("ntp")), true); jsonSun(root.createNestedObject(F("sun")), true); @@ -329,7 +333,7 @@ class settings { jsonLed(root.createNestedObject(F("led")), true); jsonPlugin(root.createNestedObject(F("plugin")), true); jsonInst(root.createNestedObject(F("inst")), true); - jsonzeroExport(root.createNestedObject(F("zeroExport")), true); + DPRINT(DBG_INFO, F("memory usage: ")); DBGPRINTLN(String(json.memoryUsage())); @@ -446,8 +450,10 @@ class settings { mCfg.mqtt.interval = 0; // off // Zero-Export + #if defined(ESP32) snprintf(mCfg.plugin.zexport.monitor_ip, ZEXPORT_ADDR_LEN, "%s", DEF_ZEXPORT); mCfg.plugin.zexport.enabled = false; + #endif mCfg.inst.rstYieldMidNight = false; mCfg.inst.rstValsNotAvail = false; @@ -630,6 +636,7 @@ class settings { } } + #if defined(ESP32) void jsonzeroExport(JsonObject obj, bool set = false) { if(set) { obj[F("en_zeroexport")] = (bool) mCfg.plugin.zexport.enabled; @@ -639,7 +646,7 @@ class settings { for (size_t i = 0; i < sizeof(mCfg.plugin.zexport.PHASE); i++) { char str[10]; - sprintf(str, "phase_%d", i); + sprintf(str, "phase_%d", i + 1); obj[str][F("power")] = mCfg.plugin.zexport.PHASE[i].power; obj[str][F("pf")] = mCfg.plugin.zexport.PHASE[i].pf; obj[str][F("current")] = mCfg.plugin.zexport.PHASE[i].current; @@ -655,6 +662,7 @@ class settings { getChar(obj, F("monitor_ipAddr"), mCfg.plugin.zexport.monitor_ip, ZEXPORT_ADDR_LEN); } } + #endif void jsonLed(JsonObject obj, bool set = false) { if(set) { diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index 8cc38949..5e864e8a 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -1,7 +1,9 @@ +#if defined(ESP32) + #ifndef __ZEROEXPORT__ #define __ZEROEXPORT__ -#include +#include #include #include "AsyncJson.h" @@ -52,68 +54,48 @@ class ZeroExport { } private: - WiFiClient client; - int status = WL_IDLE_STATUS; - const uint16_t httpPort = 80; - const String url = "/emeter/"; - char msgBuffer[256] = {'\0'}; - - const String serverIp = "192.168.5.30"; - static char msg[50]; + HTTPClient http; + //char msgBuffer[256] = {'\0'}; void loop() { } void zero() { sendReq(0); sendReq(1); sendReq(2); - - } - // TODO: change int to u_short - void sendReq(int index) { - if (!client.connect(serverIp, httpPort)) { - delay(1000); - DPRINTLN(DBG_INFO, F("connection failed")); + void sendReq(int index) + { + http.begin(String(mCfg->monitor_ip), 80, "/emeter/" + String(index)); + + int httpResponseCode = http.GET(); + if (httpResponseCode > 0) + { + DynamicJsonDocument json(128); + deserializeJson(json, getData()); + mCfg->PHASE[index].power = json[F("power")]; + mCfg->PHASE[index].pf = json[F("pf")]; + mCfg->PHASE[index].current = json[F("current")]; + mCfg->PHASE[index].voltage = json[F("voltage")]; + mCfg->PHASE[index].is_valid = json[F("is_valid")]; + mCfg->PHASE[index].total = json[F("total")]; + mCfg->PHASE[index].total_returned = json[F("total_returned")]; + } + else + { + DPRINT(DBG_INFO, "Error code: "); + DPRINTLN(DBG_INFO, String(httpResponseCode)); } - strcpy ( msgBuffer, "GET "); - strcat ( msgBuffer, url.c_str()); - - char str[10]; - sprintf(str, "%d", index); - - strcat ( msgBuffer, str); - strcat ( msgBuffer, "\r\nHTTP/1.1\r\n\r\n" ); - client.print(msgBuffer); - - DynamicJsonDocument json(128); - String raw = getData(); - deserializeJson(json, raw); - - //DPRINTLN(DBG_INFO, raw); - - mCfg->PHASE[index].power = json[F("power")]; - mCfg->PHASE[index].pf = json[F("pf")]; - mCfg->PHASE[index].current = json[F("current")]; - mCfg->PHASE[index].voltage = json[F("voltage")]; - mCfg->PHASE[index].is_valid = json[F("is_valid")]; - mCfg->PHASE[index].total = json[F("total")]; - mCfg->PHASE[index].total_returned = json[F("total_returned")]; } - String getData() { - while (client.connected()) { - //TODO: what if available is not true? It will stuck here then... - while (client.available()) - { - String raw = client.readString(); - int x = raw.indexOf("{", 0); - client.stop(); - return raw.substring(x, raw.length()); - } - } - return F("error"); + String getData() + { + String payload = http.getString(); + DPRINTLN(DBG_INFO, payload); + + int x = payload.indexOf("{", 0); + return payload.substring(x, payload.length()); } // private member variables @@ -127,4 +109,6 @@ class ZeroExport { uint16_t mRefreshCycle; }; -#endif /*__ZEROEXPORT__*/ \ No newline at end of file +#endif /*__ZEROEXPORT__*/ + +#endif /* #if defined(ESP32) */ \ No newline at end of file diff --git a/src/web/RestApi.h b/src/web/RestApi.h index d7c1692e..1530c476 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -465,6 +465,7 @@ class RestApi { obj[F("interval")] = String(mConfig->mqtt.interval); } + #if defined(ESP32) void getzeroExport(JsonObject obj) { obj[F("en_zeroexport")] = (bool) mConfig->plugin.zexport.enabled; obj[F("monitor_ipAddr")] = String(mConfig->plugin.zexport.monitor_ip); @@ -483,6 +484,7 @@ class RestApi { phases[F("total_returned")] = mConfig->plugin.zexport.PHASE[i].total_returned; } } + #endif void getNtp(JsonObject obj) { obj[F("addr")] = String(mConfig->ntp.addr); @@ -609,7 +611,6 @@ class RestApi { getSysInfo(request, obj.createNestedObject(F("system"))); //getInverterList(obj.createNestedObject(F("inverter"))); getMqtt(obj.createNestedObject(F("mqtt"))); - getzeroExport(obj.createNestedObject(F("zeroExport"))); getNtp(obj.createNestedObject(F("ntp"))); getSun(obj.createNestedObject(F("sun"))); getPinout(obj.createNestedObject(F("pinout"))); @@ -618,6 +619,10 @@ class RestApi { getSerial(obj.createNestedObject(F("serial"))); getStaticIp(obj.createNestedObject(F("static_ip"))); getDisplay(obj.createNestedObject(F("display"))); + + #if defined(ESP32) + getzeroExport(obj.createNestedObject(F("zeroExport"))); + #endif } #if !defined(ETHERNET) diff --git a/src/web/html/setup.html b/src/web/html/setup.html index 975fd544..37adc279 100644 --- a/src/web/html/setup.html +++ b/src/web/html/setup.html @@ -316,8 +316,8 @@ - -
+ +
Zero Export
@@ -330,10 +330,12 @@
Shelly EM3 IP:
+
Prio Inverter
+
-

L1: n/a

-

L2: n/a

-

L3: n/a

+

L1: n/a

+

L2: n/a

+

L3: n/a

@@ -936,15 +938,47 @@ document.getElementById("date").innerHTML = toIsoDateStr((new Date((++ts) * 1000))); } - function parsezeroExport(obj) { + function parsezeroExport(obj, type, ) { + if ("ESP8266" == type) { + var e = document.getElementById("zeroExport"); + e.remove(); + + var e = document.getElementById("zeroExport_button"); + e.textContent += " (only for ESP32 available)"; + e.disabled = true; + element.classList.add("disabled"); + + return; + } + + getAjax("/api/inverter/list", parseZeroIv); + for(var i of [["monitor_ipAddr", "monitor_ipAddr"]]) if(null != obj[i[1]]) document.getElementsByName(i[0])[0].value = obj[i[1]]; document.getElementsByName("en_zeroexport")[0].checked = obj["en_zeroexport"]; - document.getElementsByName("phase_0")[0].innerHTML = "L1: " + obj["phase_0"]["power"].toFixed(2) + "W"; - document.getElementsByName("phase_1")[0].innerHTML = "L2: " + obj["phase_1"]["power"].toFixed(2) + "W"; - document.getElementsByName("phase_2")[0].innerHTML = "L3: " + obj["phase_2"]["power"].toFixed(2) + "W"; + + for (let i = 0; i < 3; i++) document.getElementsByName("phase_" + (i + 1))[0].innerHTML = "L" + (i + 1) + ": " + obj["phase_" + i]["power"].toFixed(2) + "W"; + } + + function parseZeroIv(root) + { + for(var i = 0; i < root.inverter.length; i++) + root.inverter[i]; + + select = document.getElementById('Inv_ID'); + parseInt(select.value) + + if(null == root) return; + root = root.inverter; + for(var i = 0; i < root.length; i++) { + inv = root[i]; + var opt = document.createElement('option'); + opt.value = inv.id; + opt.innerHTML = inv.name; + select.appendChild(opt); + } } function parse(root) { @@ -959,9 +993,10 @@ parseNrfRadio(root["radioNrf"], root["pinout"], root["system"]["esp_type"], root["system"]); if(root["generic"]["esp_type"] == "ESP32") parseCmtRadio(root["radioCmt"], root["system"]["esp_type"], root["system"]); + parsezeroExport(root["zeroExport"], root["system"]["esp_type"]); parseSerial(root["serial"]); parseDisplay(root["display"], root["system"]["esp_type"], root["system"]); - parsezeroExport(root["zeroExport"]); + getAjax("/api/inverter/list", parseIv); } } diff --git a/src/web/html/style.css b/src/web/html/style.css index 6d19bd21..f0992313 100644 --- a/src/web/html/style.css +++ b/src/web/html/style.css @@ -434,6 +434,10 @@ p.lic, p.lic a { color: #fff; } +.disabled { + background-color: dimgray; +} + .s_content { display: none; overflow: hidden; diff --git a/src/web/web.h b/src/web/web.h index 30127408..41eaab29 100644 --- a/src/web/web.h +++ b/src/web/web.h @@ -604,6 +604,7 @@ class Web { mConfig->mqtt.interval = request->arg("mqttInterval").toInt(); // zero-export + #if defined(ESP32) mConfig->plugin.zexport.enabled = (request->arg("en_zeroexport") == "on"); if (request->arg("monitor_ipAddr") != "") { String addr = request->arg("monitor_ipAddr"); @@ -611,6 +612,7 @@ class Web { addr.toCharArray(mConfig->plugin.zexport.monitor_ip, ZEXPORT_ADDR_LEN); } else mConfig->plugin.zexport.monitor_ip[0] = '\0'; + #endif // serial console if (request->arg("serIntvl") != "") {