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 topvPower, so the control algorithm sees true total generation when a second inverter's output is reported in a separate variable rather than the FoxESSpvPowerreading. 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 intopvPowerimproves 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 waslast_power_w < max_power_w, also satisfied atlast_power_w == 0— but the discharge listener parks the inverter in self-use mid-session (suspend path and feed-in self-use path) withlast_power_w = 0whiledischarging_started=Trueand 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 requires0 < last_power_w < max_power_win the auto "paced" branch andlast_power_w > 0in thesmart_sessionsdischarge fall-through. Regression classtests/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_wswas start-only — when the gate returned False with a WS already running it returned early without disconnecting (_stop_realtime_wswas 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_wsnow reconciles the connection to the gate — when the gate is False and a WS is running it tears it down. Regression classtests/test_init.py::TestWsTornDownWhenGateFlipsFalse. - WS stayed connected for hours after a CHARGE session ended — no window-end guard.
_should_start_realtime_wsguarded the scheduled phase (now < start→ block) but had no symmetricnow >= endguard. The end-of-window teardown is async (override removal + 30 s WS linger before the session state clears), so during that interval the lingeringcharging_started=True/discharging_started=Truestill 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 oncenow >= endfor both the charge and discharge branches. Regression classtests/test_init.py::TestWsGateClosesAtWindowEnd. - The WS reconnect loop self-perpetuated a connect → stale → reconnect cycle during idle.
FoxESSRealtimeWS._try_reconnectwas fully autonomous — it gated only on instance-local_no_reconnect/_stop_eventflags 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 frametimeDiff=61and discarded per C-005 — pure churn). Fix:FoxESSRealtimeWSnow takes ashould_reconnectpredicate wired to the same_should_start_realtime_ws(hass)gate, and_try_reconnectconsults 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);alwaysmode keeps unconditional reconnect. Regression classtests/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 fromPOLLED_VARIABLES, with a note distinguishingdischargeEnergyToTal(battery throughput — serves house load and export) fromfeedin(grid export only). The smart-discharge feed-in energy limit /feedin_target_kwhis measured fromfeedin, not battery discharge.