Skip to content

v1.0.21

Latest

Choose a tag to compare

@github-actions github-actions released this 12 Jun 03:52
bd83aef

Consolidates the 1.0.21-beta.1 … beta.4 pre-releases. Headline: a series of
four distinct WebSocket idle-leaks is closed (the realtime WS now stays down
whenever no session needs it), AC-coupled installs can name a second solar
source, and the API reference documents the cumulative energy counters.

Added

  • Additional solar source for AC-coupled installs (cloud mode). New optional config additional_pv_power_variable (e.g. meterPower2) names a FoxESS telemetry variable whose value is added to pvPower, so the control algorithm sees true total generation when a second inverter's output is reported in a separate variable rather than the FoxESS pvPower reading. REST-polled and held across WebSocket frames; raw additive; off by default (blank). A persistently-missing configured variable is surfaced as a diagnostics error (C-020/C-026). As a side benefit, feeding the external term into pvPower improves the WS grid-direction inference that previously diverged on unmeasured external generation.

Fixed

WebSocket idle-leaks — the realtime WebSocket kept streaming ~5 s frames (and
the data_freshness badge sawtoothed ws → api → ws) while no session needed
it. Four distinct leaks were found and closed in sequence, each confirmed on
the prior beta with all earlier fixes present; together they make "the live WS
state matches _should_start_realtime_ws" a single reconciliation contract
honoured by every path (C-020, C-025, D-008, D-009):

  • Discharge self-use lulls held the WS open. _should_start_realtime_ws's "paced forced discharge" test was last_power_w < max_power_w, also satisfied at last_power_w == 0 — but the discharge listener parks the inverter in self-use mid-session (suspend path and feed-in self-use path) with last_power_w = 0 while discharging_started=True and the window open. With no ForceDischarge override there is no P-001 import window to monitor, so the WS had nothing to pace yet the gate kept it up (live 2026-06-02: 60 frames over 182 s during confirmed idle; 278 badge transitions in ~12 h). Fix: the gate now requires 0 < last_power_w < max_power_w in the auto "paced" branch and last_power_w > 0 in the smart_sessions discharge fall-through. Regression class tests/test_init.py::TestWsGateSelfUseLull.
  • A running WS was never torn down when the gate flipped False. Correcting the gate's return value was not enough: _maybe_start_realtime_ws was start-only — when the gate returned False with a WS already running it returned early without disconnecting (_stop_realtime_ws was wired solely to the session-cancel/end hook). A connection opened during genuine paced discharge kept streaming through every later self-use lull and after the window ended (live: ~5 s frames for 2h46m after the last session fully ended). Fix: _maybe_start_realtime_ws now reconciles the connection to the gate — when the gate is False and a WS is running it tears it down. Regression class tests/test_init.py::TestWsTornDownWhenGateFlipsFalse.
  • WS stayed connected for hours after a CHARGE session ended — no window-end guard. _should_start_realtime_ws guarded the scheduled phase (now < start → block) but had no symmetric now >= end guard. The end-of-window teardown is async (override removal + 30 s WS linger before the session state clears), so during that interval the lingering charging_started=True/discharging_started=True still satisfied the gate and the WS-aware listener interval re-armed the connection the teardown was concurrently stopping (live 2026-06-05: charge 11:00–13:59 Melbourne, then 44 ws↔api sawtooths across the ~3h50m idle gap). Fix: the gate now blocks WS once now >= end for both the charge and discharge branches. Regression class tests/test_init.py::TestWsGateClosesAtWindowEnd.
  • The WS reconnect loop self-perpetuated a connect → stale → reconnect cycle during idle. FoxESSRealtimeWS._try_reconnect was fully autonomous — it gated only on instance-local _no_reconnect / _stop_event flags and never consulted _should_start_realtime_ws. All three prior fixes live in the start chokepoint, which the reconnect loop never calls, so the listen loop's 30 s stale-timeout (C-005) kept re-establishing the connection ~6 s later, below the start-gate's visibility (live 2026-06-07: a connect → stale(30 s) → reconnect(6 s) cycle every ~5.5 min, every frame timeDiff=61 and discarded per C-005 — pure churn). Fix: FoxESSRealtimeWS now takes a should_reconnect predicate wired to the same _should_start_realtime_ws(hass) gate, and _try_reconnect consults it before scheduling any reconnect I/O. The legitimate case is preserved: during an active session the gate returns True, so a stale/dropped WS still reconnects (D-009); always mode keeps unconditional reconnect. Regression class tests/test_realtime_ws.py::TestReconnectRespectsGate.

Documentation

  • API reference now documents the cumulative energy counters for device/real/query. The section previously listed only "common" power variables, so an implementer working from the docs could conclude battery discharge energy "isn't available via the API" — it is. Added the full energy-counter family (dischargeEnergyToTal, chargeEnergyToTal, feedin, gridConsumption, generation, loads, energyThroughput) sourced from POLLED_VARIABLES, with a note distinguishing dischargeEnergyToTal (battery throughput — serves house load and export) from feedin (grid export only). The smart-discharge feed-in energy limit / feedin_target_kwh is measured from feedin, not battery discharge.