Releases: SpanPanel/span-panel-api
Releases · SpanPanel/span-panel-api
v2.6.3
Fixed
- Feedthrough values now derived via Kirchhoff instead of read from
downstream-lugs—SpanPanelSnapshot.feedthrough_power_w,feedthrough_energy_consumed_wh, andfeedthrough_energy_produced_whare computed asmain − Σ(branches)inside
HomieDeviceConsumer._build_snapshot(and mirrored in the dirty-circuit rebuild path) rather than sourced from the nativeenergy.ebus.device.lugs.downstreamactive-power/imported-energy/exported-energyproperties. Kirchhoff at the main bus
produces a physically-consistent result:P_main = P_feedthrough + Σ(branches, load-perspective)for instantaneous power, and the same identity applied to net energy (main.consumed − main.produced − Σ(branch.net)) split into non-negative consumed /
produced counters. Net-based energy handling is required because a per-direction subtraction would emit negative cumulative counters whenever circuits flow bidirectionally (the classic case is PV self-consumption:Σ(branch.consumed)can exceed
main.consumedeven when the net balance is valid). The synthesized PV virtual circuit participates with the correct load-perspective sign, and unmapped tab entries are zero-power, so both contribute safely to the sum. No public interface change — field
names and types are unchanged; only the source of the values shifts.downstream_l1_current_a/downstream_l2_current_acontinue to be read directly from the downstream-lugs node — those per-phase readings are orthogonal to the defect. The underlying
firmware defect is tracked upstream at spanio/SPAN-API-Client-Docs#13.
v2.6.2
Changed
- Reconnect loop log noise reduced —
SpanMqttClient._reconnect_loopnow splits the catch-all exception handler in two: expected transient failures (OSErrorfamily — refused connection, DNS miss, socket timeout,ssl.SSLError) log a one-line
WARNING with the exception repr, while unexpected exceptions retain the full traceback viaexc_info=True. The common "panel offline" case no longer buries logs in paho/stdlib stack frames that add no diagnostic signal; genuinely unknown failures still
surface full tracebacks for support-ticket triage.
v2.6.1
Changed
get_fqdn()returnsstr | None—Nonenow distinguishes "no FQDN configured" (HTTP 404 or missing field) from an explicit empty string. Callers that treated""as "not registered" must update to check forNone.- Connection callback errors logged at WARNING —
SpanMqttClient._on_connection_changenow logs callback exceptions via_LOGGER.warning(..., exc_info=True)instead of_LOGGER.exception(...), consistent with_dispatch_snapshot. - Reconnect loop catches all exceptions —
AsyncMqttBridge._reconnect_loopno longer silently drops on non-OSErrorfailures (e.g.WebsocketConnectionError,ssl.SSLError). All exceptions are logged at WARNING and the loop keeps backing off. - Abnormal MQTT disconnects logged at WARNING — disconnects where
reason_code.is_failureis true now log at WARNING; clean disconnects continue to log at DEBUG.
Fixed
- CA certificate no longer written to disk —
AsyncMqttBridge.connect()builds thessl.SSLContextfrom the fetched PEM viacadata, eliminating the temp-file lifecycle (and the small leak window on unexpected process exit) that the prior
tls_set(ca_certs=path)path required. - Deprecated
asyncio.get_event_loop()removed —_wait_for_circuit_namesnow usestime.monotonic(). The previous code emitted aDeprecationWarningon Python 3.12+. - Negative-zero on circuit
instant_power_w— explicit guard replaces a cryptic-raw or 0.0idiom inHomieDeviceConsumer._build_circuit. - DSM grid-exchanging heuristic uses epsilon — replaces
!= 0.0float comparison withabs(x) > 1.0 W, so theDSM_OFF_GRIDbranch is actually reachable when no BESS is commissioned and lugs readings hover near zero. SpanPanelAPIError.__str__override removed — the override silently hid exception args beyond the first; defaultException.__str__is now used.- Paho lock-layout check at import —
span_panel_api.mqtt.async_clientverifies on import that the_PAHO_LOCK_ATTRSlist exactly matches paho's*_mutexattributes. RaisesRuntimeError(notassert, sopython -Odoes not bypass it) on drift.
v2.6.0
Added
SpanMqttClient.register_connection_callback(cb)— subscribe to broker connection state transitions. Callback fires withFalseon broker disconnect andTrueon reconnect; returns an idempotent unregister function. Added to
SpanPanelClientProtocolso any transport that claims the protocol must implement it.SpanPanelStaleDataErrorexception — raised byget_snapshot()when the client is not fully live. Derives fromSpanPanelError(not fromSpanPanelConnectionError), because "never connected" and "running but data not currently live" are
semantically distinct states.
Changed
get_snapshot()contract — now raisesSpanPanelStaleDataErrorwhen the bridge is not connected or the Homie device has not reached ready state. Previously, the method silently returned a snapshot built from whatever the in-memory accumulator
happened to hold, which made offline panels indistinguishable from online ones. This is the primary reason the span integration could not detect panel-offline transitions.
Fixed
- Stale snapshot dispatch after bridge disconnect — a pending snapshot-debounce timer scheduled just before a bridge disconnect could fire afterwards, delivering a snapshot built from the still-
ready()accumulator to subscribers.
_on_connection_change(False)now cancels the pending timer, and_dispatch_snapshotis now guarded by the same liveness predicate asget_snapshot(), so push consumers never receive a post-disconnect stale snapshot.
Breaking
- Consumers of
get_snapshot()must now handleSpanPanelStaleDataError. Any consumer with a broadexcept Exception(orexcept SpanPanelError) branch already handles this correctly.
v2.5.4
What's Changed
- revert: restore accumulator and consumer to stable 2.5.1 behavior by @cayossarian in #133
Full Changelog: v2.5.3...v2.5.4
v2.5.3
What's Changed
- chore(deps): bump cryptography from 46.0.6 to 46.0.7 by @dependabot[bot] in #131
Fixed
- Preserve property values on lifecycle reset — the 2.5.2 property clear caused
_parse_float('')to return0.0for energy counters during panel reboots or network interruptions, triggering false dip-compensation offsets in the integration that
permanently inflated energy sensor values. Removed the property/timestamp/target clearing from_handle_description(). Pre-reboot values now serve as safe placeholders until the panel re-publishes fresh data. - Snapshot cache invalidated on reboot — the generation counter still increments on lifecycle resets, forcing consumers to discard cached snapshots and rebuild from current accumulator state.
Full Changelog: v2.5.2...v2.5.3
v2.5.2
What's Changed
- fix: clear stale property values on panel reboot to prevent mixed-gen snapshots by @cayossarian in #130
Full Changelog: v2.5.1...v2.5.2
v2.5.1
What's Changed
- fix: replace assert in production code, fix broken bandit hook, bump … by @cayossarian in #129
Full Changelog: v2.5.0...v2.5.1
v2.5.0
Added
HomiePropertyAccumulator— new layer that handles generic Homie v5 protocol parsing (message routing, property/target storage, dirty-node tracking) with an explicit lifecycle state machine (HomieLifecycle), cleanly separated from SPAN-specific
snapshot construction.$targetproperty support —SpanCircuitSnapshotgainsrelay_state_targetandpriority_targetfields, surfacing the desired-vs-actual state for relay and shed-priority commands.- Dirty-node snapshot caching —
HomieDeviceConsumer.build_snapshot()tracks which nodes changed since the last build and returns a cached snapshot when nothing is dirty, reducing per-scan CPU cost on constrained hardware.
Changed
- Layered Homie consumer architecture —
HomieDeviceConsumerno longer handles protocol plumbing. It reads fromHomiePropertyAccumulatorvia a query API (get_prop,get_target,nodes_by_type, etc.) and focuses solely on SPAN domain
interpretation: power sign normalization, DSM derivation, unmapped tab synthesis, and snapshot assembly. SpanMqttClientcomposes both layers —connect()creates an accumulator and wires it into the consumer. The public client API is unchanged.- Property callbacks fire only on value change — retained messages replaying already-known values no longer trigger callback storms on MQTT reconnect.
v2.4.2
What's Changed
- chore(deps): bump astral-sh/setup-uv from 5 to 7 by @dependabot[bot] in #121
- chore(deps): bump dependabot/fetch-metadata from 2.5.0 to 3.0.0 by @dependabot[bot] in #126
- chore(deps): bump pygments from 2.19.2 to 2.20.0 by @dependabot[bot] in #125
- chore(deps): bump cryptography from 46.0.5 to 46.0.6 by @dependabot[bot] in #124
- chore(deps): bump requests from 2.32.5 to 2.33.0 by @dependabot[bot] in #123
- Fix blocking load_verify_locations call in event loop by @cayossarian in #127
Full Changelog: v2.4.1...v2.4.2