From 784e3694828df6adc533d5e14b9661968257bbd4 Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Tue, 5 Mar 2024 12:34:09 +0100 Subject: [PATCH] optimize DPL thresholds MQTT integration * fix logic in HomeAssistent handler * also publish voltage thresholds (not just SoC thresholds) * do not publish irrelevant thresholds to MQTT. if the inverter is solar-powered, no thresholds are effectively in use by the DPL and it therefore makes no sense to publish them to the broker. similarly, if no battery interface is enabled or the SoC values are set to be ignored, the SoC thresholds are effectively not in use and will not be published to the broker. * make HA auto-discovery expire. this makes auto-dicovered items disappear from Home Assistent if their value is no longer updated. changes to settings which cause other thresholds to be relevant will then be reflected in Home Assistent even if some thresholds are no longer maintaned in MQTT. * force HA update when related settings change enabling VE.Direct shall trigger an update since solar passthrough thresholds become relevant. similarly, enabling the battery interface makes SoC thresholds become relevant. there are more settings in the power limiter that also influence the auto-discoverable items. * break very long lines --- src/MqttHandlePowerLimiter.cpp | 33 ++++++++++++++----- src/MqttHandlePowerLimiterHass.cpp | 53 ++++++++++++++++++++++++++---- src/WebApi_battery.cpp | 4 +++ src/WebApi_powerlimiter.cpp | 8 ++--- src/WebApi_vedirect.cpp | 4 +++ 5 files changed, 82 insertions(+), 20 deletions(-) diff --git a/src/MqttHandlePowerLimiter.cpp b/src/MqttHandlePowerLimiter.cpp index 065f65105..411fa3f1a 100644 --- a/src/MqttHandlePowerLimiter.cpp +++ b/src/MqttHandlePowerLimiter.cpp @@ -67,18 +67,33 @@ void MqttHandlePowerLimiterClass::loop() if (!MqttSettings.getConnected() ) { return; } - if ((millis() - _lastPublish) > (config.Mqtt.PublishInterval * 1000) ) { - auto val = static_cast(PowerLimiter.getMode()); - MqttSettings.publish("powerlimiter/status/mode", String(val)); - MqttSettings.publish("powerlimiter/status/threshold/soc/start", String(config.PowerLimiter.BatterySocStartThreshold)); - MqttSettings.publish("powerlimiter/status/threshold/soc/stop", String(config.PowerLimiter.BatterySocStopThreshold)); - MqttSettings.publish("powerlimiter/status/threshold/soc/full_solar_passthrough", String(config.PowerLimiter.FullSolarPassThroughSoc)); - MqttSettings.publish("powerlimiter/status/threshold/voltage/start", String(config.PowerLimiter.VoltageStartThreshold)); - MqttSettings.publish("powerlimiter/status/threshold/voltage/stop", String(config.PowerLimiter.VoltageStopThreshold)); + if ((millis() - _lastPublish) < (config.Mqtt.PublishInterval * 1000)) { + return; + } + + _lastPublish = millis(); + + auto val = static_cast(PowerLimiter.getMode()); + MqttSettings.publish("powerlimiter/status/mode", String(val)); + + // no thresholds are relevant for setups without a battery + if (config.PowerLimiter.IsInverterSolarPowered) { return; } + + MqttSettings.publish("powerlimiter/status/threshold/voltage/start", String(config.PowerLimiter.VoltageStartThreshold)); + MqttSettings.publish("powerlimiter/status/threshold/voltage/stop", String(config.PowerLimiter.VoltageStopThreshold)); + + if (config.Vedirect.Enabled) { MqttSettings.publish("powerlimiter/status/threshold/voltage/full_solar_passthrough_start", String(config.PowerLimiter.FullSolarPassThroughStartVoltage)); MqttSettings.publish("powerlimiter/status/threshold/voltage/full_solar_passthrough_stop", String(config.PowerLimiter.FullSolarPassThroughStopVoltage)); + } - _lastPublish = millis(); + if (!config.Battery.Enabled || config.PowerLimiter.IgnoreSoc) { return; } + + MqttSettings.publish("powerlimiter/status/threshold/soc/start", String(config.PowerLimiter.BatterySocStartThreshold)); + MqttSettings.publish("powerlimiter/status/threshold/soc/stop", String(config.PowerLimiter.BatterySocStopThreshold)); + + if (config.Vedirect.Enabled) { + MqttSettings.publish("powerlimiter/status/threshold/soc/full_solar_passthrough", String(config.PowerLimiter.FullSolarPassThroughSoc)); } } diff --git a/src/MqttHandlePowerLimiterHass.cpp b/src/MqttHandlePowerLimiterHass.cpp index 17b9ad7cb..47edfbae6 100644 --- a/src/MqttHandlePowerLimiterHass.cpp +++ b/src/MqttHandlePowerLimiterHass.cpp @@ -46,7 +46,9 @@ void MqttHandlePowerLimiterHassClass::forceUpdate() void MqttHandlePowerLimiterHassClass::publishConfig() { - if (!Configuration.get().Mqtt.Hass.Enabled) { + auto const& config = Configuration.get(); + + if (!config.Mqtt.Hass.Enabled) { return; } @@ -54,13 +56,45 @@ void MqttHandlePowerLimiterHassClass::publishConfig() return; } - if (!Configuration.get().PowerLimiter.Enabled) { - publishSelect("DPL Mode", "mdi:gauge", "config", "mode", "mode"); - publishNumber("DPL battery SoC start threshold", "mdi:battery-charging", "config", "threshold/soc/start", "threshold/soc/start", "%", 0, 100); - publishNumber("DPL battery SoC stop threshold", "mdi:battery-charging", "config", "threshold/soc/stop", "threshold/soc/stop", "%", 0, 100); + if (!config.PowerLimiter.Enabled) { + return; + } + + publishSelect("DPL Mode", "mdi:gauge", "config", "mode", "mode"); + + if (config.PowerLimiter.IsInverterSolarPowered) { + return; + } + + // as this project revolves around Hoymiles inverters, 16 - 60 V is a reasonable voltage range + publishNumber("DPL battery voltage start threshold", "mdi:battery-charging", + "config", "threshold/voltage/start", "threshold/voltage/start", "V", 16, 60); + publishNumber("DPL battery voltage stop threshold", "mdi:battery-charging", + "config", "threshold/voltage/stop", "threshold/voltage/stop", "V", 16, 60); + + if (config.Vedirect.Enabled) { + publishNumber("DPL full solar passthrough start voltage", + "mdi:transmission-tower-import", "config", + "threshold/voltage/full_solar_passthrough_start", + "threshold/voltage/full_solar_passthrough_start", "V", 16, 60); + publishNumber("DPL full solar passthrough stop voltage", + "mdi:transmission-tower-import", "config", + "threshold/voltage/full_solar_passthrough_stop", + "threshold/voltage/full_solar_passthrough_stop", "V", 16, 60); } - if (!Configuration.get().Vedirect.Enabled) { - publishNumber("DPL full solar passthrough SoC", "mdi:transmission-tower-import", "config", "threshold/soc/full_solar_passthrough", "threshold/soc/full_solar_passthrough", "%", 0, 100); + + if (config.Battery.Enabled && !config.PowerLimiter.IgnoreSoc) { + publishNumber("DPL battery SoC start threshold", "mdi:battery-charging", + "config", "threshold/soc/start", "threshold/soc/start", "%", 0, 100); + publishNumber("DPL battery SoC stop threshold", "mdi:battery-charging", + "config", "threshold/soc/stop", "threshold/soc/stop", "%", 0, 100); + + if (config.Vedirect.Enabled) { + publishNumber("DPL full solar passthrough SoC", + "mdi:transmission-tower-import", "config", + "threshold/soc/full_solar_passthrough", + "threshold/soc/full_solar_passthrough", "%", 0, 100); + } } } @@ -137,6 +171,11 @@ void MqttHandlePowerLimiterHassClass::publishNumber( root["max"] = max; root["mode"] = "box"; + auto const& config = Configuration.get(); + if (config.Mqtt.Hass.Expire) { + root["exp_aft"] = config.Mqtt.PublishInterval * 3; + } + JsonObject deviceObj = root.createNestedObject("dev"); createDeviceInfo(deviceObj); diff --git a/src/WebApi_battery.cpp b/src/WebApi_battery.cpp index 9e2230c4e..3f26d83cc 100644 --- a/src/WebApi_battery.cpp +++ b/src/WebApi_battery.cpp @@ -8,6 +8,7 @@ #include "Battery.h" #include "Configuration.h" #include "MqttHandleBatteryHass.h" +#include "MqttHandlePowerLimiterHass.h" #include "WebApi.h" #include "WebApi_battery.h" #include "WebApi_errors.h" @@ -114,4 +115,7 @@ void WebApiBatteryClass::onAdminPost(AsyncWebServerRequest* request) Battery.updateSettings(); MqttHandleBatteryHass.forceUpdate(); + + // potentially make SoC thresholds auto-discoverable + MqttHandlePowerLimiterHass.forceUpdate(); } diff --git a/src/WebApi_powerlimiter.cpp b/src/WebApi_powerlimiter.cpp index 3a7d827a2..444c134f2 100644 --- a/src/WebApi_powerlimiter.cpp +++ b/src/WebApi_powerlimiter.cpp @@ -7,10 +7,7 @@ #include "ArduinoJson.h" #include "AsyncJson.h" #include "Configuration.h" -#include "MqttHandleHass.h" -#include "MqttHandleVedirectHass.h" -#include "MqttSettings.h" -#include "PowerMeter.h" +#include "MqttHandlePowerLimiterHass.h" #include "PowerLimiter.h" #include "WebApi.h" #include "helper.h" @@ -155,4 +152,7 @@ void WebApiPowerLimiterClass::onAdminPost(AsyncWebServerRequest* request) request->send(response); PowerLimiter.calcNextInverterRestart(); + + // potentially make thresholds auto-discoverable + MqttHandlePowerLimiterHass.forceUpdate(); } diff --git a/src/WebApi_vedirect.cpp b/src/WebApi_vedirect.cpp index 088be259f..4e1e352b2 100644 --- a/src/WebApi_vedirect.cpp +++ b/src/WebApi_vedirect.cpp @@ -10,6 +10,7 @@ #include "WebApi.h" #include "WebApi_errors.h" #include "helper.h" +#include "MqttHandlePowerLimiterHass.h" void WebApiVedirectClass::init(AsyncWebServer& server, Scheduler& scheduler) { @@ -118,4 +119,7 @@ void WebApiVedirectClass::onVedirectAdminPost(AsyncWebServerRequest* request) request->send(response); VictronMppt.updateSettings(); + + // potentially make solar passthrough thresholds auto-discoverable + MqttHandlePowerLimiterHass.forceUpdate(); }