Skip to content

Releases: SpanPanel/span-panel-api

v2.6.3

20 Apr 20:00
22222c1

Choose a tag to compare

Fixed

  • Feedthrough values now derived via Kirchhoff instead of read from downstream-lugsSpanPanelSnapshot.feedthrough_power_w, feedthrough_energy_consumed_wh, and feedthrough_energy_produced_wh are computed as main − Σ(branches) inside
    HomieDeviceConsumer._build_snapshot (and mirrored in the dirty-circuit rebuild path) rather than sourced from the native energy.ebus.device.lugs.downstream active-power / imported-energy / exported-energy properties. 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.consumed even 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_a continue 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

17 Apr 04:34
99f174f

Choose a tag to compare

Changed

  • Reconnect loop log noise reducedSpanMqttClient._reconnect_loop now splits the catch-all exception handler in two: expected transient failures (OSError family — refused connection, DNS miss, socket timeout, ssl.SSLError) log a one-line
    WARNING with the exception repr, while unexpected exceptions retain the full traceback via exc_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

16 Apr 22:29
246dd1f

Choose a tag to compare

Changed

  • get_fqdn() returns str | NoneNone now distinguishes "no FQDN configured" (HTTP 404 or missing field) from an explicit empty string. Callers that treated "" as "not registered" must update to check for None.
  • Connection callback errors logged at WARNINGSpanMqttClient._on_connection_change now logs callback exceptions via _LOGGER.warning(..., exc_info=True) instead of _LOGGER.exception(...), consistent with _dispatch_snapshot.
  • Reconnect loop catches all exceptionsAsyncMqttBridge._reconnect_loop no longer silently drops on non-OSError failures (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_failure is true now log at WARNING; clean disconnects continue to log at DEBUG.

Fixed

  • CA certificate no longer written to diskAsyncMqttBridge.connect() builds the ssl.SSLContext from the fetched PEM via cadata, 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_names now uses time.monotonic(). The previous code emitted a DeprecationWarning on Python 3.12+.
  • Negative-zero on circuit instant_power_w — explicit guard replaces a cryptic -raw or 0.0 idiom in HomieDeviceConsumer._build_circuit.
  • DSM grid-exchanging heuristic uses epsilon — replaces != 0.0 float comparison with abs(x) > 1.0 W, so the DSM_OFF_GRID branch 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; default Exception.__str__ is now used.
  • Paho lock-layout check at importspan_panel_api.mqtt.async_client verifies on import that the _PAHO_LOCK_ATTRS list exactly matches paho's *_mutex attributes. Raises RuntimeError (not assert, so python -O does not bypass it) on drift.

v2.6.0

16 Apr 18:43
9e383bf

Choose a tag to compare

Added

  • SpanMqttClient.register_connection_callback(cb) — subscribe to broker connection state transitions. Callback fires with False on broker disconnect and True on reconnect; returns an idempotent unregister function. Added to
    SpanPanelClientProtocol so any transport that claims the protocol must implement it.
  • SpanPanelStaleDataError exception — raised by get_snapshot() when the client is not fully live. Derives from SpanPanelError (not from SpanPanelConnectionError), because "never connected" and "running but data not currently live" are
    semantically distinct states.

Changed

  • get_snapshot() contract — now raises SpanPanelStaleDataError when 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_snapshot is now guarded by the same liveness predicate as get_snapshot(), so push consumers never receive a post-disconnect stale snapshot.

Breaking

  • Consumers of get_snapshot() must now handle SpanPanelStaleDataError. Any consumer with a broad except Exception (or except SpanPanelError) branch already handles this correctly.

v2.5.4

10 Apr 02:52
1dab904

Choose a tag to compare

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

09 Apr 01:48
7648da1

Choose a tag to compare

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 return 0.0 for 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

07 Apr 01:39
ea22c9f

Choose a tag to compare

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

01 Apr 18:09

Choose a tag to compare

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

01 Apr 00:39
bb72f42

Choose a tag to compare

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.
  • $target property supportSpanCircuitSnapshot gains relay_state_target and priority_target fields, surfacing the desired-vs-actual state for relay and shed-priority commands.
  • Dirty-node snapshot cachingHomieDeviceConsumer.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 architectureHomieDeviceConsumer no longer handles protocol plumbing. It reads from HomiePropertyAccumulator via 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.
  • SpanMqttClient composes both layersconnect() 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

31 Mar 08:29
fc60d01

Choose a tag to compare

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