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 <martin.dummer@gmx.net>
* 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.
* VE.Direct: remove polling interval
the polling interval was meant to limit the amount of MQTT updates.
however, that is already controlled by the global MQTT publish interval.
the removed interval was instead used to limit polling of the VE.Direct
UART for incoming data.
the Victron device sends data unsolicited. the VeDirectFrameHandler does
not implement any polling mechanism. no data is ever sent to the Victron
device.
what the removed polling interval did was cause a buffer overrun of the
HardwareSerial class, since the incoming data was not processed in time.
so every five seconds, we read a whole valid VE.Direct frame, plus some
old data, which was not a whole frame, leading to VE.Direct error
messages to pop up.
with the polling interval removed, no framing errors are reported, and
instead we gain new data from the charge controller approximately ever
two seconds -- for free.
* VE.Direct: change texts to correct VE.Direct capital letters
* VE.Direct: improve "UpdatesOnly" switch labels
especially since the publish interval setting is gone, the label makes
it hard to comprehend what the switch does. update the texts to better
explain what the switch is used for.
use the same text on the VE.Direct info view.
* VE.Direct: use StatusBadge on info view
there were custom badges to indicate the VE.Direct settings. replace
those by the common StatusBadge to make then look the same as the other
badged on the info views.
the requested newPowerLimit was indeed limited to the configured maximum
inverter output and the result was stored in effPowerLimit. later,
however, effPowerLimit must be scaled up (if necessary), not
newPowerLimit. the latter variable is not respecting the configured
maximum inverter output.
* DPL: use VeDirect.isDataValid()
in case the communication to the Victron charger is disrupted, the
respective values in VeDirect.veFrame are not invalidated such that we
would notice a problem. instead, isDataValid() should be used to make
sure that the Victron charger is actually alive and that we can trust to
use the reported values.
* DPL: simplify canUseDirectSolarPower return statement
a new status is needed to communicate that no update was sent to the
inverter because its power limit is still valid. in this case,
calculating a new power limit is delayed by an exponentially increasing
backoff. the maximum backoff time is ~1s, which is still plenty fast.
the backoff is actually necessary for another reason: at least
currently, a lot of debug messages are printed to the console. printing
all that information in every DPL loop() is too much.
the unconditional solar passthrough mode, configured using MQTT, works
differently than the normal mode of operation. it is also independent
from the power meter reading. if this mode is active, a shortcut is
taken to a function that implements the actions for this mode. this is
convenient since we don't have to consider special cases in the code
that handles normal mode of operation.
the DPL already took care to shut down the inverter if anything fishy
was going on, mainly to make sure that the battery is not drained.
however, some cases were missed:
* if the configuration changed such that another inverter is now
targeted, the one the DPL controlled previously was not shut down.
* if the configuration changed such that another inverter (different
serial number) was configured at the same index, the previous one
was not shut down.
this change corrects these problems by making the DPL keep a copy of the
shared_ptr to the inverter. the shared_ptr is only released once the DPL
shut the respective inverter down.
this implementation checks all requirements for a new power limit to be
calculated, one after the other. if any requirement is not met, a
respective status is announced.
status messages are communicated on the (serial) console. these can also
be displayed easily on the web app in the future. the status texts
explain clearly what the DPL is currently doing, which aids
understanding how the DPL works. the status is only announced if it
changes, or after a fixed interval.
as each requirement is checked individually, the code readability is
improved as well. previously, all the respective conditions had to be
checked as well, but the statements were more complex.
the DPL loop is now executed with high frequency, i.e., it does not wait
for a fixed timespan to pass before checking requirements. it always
aborts on the first unmet requirement. this should improve responsiveness,
as the DPL checks all requirements more often.
the DPL now waits for all power commands and power limit updates to
complete. when that is the case, a settling time elapses. after the
settling phase, the DPL waits for a new update from the inverter and
from the power meter. now it can be assumed that the values are in sync.
it then makes sense to calculate a new power limit immediately, which
the DPL then does.
the defaults for solar passthrough voltage thresholds shall be floats,
so the user can store float values to the config. otherwise, float
values can and will be stored, but when reading them, the defaults will
be applied as the defaults are of an incompatible type.
* add calculated values to VE.Direct data
solar current, battery output power, and the charger's efficiency can be
calculated from the values reported by the charger. the efficiency must
be taken with a grain of salt. it seems that the solar power value and
the battery output voltage/current are not always in sync. for that
reason a moving average is used to smooth out the calculated efficiency
value.
* show calculated VE.Direct values in web live view
order the values and translations similarly for the input and output,
starting with power at the top, then voltage, then current as the last
of these three.
* VE.Direct live view: use 'd' as unit for days
'd' is the SI unit symbol for days and does not need translation, which
is desirable as units are not translated throughout the project.
* refactor VE.Direct live view
* move Dynamic Power Limiter data into its own type.
* split VE.Direct data into three types: "device", "input", and
"output". hence all input and output values are now ValueObject, which
allows to iterate over them using a loop without typing issues.
* generate the tables with input and output values using a loop, rather
than defining each row individually.
* localize numbers using $n (vue method), which fixes switching the
number format (dot vs. comma) when switching the language.
* use no decimal point for power values (they are integers), three
decimal points for kWh values (charger only reports two decimal
places, but three are easier to read since the unit is *kilo* Wh), one
decimal point for the efficiency, and two for voltage and current.
* update language tokens to avoid mapping JSON keys to language keys
(use the JSON keys to access the language tokens).
* re-structure language tokes so the brief keys took over from
VeDirectFrameHandler always make sense (nest into "input" and
"output").
* order values similarly from top to bottom: power, then voltage, then
current. this is following the order of the inverters' details.
* group values by type/unit (yield and max. power) and order them
"newest" to "oldest" from top to bottom.
* increase the DynamicJsonDocument as it was too small to hold the newly
added data.
* update webapp_dist to include VE.Direct live view refactoring