From 3bd8fe61b55acc800070b8ca5ddbdcbeded39e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20K?= Date: Sat, 28 Jan 2023 05:58:32 +0100 Subject: [PATCH 1/6] Fix attempt for HA autodiscovery --- src/publisher/pubMqtt.h | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/publisher/pubMqtt.h b/src/publisher/pubMqtt.h index f066a38a..dc80d06d 100644 --- a/src/publisher/pubMqtt.h +++ b/src/publisher/pubMqtt.h @@ -236,23 +236,24 @@ class PubMqtt { const char *devCls = getFieldDeviceClass(rec->assign[i].fieldId); const char *stateCls = getFieldStateClass(rec->assign[i].fieldId); - doc.clear(); - doc[F("name")] = name; - doc[F("stat_t")] = String(mCfgMqtt->topic) + "/" + String(iv->config->name) + String(topic); - doc[F("unit_of_meas")] = iv->getUnit(i, rec); - doc[F("uniq_id")] = String(iv->config->serial.u64, HEX) + "_" + uniq_id; - doc[F("dev")] = deviceObj; - doc[F("exp_aft")] = MQTT_INTERVAL + 5; // add 5 sec if connection is bad or ESP too slow @TODO: stimmt das wirklich als expire!? + DynamicJsonDocument doc2(512); + doc2.clear(); + doc2[F("name")] = name; + doc2[F("stat_t")] = String(mCfgMqtt->topic) + "/" + String(iv->config->name) + String(topic); + doc2[F("unit_of_meas")] = iv->getUnit(i, rec); + doc2[F("uniq_id")] = String(iv->config->serial.u64, HEX) + "_" + uniq_id; + doc2[F("dev")] = deviceObj; + doc2[F("exp_aft")] = MQTT_INTERVAL + 5; // add 5 sec if connection is bad or ESP too slow @TODO: stimmt das wirklich als expire!? if (devCls != NULL) - doc[F("dev_cla")] = String(devCls); + doc2[F("dev_cla")] = String(devCls); if (stateCls != NULL) - doc[F("stat_cla")] = String(stateCls); + doc2[F("stat_cla")] = String(stateCls); snprintf(topic, 64, "%s/sensor/%s/ch%d_%s/config", MQTT_DISCOVERY_PREFIX, iv->config->name, rec->assign[i].ch, iv->getFieldName(i, rec)); - size_t size = measureJson(doc) + 1; + size_t size = measureJson(doc2) + 1; char *buf = new char[size]; memset(buf, 0, size); - serializeJson(doc, buf, size); + serializeJson(doc2, buf, size); publish(topic, buf, true, false); delete[] buf; } From 9456937baf8bdef0f182f0814d00d3128e9dd5f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20K?= Date: Sat, 28 Jan 2023 10:39:09 +0100 Subject: [PATCH 2/6] Add Autodiscovery for total topic --- src/publisher/pubMqtt.h | 74 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/src/publisher/pubMqtt.h b/src/publisher/pubMqtt.h index dc80d06d..e59eb9f4 100644 --- a/src/publisher/pubMqtt.h +++ b/src/publisher/pubMqtt.h @@ -258,8 +258,72 @@ class PubMqtt { delete[] buf; } - yield(); + yield(); } +//Début modif + + String node_mac = WiFi.macAddress().substring(12,14)+ WiFi.macAddress().substring(15,17); + String node_id = "AHOY_DTU_" + node_mac; + doc.clear(); + doc[F("name")] = node_id; + doc[F("ids")] = node_id; + doc[F("cu")] = F("http://") + String(WiFi.localIP().toString()); + doc[F("mf")] = F("Hoymiles"); + doc[F("mdl")] = "AHOY_DTU"; + JsonObject deviceObj = doc.as(); + + uint8_t fieldId; + String fieldUnit; + for (uint8_t i = 0; i < 4; i++) { + switch (i) { + default: + case 0: + fieldId = FLD_PAC; + fieldUnit = "W"; + break; + case 1: + fieldId = FLD_YT; + fieldUnit = "kWh"; + break; + case 2: + fieldId = FLD_YD; + fieldUnit = "Wh"; + break; + case 3: + fieldId = FLD_PDC; + fieldUnit = "W"; + break; + } + + snprintf(name, 32, "Total %s", fields[fieldId]); + snprintf(topic, 64, "/%s", fields[fieldId]); + + + const char *devCls = getFieldDeviceClass(fieldId); + const char *stateCls = getFieldStateClass(fieldId); + + DynamicJsonDocument doc2(512); + doc2.clear(); + doc2[F("name")] = String(name); + doc2[F("stat_t")] = String(mCfgMqtt->topic) + "/total" + String(topic); + doc2[F("unit_of_meas")] = fieldUnit; + doc2[F("uniq_id")] = String(node_id) + "_" + String(fields[fieldId]); + doc2[F("dev")] = deviceObj; + doc2[F("exp_aft")] = MQTT_INTERVAL + 5; // add 5 sec if connection is bad or ESP too slow @TODO: stimmt das wirklich als expire!? + if (devCls != NULL) + doc2[F("dev_cla")] = String(devCls); + if (stateCls != NULL) + doc2[F("stat_cla")] = String(stateCls); + + snprintf(topic, 64, "%s/sensor/%s/Total_%s/config", MQTT_DISCOVERY_PREFIX, node_id.c_str(),fields[fieldId]); + size_t size = measureJson(doc2) + 1; + char *buf = new char[size]; + memset(buf, 0, size); + serializeJson(doc2, buf, size); + publish(topic, buf, true, false); + delete[] buf; + } + yield(); } void setPowerLimitAck(Inverter<> *iv) { @@ -388,6 +452,14 @@ class PubMqtt { return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : stateClasses[deviceFieldAssignment[pos].stateClsId]; } + const char *getFieldUnit(uint8_t fieldId) { + uint8_t pos = 0; + for (; pos < DEVICE_CLS_ASSIGN_LIST_LEN; pos++) { + if (deviceFieldAssignment[pos].fieldId == fieldId) + break; + } + return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : deviceClasses[deviceFieldAssignment[pos].deviceClsId]; + } bool processIvStatus() { // returns true if all inverters are available bool allAvail = true; From bd8de5f8f93267357c00d8b42b4829230e3f6d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20K?= Date: Sun, 29 Jan 2023 11:43:39 +0100 Subject: [PATCH 3/6] Send totals values once --- src/publisher/pubMqtt.h | 56 ++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/src/publisher/pubMqtt.h b/src/publisher/pubMqtt.h index 1b3effd3..c0bcff5e 100644 --- a/src/publisher/pubMqtt.h +++ b/src/publisher/pubMqtt.h @@ -217,24 +217,39 @@ class PubMqtt { uint8_t fldTotal[4] = {FLD_PAC, FLD_YT, FLD_YD, FLD_PDC}; const char* unitTotal[4] = {"W", "kWh", "Wh", "W"}; - for (uint8_t id = 0; id < mSys->getNumInverters(); id++) { - Inverter<> *iv = mSys->getInverterByPos(id); - if (NULL == iv) - continue; + String node_mac = WiFi.macAddress().substring(12,14)+ WiFi.macAddress().substring(15,17); + String node_id = "AHOY_DTU_" + node_mac; + bool total = false; - record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); + for (uint8_t id = 0; id < mSys->getNumInverters() ; id++) { doc.clear(); - doc[F("name")] = iv->config->name; - doc[F("ids")] = String(iv->config->serial.u64, HEX); + if (total) // total become true at iv = NULL next cycle + continue; + + Inverter<> *iv = mSys->getInverterByPos(id); + if (NULL == iv) + total = true; + record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); + + if (!total) { + doc[F("name")] = iv->config->name; + doc[F("ids")] = String(iv->config->serial.u64, HEX); + doc[F("mdl")] = iv->config->name; + } + else { + doc[F("name")] = node_id; + doc[F("ids")] = node_id; + doc[F("mdl")] = node_id; + } + doc[F("cu")] = F("http://") + String(WiFi.localIP().toString()); doc[F("mf")] = F("Hoymiles"); - doc[F("mdl")] = iv->config->name; JsonObject deviceObj = doc.as(); // deviceObj is only pointer!? - for (uint8_t i = 0; i < (rec->length + 4); i++) { + for (uint8_t i = 0; i < ((!total) ? (rec->length) : (4) ) ; i++) { const char *devCls, *stateCls; - if(i < rec->length) { + if (!total) { if (rec->assign[i].ch == CH0) snprintf(name, 32, "%s %s", iv->config->name, iv->getFieldName(i, rec)); else @@ -245,19 +260,20 @@ class PubMqtt { devCls = getFieldDeviceClass(rec->assign[i].fieldId); stateCls = getFieldStateClass(rec->assign[i].fieldId); } + else { // total values - snprintf(name, 32, "Total %s", fields[fldTotal[i-rec->length]]); - snprintf(topic, 64, "/%s", fields[fldTotal[i-rec->length]]); - snprintf(uniq_id, 32, "total_%s", fields[fldTotal[i-rec->length]]); - devCls = getFieldDeviceClass(fldTotal[i-rec->length]); - stateCls = getFieldStateClass(fldTotal[i-rec->length]); + snprintf(name, 32, "Total %s", fields[fldTotal[i]]); + snprintf(topic, 64, "/%s", fields[fldTotal[i]]); + snprintf(uniq_id, 32, "total_%s", fields[fldTotal[i]]); + devCls = getFieldDeviceClass(fldTotal[i]); + stateCls = getFieldStateClass(fldTotal[i]); } DynamicJsonDocument doc2(512); doc2[F("name")] = name; - doc2[F("stat_t")] = String(mCfgMqtt->topic) + "/" + ((i < rec->length) ? String(iv->config->name) : "total" ) + String(topic); - doc2[F("unit_of_meas")] = ((i < rec->length) ? (iv->getUnit(i,rec)) : (unitTotal[i-rec->length])); - doc2[F("uniq_id")] = String(iv->config->serial.u64, HEX) + "_" + uniq_id; + doc2[F("stat_t")] = String(mCfgMqtt->topic) + "/" + ((!total) ? String(iv->config->name) : "total" ) + String(topic); + doc2[F("unit_of_meas")] = ((!total) ? (iv->getUnit(i,rec)) : (unitTotal[i])); + doc2[F("uniq_id")] = ((!total) ? (String(iv->config->serial.u64, HEX)) : (node_id)) + "_" + uniq_id; doc2[F("dev")] = deviceObj; doc2[F("exp_aft")] = MQTT_INTERVAL + 5; // add 5 sec if connection is bad or ESP too slow @TODO: stimmt das wirklich als expire!? if (devCls != NULL) @@ -265,10 +281,10 @@ class PubMqtt { if (stateCls != NULL) doc2[F("stat_cla")] = String(stateCls); - if(i < rec->length) + if (!total) snprintf(topic, 64, "%s/sensor/%s/ch%d_%s/config", MQTT_DISCOVERY_PREFIX, iv->config->name, rec->assign[i].ch, iv->getFieldName(i, rec)); else // total values - snprintf(topic, 64, "%s/sensor/%s/total_%s/config", MQTT_DISCOVERY_PREFIX, iv->config->name, fields[fldTotal[i-rec->length]]); + snprintf(topic, 64, "%s/sensor/%s/total_%s/config", MQTT_DISCOVERY_PREFIX, node_id.c_str(),fields[fldTotal[i]]); size_t size = measureJson(doc2) + 1; char *buf = new char[size]; memset(buf, 0, size); From a37a9d4cc0d6f9544a6471b95f6833e82a3da685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20K?= Date: Sun, 29 Jan 2023 19:08:18 +0100 Subject: [PATCH 4/6] Ignore "exp_aft" for yieldtotal and yieldday. --- src/publisher/pubMqtt.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/publisher/pubMqtt.h b/src/publisher/pubMqtt.h index c0bcff5e..8788cb99 100644 --- a/src/publisher/pubMqtt.h +++ b/src/publisher/pubMqtt.h @@ -275,7 +275,8 @@ class PubMqtt { doc2[F("unit_of_meas")] = ((!total) ? (iv->getUnit(i,rec)) : (unitTotal[i])); doc2[F("uniq_id")] = ((!total) ? (String(iv->config->serial.u64, HEX)) : (node_id)) + "_" + uniq_id; doc2[F("dev")] = deviceObj; - doc2[F("exp_aft")] = MQTT_INTERVAL + 5; // add 5 sec if connection is bad or ESP too slow @TODO: stimmt das wirklich als expire!? + if (!(total && String(stateCls) == String("total_increasing"))) + doc2[F("exp_aft")] = MQTT_INTERVAL + 5; // add 5 sec if connection is bad or ESP too slow @TODO: stimmt das wirklich als expire!? if (devCls != NULL) doc2[F("dev_cla")] = String(devCls); if (stateCls != NULL) From 146c7612f698de4d0322ba77eb497cf79cd0de20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20K?= Date: Tue, 31 Jan 2023 06:35:37 +0100 Subject: [PATCH 5/6] Remove exp_aft for all YD & YT. Add reset YD total at midnight. Publish YD & YT on CH0 only when iv is producing to avoids 0 at restart. --- src/config/config.h | 2 +- src/publisher/pubMqtt.h | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/config/config.h b/src/config/config.h index 69e7193b..722e5323 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -99,7 +99,7 @@ #define NTP_REFRESH_INTERVAL 12 * 3600 * 1000 // default mqtt interval -#define MQTT_INTERVAL 60 +#define MQTT_INTERVAL 90 // default MQTT broker uri #define DEF_MQTT_BROKER "\0" diff --git a/src/publisher/pubMqtt.h b/src/publisher/pubMqtt.h index 8788cb99..ec3465d9 100644 --- a/src/publisher/pubMqtt.h +++ b/src/publisher/pubMqtt.h @@ -148,7 +148,10 @@ class PubMqtt { snprintf(val, 4, "0.0"); publish(topic, val, true); } - } + // set Total YieldDay to zero + snprintf(topic, 32 + MAX_NAME_LENGTH, "total/%s", fields[FLD_YD]); + snprintf(val, 4, "0.0"); + publish(topic, val, true); } void payloadEventListener(uint8_t cmd) { if(mClient.connected()) { // prevent overflow if MQTT broker is not reachable but set @@ -275,7 +278,7 @@ class PubMqtt { doc2[F("unit_of_meas")] = ((!total) ? (iv->getUnit(i,rec)) : (unitTotal[i])); doc2[F("uniq_id")] = ((!total) ? (String(iv->config->serial.u64, HEX)) : (node_id)) + "_" + uniq_id; doc2[F("dev")] = deviceObj; - if (!(total && String(stateCls) == String("total_increasing"))) + if (!(String(stateCls) == String("total_increasing"))) doc2[F("exp_aft")] = MQTT_INTERVAL + 5; // add 5 sec if connection is bad or ESP too slow @TODO: stimmt das wirklich als expire!? if (devCls != NULL) doc2[F("dev_cla")] = String(devCls); @@ -525,6 +528,8 @@ class PubMqtt { switch (rec->assign[i].fieldId) { case FLD_YT: case FLD_YD: + if ((rec->assign[i].ch == CH0) && (!iv->isProducing(*mUtcTimestamp))) // avoids returns to 0 on restart + continue; retained = true; break; } From a8b9ca0b4cfcde22e528f2897a730f45666efe53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20K?= Date: Tue, 31 Jan 2023 11:51:21 +0100 Subject: [PATCH 6/6] don't send "0" for CH0 YT YD at reboot --- src/publisher/pubMqtt.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/publisher/pubMqtt.h b/src/publisher/pubMqtt.h index ec3465d9..8b5b7dce 100644 --- a/src/publisher/pubMqtt.h +++ b/src/publisher/pubMqtt.h @@ -145,12 +145,12 @@ class PubMqtt { iv->setValue(pos, rec, 0.0f); snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/ch0/%s", iv->config->name, fields[FLD_YD]); - snprintf(val, 4, "0.0"); + snprintf(val, 2, "0"); publish(topic, val, true); } // set Total YieldDay to zero snprintf(topic, 32 + MAX_NAME_LENGTH, "total/%s", fields[FLD_YD]); - snprintf(val, 4, "0.0"); + snprintf(val, 2, "0"); publish(topic, val, true); } void payloadEventListener(uint8_t cmd) { @@ -484,7 +484,7 @@ class PubMqtt { if(changed) { snprintf(val, 32, "%d", ((allAvail) ? MQTT_STATUS_ONLINE : ((mIvAvail) ? MQTT_STATUS_PARTIAL : MQTT_STATUS_OFFLINE))); publish("status", val, true); - sendIvData(false); // false prevents loop of same function + //sendIvData(false); // false prevents loop of same function } return totalComplete;