From cdf5c855103827924fcaaa13a0dec4843ddea1a4 Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 9 Jul 2023 17:08:50 +0200 Subject: [PATCH] fix: Home Assistant MQTT-Auto-Discovery with VE.Direct (#297) In Home Assistant, when Home Assistant MQTT-Auto-Discovery is active, almost all Sensors of the auto-discovered Victron device in Home Assistant become "unavailable" after a short time - except those Sensors with frequent changes like battery voltage or panel voltage. This patch introduces regular mqtt updates for all VE.Direct sensors when MQTT-Auto-Discovery is enabled. Signed-off-by: Martin Dummer --- include/MqttHandleVedirect.h | 9 +++- src/MqttHandleVedirect.cpp | 87 +++++++++++++++++++++++++++--------- src/WebApi_mqtt.cpp | 2 + 3 files changed, 76 insertions(+), 22 deletions(-) diff --git a/include/MqttHandleVedirect.h b/include/MqttHandleVedirect.h index 9011b01b..e1abc90b 100644 --- a/include/MqttHandleVedirect.h +++ b/include/MqttHandleVedirect.h @@ -19,7 +19,14 @@ public: void loop(); private: veStruct _kvFrame{}; - uint32_t _lastPublish; + + // point of time in millis() when updated values will be published + uint32_t _nextPublishUpdatesOnly = 0; + + // point of time in millis() when all values will be published + uint32_t _nextPublishFull = 1; + + bool _PublishFull; }; extern MqttHandleVedirectClass MqttHandleVedirect; \ No newline at end of file diff --git a/src/MqttHandleVedirect.cpp b/src/MqttHandleVedirect.cpp index 66c1d1bf..d1f13259 100644 --- a/src/MqttHandleVedirect.cpp +++ b/src/MqttHandleVedirect.cpp @@ -12,8 +12,13 @@ MqttHandleVedirectClass MqttHandleVedirect; +// #define MQTTHANDLEVEDIRECT_DEBUG + void MqttHandleVedirectClass::init() { + // initially force a full publish + _nextPublishUpdatesOnly = 0; + _nextPublishFull = 1; } void MqttHandleVedirectClass::loop() @@ -28,71 +33,111 @@ void MqttHandleVedirectClass::loop() return; } - if (millis() - _lastPublish > (config.Mqtt_PublishInterval * 1000)) { + if ((millis() >= _nextPublishFull) || (millis() >= _nextPublishUpdatesOnly)) { + // determine if this cycle should publish full values or updates only + if (_nextPublishFull <= _nextPublishUpdatesOnly) { + _PublishFull = true; + } else { + _PublishFull = !config.Vedirect_UpdatesOnly; + } + + #ifdef MQTTHANDLEVEDIRECT_DEBUG + MessageOutput.printf("\r\n\r\nMqttHandleVedirectClass::loop millis %lu _nextPublishUpdatesOnly %u _nextPublishFull %u\r\n", millis(), _nextPublishUpdatesOnly, _nextPublishFull); + if (_PublishFull) { + MessageOutput.println("MqttHandleVedirectClass::loop publish full"); + } else { + MessageOutput.println("MqttHandleVedirectClass::loop publish updates only"); + } + #endif + String value; String topic = "victron/"; topic.concat(VeDirect.veFrame.SER); topic.concat("/"); - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.PID != _kvFrame.PID) + if (_PublishFull || VeDirect.veFrame.PID != _kvFrame.PID) MqttSettings.publish(topic + "PID", VeDirect.getPidAsString(VeDirect.veFrame.PID)); - if (!config.Vedirect_UpdatesOnly || strcmp(VeDirect.veFrame.SER, _kvFrame.SER) != 0) + if (_PublishFull || strcmp(VeDirect.veFrame.SER, _kvFrame.SER) != 0) MqttSettings.publish(topic + "SER", VeDirect.veFrame.SER ); - if (!config.Vedirect_UpdatesOnly || strcmp(VeDirect.veFrame.FW, _kvFrame.FW) != 0) + if (_PublishFull || strcmp(VeDirect.veFrame.FW, _kvFrame.FW) != 0) MqttSettings.publish(topic + "FW", VeDirect.veFrame.FW); - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.LOAD != _kvFrame.LOAD) + if (_PublishFull || VeDirect.veFrame.LOAD != _kvFrame.LOAD) MqttSettings.publish(topic + "LOAD", VeDirect.veFrame.LOAD == true ? "ON": "OFF"); - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.CS != _kvFrame.CS) + if (_PublishFull || VeDirect.veFrame.CS != _kvFrame.CS) MqttSettings.publish(topic + "CS", VeDirect.getCsAsString(VeDirect.veFrame.CS)); - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.ERR != _kvFrame.ERR) + if (_PublishFull || VeDirect.veFrame.ERR != _kvFrame.ERR) MqttSettings.publish(topic + "ERR", VeDirect.getErrAsString(VeDirect.veFrame.ERR)); - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.OR != _kvFrame.OR) + if (_PublishFull || VeDirect.veFrame.OR != _kvFrame.OR) MqttSettings.publish(topic + "OR", VeDirect.getOrAsString(VeDirect.veFrame.OR)); - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.MPPT != _kvFrame.MPPT) + if (_PublishFull || VeDirect.veFrame.MPPT != _kvFrame.MPPT) MqttSettings.publish(topic + "MPPT", VeDirect.getMpptAsString(VeDirect.veFrame.MPPT)); - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.HSDS != _kvFrame.HSDS) { + if (_PublishFull || VeDirect.veFrame.HSDS != _kvFrame.HSDS) { value = VeDirect.veFrame.HSDS; MqttSettings.publish(topic + "HSDS", value); } - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.V != _kvFrame.V) { + if (_PublishFull || VeDirect.veFrame.V != _kvFrame.V) { value = VeDirect.veFrame.V; MqttSettings.publish(topic + "V", value); } - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.I != _kvFrame.I) { + if (_PublishFull || VeDirect.veFrame.I != _kvFrame.I) { value = VeDirect.veFrame.I; MqttSettings.publish(topic + "I", value); } - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.VPV != _kvFrame.VPV) { + if (_PublishFull || VeDirect.veFrame.VPV != _kvFrame.VPV) { value = VeDirect.veFrame.VPV; MqttSettings.publish(topic + "VPV", value); } - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.PPV != _kvFrame.PPV) { + if (_PublishFull || VeDirect.veFrame.PPV != _kvFrame.PPV) { value = VeDirect.veFrame.PPV; MqttSettings.publish(topic + "PPV", value); } - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.H19 != _kvFrame.H19) { + if (_PublishFull || VeDirect.veFrame.H19 != _kvFrame.H19) { value = VeDirect.veFrame.H19; MqttSettings.publish(topic + "H19", value); } - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.H20 != _kvFrame.H20) { + if (_PublishFull || VeDirect.veFrame.H20 != _kvFrame.H20) { value = VeDirect.veFrame.H20; MqttSettings.publish(topic + "H20", value); } - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.H21 != _kvFrame.H21) { + if (_PublishFull || VeDirect.veFrame.H21 != _kvFrame.H21) { value = VeDirect.veFrame.H21; MqttSettings.publish(topic + "H21", value); } - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.H22 != _kvFrame.H22) { + if (_PublishFull || VeDirect.veFrame.H22 != _kvFrame.H22) { value = VeDirect.veFrame.H22; MqttSettings.publish(topic + "H22", value); } - if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.H23 != _kvFrame.H23) { + if (_PublishFull || VeDirect.veFrame.H23 != _kvFrame.H23) { value = VeDirect.veFrame.H23; MqttSettings.publish(topic + "H23", value); } - if (config.Vedirect_UpdatesOnly){ + if (!_PublishFull) { _kvFrame= VeDirect.veFrame; } - _lastPublish = millis(); + + // now calculate next points of time to publish + _nextPublishUpdatesOnly = millis() + (config.Mqtt_PublishInterval * 1000); + + if (_PublishFull) { + // when Home Assistant MQTT-Auto-Discovery is active, + // and "enable expiration" is active, all values must be published at + // least once before the announced expiry interval is reached + if ((config.Vedirect_UpdatesOnly) && (config.Mqtt_Hass_Enabled) && (config.Mqtt_Hass_Expire)) { + _nextPublishFull = millis() + (((config.Mqtt_PublishInterval * 3) - 1) * 1000); + + #ifdef MQTTHANDLEVEDIRECT_DEBUG + uint32_t _tmpNextFullSeconds = (config.Mqtt_PublishInterval * 3) - 1; + MessageOutput.printf("MqttHandleVedirectClass::loop _tmpNextFullSeconds %u - _nextPublishFull %u \r\n", _tmpNextFullSeconds, _nextPublishFull); + #endif + + } else { + // no future publish full needed + _nextPublishFull = UINT32_MAX; + } + } + + #ifdef MQTTHANDLEVEDIRECT_DEBUG + MessageOutput.printf("MqttHandleVedirectClass::loop _nextPublishUpdatesOnly %u _nextPublishFull %u\r\n", _nextPublishUpdatesOnly, _nextPublishFull); + #endif } } \ No newline at end of file diff --git a/src/WebApi_mqtt.cpp b/src/WebApi_mqtt.cpp index 4182391c..f9fd85d1 100644 --- a/src/WebApi_mqtt.cpp +++ b/src/WebApi_mqtt.cpp @@ -6,6 +6,7 @@ #include "Configuration.h" #include "MqttHandleHass.h" #include "MqttHandleVedirectHass.h" +#include "MqttHandleVedirect.h" #include "MqttSettings.h" #include "WebApi.h" #include "WebApi_errors.h" @@ -333,6 +334,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request) MqttSettings.performReconnect(); MqttHandleHass.forceUpdate(); MqttHandleVedirectHass.forceUpdate(); + MqttHandleVedirect.init(); PowerMeter.init(); PowerLimiter.init(); }