From 107182f9483fdda31a91644a2d64a06e439785b0 Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Tue, 4 Jul 2023 12:05:10 +0200 Subject: [PATCH] DPL: check more requirements and fix backoff initialization (#290) * DPL: wait for valid time information we know that the Hoymiles library refuses to send any message to any inverter until the system has valid time information. until then we can do nothing, not even shutdown the inverter. * DPL: wait for device info to be ready a calculated power limit will always be limited to the reported device's max power. that upper limit is only known after the first DevInfoSimpleCommand succeeded. wait for that information to be available. * DPL: fix initial calculcation backoff if the calculation backoff is initialized to zero, the backoff will be doubled to zero until a new, different power limit was calculated for the first time. this lead to the DPL recalculating a power limit hundreds of times without a backoff after startup. --- include/PowerLimiter.h | 5 ++++- src/PowerLimiter.cpp | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/include/PowerLimiter.h b/include/PowerLimiter.h index 3a59d73c..f448dc20 100644 --- a/include/PowerLimiter.h +++ b/include/PowerLimiter.h @@ -28,6 +28,7 @@ public: Initializing, DisabledByConfig, DisabledByMqtt, + WaitingForValidTimestamp, PowerMeterDisabled, PowerMeterTimeout, PowerMeterPending, @@ -37,6 +38,7 @@ public: InverterCommandsDisabled, InverterLimitPending, InverterPowerCmdPending, + InverterDevInfoPending, InverterStatsPending, UnconditionalSolarPassthrough, NoVeDirect, @@ -59,7 +61,8 @@ private: Status _lastStatus = Status::Initializing; uint32_t _lastStatusPrinted = 0; uint32_t _lastCalculation = 0; - uint32_t _calculationBackoffMs = 0; + static constexpr uint32_t _calculationBackoffMsDefault = 128; + uint32_t _calculationBackoffMs = _calculationBackoffMsDefault; uint8_t _mode = PL_MODE_ENABLE_NORMAL_OP; std::shared_ptr _inverter = nullptr; bool _batteryDischargeEnabled = false; diff --git a/src/PowerLimiter.cpp b/src/PowerLimiter.cpp index 4e4a9cfa..1b3546f1 100644 --- a/src/PowerLimiter.cpp +++ b/src/PowerLimiter.cpp @@ -30,6 +30,7 @@ std::string const& PowerLimiterClass::getStatusText(PowerLimiterClass::Status st { Status::Initializing, "initializing (should not see me)" }, { Status::DisabledByConfig, "disabled by configuration" }, { Status::DisabledByMqtt, "disabled by MQTT" }, + { Status::WaitingForValidTimestamp, "waiting for valid date and time to be available" }, { Status::PowerMeterDisabled, "no power meter is configured/enabled" }, { Status::PowerMeterTimeout, "power meter readings are outdated" }, { Status::PowerMeterPending, "waiting for sufficiently recent power meter reading" }, @@ -39,6 +40,7 @@ std::string const& PowerLimiterClass::getStatusText(PowerLimiterClass::Status st { Status::InverterCommandsDisabled, "inverter configuration prohibits sending commands" }, { Status::InverterLimitPending, "waiting for a power limit command to complete" }, { Status::InverterPowerCmdPending, "waiting for a start/stop/restart command to complete" }, + { Status::InverterDevInfoPending, "waiting for inverter device information to be available" }, { Status::InverterStatsPending, "waiting for sufficiently recent inverter data" }, { Status::UnconditionalSolarPassthrough, "unconditionally passing through all solar power (MQTT override)" }, { Status::NoVeDirect, "VE.Direct disabled, connection broken, or data outdated" }, @@ -97,6 +99,14 @@ void PowerLimiterClass::loop() { CONFIG_T& config = Configuration.get(); + // we know that the Hoymiles library refuses to send any message to any + // inverter until the system has valid time information. until then we can + // do nothing, not even shutdown the inverter. + struct tm timeinfo; + if (!getLocalTime(&timeinfo, 5)) { + return announceStatus(Status::WaitingForValidTimestamp); + } + if (_shutdownInProgress) { // we transition from SHUTDOWN to OFF when we know the inverter was // shut down. until then, we retry shutting it down. in this case we @@ -152,6 +162,13 @@ void PowerLimiterClass::loop() return announceStatus(Status::InverterPowerCmdPending); } + // a calculated power limit will always be limited to the reported + // device's max power. that upper limit is only known after the first + // DevInfoSimpleCommand succeeded. + if (_inverter->DevInfo()->getMaxPower() <= 0) { + return announceStatus(Status::InverterDevInfoPending); + } + if (PL_MODE_SOLAR_PT_ONLY == _mode) { // handle this mode of operation separately return unconditionalSolarPassthrough(_inverter); @@ -275,7 +292,7 @@ void PowerLimiterClass::loop() return announceStatus(Status::Stable); } - _calculationBackoffMs = 128; + _calculationBackoffMs = _calculationBackoffMsDefault; } /**