Skip to content

build(deps): bump ebus-sdk from 0.1.5 to 0.17.0 - #39

Open
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/pip/ebus-sdk-0.17.0
Open

build(deps): bump ebus-sdk from 0.1.5 to 0.17.0#39
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/pip/ebus-sdk-0.17.0

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Aug 7, 2026

Copy link
Copy Markdown
Contributor

Bumps ebus-sdk from 0.1.5 to 0.17.0.

Release notes

Sourced from ebus-sdk's releases.

v0.17.0

Added

  • Device(async_loop=...): set the consumer's asyncio event loop once on the root instead of on every settable Property. It propagates to every property in the tree via add_node() / Node.add_property() (like QoS) and is inherited by child devices, so inbound /set callbacks for a tree with N settable properties no longer configure the loop N times. The per-Property async_loop argument still works and is preserved when no device-level loop is set. (#15)

Changed

  • Dependency floor: ebus-mqtt-client>=0.3.0 (was >=0.2.0). 0.3.0 ships the PEP 561 py.typed marker, so a downstream type checker resolves the re-exported MqttClient to the concrete class instead of Any, completing the transport Protocol typing (MqttTransport / MqttControllerTransport / MqttDeviceTransport) added in 0.16.0. The bump requires the typed transport rather than any new runtime API: 0.3.0 adds nothing the SDK's runtime needs beyond 0.2.0, and the test suite passes unchanged against it.

Fixed

  • Inbound /set on a settable property with an async (coroutine) callback is now dispatched to the consumer's event loop with asyncio.run_coroutine_threadsafe instead of asyncio.ensure_future. /set arrives on the transport's network-loop thread, and ensure_future is not safe to call from a thread other than the loop's own, so the previous path could misbehave on a real async host. The dispatch now branches on whether the callback returns a coroutine (not merely on a loop being configured), so a synchronous callback keeps running inline even when a tree-wide loop is set, and an exception raised inside an async callback is logged rather than silently swallowed by the discarded scheduling future. The async_loop default is corrected from False to None (a bool against the event-loop annotation, which py.typed surfaces), and Node's mutable default properties={} is replaced with None (a shared-dict footgun the new propagation reads and writes through). (#15)

v0.16.0

Added

  • Device bring-your-own-transport: Device(..., mqttc=<client>) accepts a pre-built MQTT client instead of constructing one from mqtt_cfg, so a host that already owns its MQTT connection (e.g. a Home Assistant integration, whose MQTT integration is single_config_entry and forbids background threads) can publish an eBus device tree over its own transport and event loop. This is the producer-side mirror of the Controller seam added in 0.13.0. An injected client is used as-is: the SDK never start()s or stop()s it, and stop() publishes a final retained $state=disconnected through it and returns without flushing or closing (non-blocking on the caller's loop). Root-only, and mutually exclusive with mqtt_cfg= / parent=. Property publishing now gates on connectivity (is_connected()) rather than only the SDK-owned run flag, so a caller-driven client that never calls the SDK's start() still publishes property values (owned behavior is unchanged: there, connected implies running). Because an injected client bypasses the SDK's connect path (where the LWT, the reconnect republish, and the disconnect hook are wired), the caller wires the Homie-correctness pieces itself using Device.will() and Device.refresh_tree() (below); on_disconnect= is inert for an injected client (documented, and warned at construction). Additive: the default (no mqttc) path constructs, starts, owns, and stops a client exactly as before. Thanks to @​cayossarian (GH #7). (#14)
  • Device.will(): returns the tree root's Last Will and Testament descriptor (topic ending in /$state, payload lost), exposed so a bring-your-own-transport caller can set it on their client before connecting: the will rides the MQTT CONNECT packet, so the SDK cannot add it to a client it is merely handed. The SDK uses it for any client it builds. Device.refresh_tree() (which republishes the whole tree) is documented as the companion on-connect hook a BYO caller wires so the retained tree re-announces after a reconnect. Additive. (#13)
  • Controller.resync(): the tree-rooted discovery bookkeeping reset, extracted from the owned-client on-connect handler and made public so a bring-your-own-transport tree-rooted controller can re-walk the tree from a broker reconnect it drives itself (an injected client bypasses the SDK's on-connect, where the reset is otherwise wired). A no-op in wildcard and single-device modes. Additive. (#13)
  • Bring-your-own-transport injection points are now typed by narrow structural Protocols instead of the concrete MqttClient: MqttTransport (the shared publish + subscribe base) and MqttControllerTransport (which adds unsubscribe, the only member the Controller path reaches beyond the base), both re-exported from ebus_sdk. Controller's mqttc= now accepts any object satisfying MqttControllerTransport, so a consumer injecting its own client keeps type-checking once ebus-mqtt-client ships py.typed (until then MqttClient resolves to Any, so injection already type-checks; the protocol keeps it working past that release, which is the whole point). The protocols deliberately omit start / stop; the SDK keeps the client it constructs on a separate _owned_client handle, so those lifecycle methods resolve on the concrete type and "the SDK never starts or stops a client it did not build" is a property of the types rather than a convention. Additive: the default (SDK-owned) path is unchanged. Thanks to @​cayossarian (GH #8). (#12)
  • MqttDeviceTransport: the structural (Protocol) type for a client injected into a root Device, re-exported from ebus_sdk. It derives from the shared MqttTransport base (publish + subscribe) and adds is_connected + is_running, the members the device publish path reads; it omits start / stop / publish_and_flush, which are owned-only. Device's mqttc= parameter is now annotated with it instead of the concrete MqttClient (parallel to Controller's MqttControllerTransport), so a consumer injecting its own client type-checks once ebus-mqtt-client ships py.typed (today MqttClient resolves to Any, so injection already type-checks; the protocol keeps it working past that release). The SDK keeps the client it builds on a separate Device._owned_client handle, so the owned-only lifecycle methods resolve on the concrete type and "the SDK never starts or stops a client it did not build" is enforced by the types rather than a convention. Additive.

Changed

  • A missing MQTT client is now logged at debug instead of warning when the device tree is TRANSPORT-FREE BY DESIGN — the root holds no client and was given no mqtt_cfg to build one from — because there "no client" is the requested state rather than a fault. A 31-device / 120-property transport-free tree emitted 2,553 warning lines (plus 633 info) reporting only that the caller got what it asked for; the same workload now emits the same 3,186 records at debug, so nothing is dropped and DEBUG still shows every one. Every other case keeps the severity it had: a root that was given a config, or handed a client, and holds none is a genuine fault ("you forgot to start the root") and stays as loud as before — so the blunt workaround (logging.getLogger("homie").setLevel(logging.ERROR)), which also hid those real warnings, is no longer needed. Bring-your-own-transport is deliberately NOT transport-free: the client is present, so its absence would still be an anomaly. devicePublishNoMqttClient, already info rather than warning, keeps info for the expected-a-client case rather than being promoted. Thanks to @​cayossarian (GH #11). (#20)

v0.15.0

Added

  • SiteTopology.to_dot() and SiteTopology.to_mermaid(): serialize the assembled site graph to Graphviz DOT and Mermaid source. Both are pure, dependency-free text builders (they emit source; rendering to an image is the consumer's job, via dot -Tsvg or a Mermaid viewer), so they add no dependency and stay safe in a constrained build (e.g. the panel's Yocto image, where topology.py is consumer-side and typically not even imported). Conventions: a solid edge is a confirmed wiring edge and a dashed edge is one-sided; the service-entrance root, backed-up (island-side) nodes, and undiscovered (dangling-reference) placeholders are visually distinguished. This gives any consumer a one-line topology visualization; the ebus-topology CLI in the electrification-bus/tools repo is a thin wrapper over these. Additive.
  • Device(on_disconnect=...) and Controller.set_on_disconnect_callback(...): an optional consumer hook for push-based disconnect notification (mirror link state into a health readout, log, invalidate cached state) instead of polling is_connected(). The callback contract is transport-neutral: it receives a single clean: bool (True for an orderly/expected disconnect, False for an unexpected drop). The transport (paho) integer reason code is normalized to that boolean at the SDK boundary and never surfaced, so no paho type or value semantics leak into a consumer (paho stays a hidden transitive dependency behind ebus-mqtt-client). Best-effort: a consumer exception is logged, never propagated to the network loop. Fires on the client owner only (a device root; a controller that owns its client). Adopts ebus-mqtt-client 0.2.0's on_disconnect_callback, so the floor rises to ebus-mqtt-client>=0.2.0 (0.14.0 was already verified compatible). Additive. (SDK-al5)

v0.14.0

Added

  • Property.set_format(): a public setter for a property's Homie $format, so an adapter with a dynamic enum/range format (for example an EVSE whose advertised current range changes at runtime) no longer has to assign the private _format. $format lives in the device $description, so the change reaches the wire on the next $description republish; call it inside a device.state_transition() for the batched INIT->READY republish. Additive.
  • examples/simple-tree-device: a runnable parent/child/grandchild device tree over one MQTT connection (a root distribution-enclosure with circuit and BESS children and a MID grandchild). It demonstrates building the whole tree inside one with root.state_transition(): so the root publishes a single INIT->READY, a settable property on a child whose /set routes back over the shared connection, and a --check mode that builds the tree and exits (works with the broker down, via the asynchronous connect).

Removed

  • examples/simple-controller: removed. It did flat, tree-unaware wildcard discovery, which is the dead model now that eBus devices are parent/child trees: it listed a panel and its children as unrelated devices and reported a child's own stale state instead of the effective state under a lost root. A tree-aware replacement that reclaims the simple-controller name is planned as a follow-up.

Fixed

  • Device(mqtt_cfg=None) (the documented default argument) no longer raises AttributeError. Constructing a root Device without a config now builds a TRANSPORT-FREE tree: it composes $description, resolves ids and topics, and holds property values, but never opens a socket, which is useful for tests, schema derivation, and hosts that publish through their own client. mqtt_cfg={} is unchanged (still connects on ebus-mqtt-client's defaults); only None, which previously raised, changes behaviour, so no working caller is affected. The child-attach guard is qualified to still fire for a configured root that has lost its client. Thanks to @​cayossarian (GH #9 / #10).

v0.13.0

Added

  • HA discovery emit: an override hook now sees the device's live property values. PropertyContext (the object emit passes to an OverrideHook) carries a new read-only property_values field, the whole device's {node_id: {prop_id: value}} map, threaded through homie_property_to_component / homie_description_to_ha (and already sourced from discovered.properties by homie_device_to_ha). A hook can now derive stable entity identity or metadata from a sibling property's value (e.g. a circuit's breaker position in info/space) rather than from a user-editable name. Additive and backward compatible: hooks that ignore the field are unaffected, and the field is None for a description-only caller. The dict aliases the controller's live cache, so treat it as read-only.

... (truncated)

Changelog

Sourced from ebus-sdk's changelog.

[0.17.0] — 2026-08-02

Added

  • Device(async_loop=...): set the consumer's asyncio event loop once on the root instead of on every settable Property. It propagates to every property in the tree via add_node() / Node.add_property() (like QoS) and is inherited by child devices, so inbound /set callbacks for a tree with N settable properties no longer configure the loop N times. The per-Property async_loop argument still works and is preserved when no device-level loop is set. (#15)

Changed

  • Dependency floor: ebus-mqtt-client>=0.3.0 (was >=0.2.0). 0.3.0 ships the PEP 561 py.typed marker, so a downstream type checker resolves the re-exported MqttClient to the concrete class instead of Any, completing the transport Protocol typing (MqttTransport / MqttControllerTransport / MqttDeviceTransport) added in 0.16.0. The bump requires the typed transport rather than any new runtime API: 0.3.0 adds nothing the SDK's runtime needs beyond 0.2.0, and the test suite passes unchanged against it.

Fixed

  • Inbound /set on a settable property with an async (coroutine) callback is now dispatched to the consumer's event loop with asyncio.run_coroutine_threadsafe instead of asyncio.ensure_future. /set arrives on the transport's network-loop thread, and ensure_future is not safe to call from a thread other than the loop's own, so the previous path could misbehave on a real async host. The dispatch now branches on whether the callback returns a coroutine (not merely on a loop being configured), so a synchronous callback keeps running inline even when a tree-wide loop is set, and an exception raised inside an async callback is logged rather than silently swallowed by the discarded scheduling future. The async_loop default is corrected from False to None (a bool against the event-loop annotation, which py.typed surfaces), and Node's mutable default properties={} is replaced with None (a shared-dict footgun the new propagation reads and writes through). (#15)

[0.16.0] — 2026-08-02

Added

  • Device bring-your-own-transport: Device(..., mqttc=<client>) accepts a pre-built MQTT client instead of constructing one from mqtt_cfg, so a host that already owns its MQTT connection (e.g. a Home Assistant integration, whose MQTT integration is single_config_entry and forbids background threads) can publish an eBus device tree over its own transport and event loop. This is the producer-side mirror of the Controller seam added in 0.13.0. An injected client is used as-is: the SDK never start()s or stop()s it, and stop() publishes a final retained $state=disconnected through it and returns without flushing or closing (non-blocking on the caller's loop). Root-only, and mutually exclusive with mqtt_cfg= / parent=. Property publishing now gates on connectivity (is_connected()) rather than only the SDK-owned run flag, so a caller-driven client that never calls the SDK's start() still publishes property values (owned behavior is unchanged: there, connected implies running). Because an injected client bypasses the SDK's connect path (where the LWT, the reconnect republish, and the disconnect hook are wired), the caller wires the Homie-correctness pieces itself using Device.will() and Device.refresh_tree() (below); on_disconnect= is inert for an injected client (documented, and warned at construction). Additive: the default (no mqttc) path constructs, starts, owns, and stops a client exactly as before. Thanks to @​cayossarian (GH #7). (#14)
  • Device.will(): returns the tree root's Last Will and Testament descriptor (topic ending in /$state, payload lost), exposed so a bring-your-own-transport caller can set it on their client before connecting: the will rides the MQTT CONNECT packet, so the SDK cannot add it to a client it is merely handed. The SDK uses it for any client it builds. Device.refresh_tree() (which republishes the whole tree) is documented as the companion on-connect hook a BYO caller wires so the retained tree re-announces after a reconnect. Additive. (#13)
  • Controller.resync(): the tree-rooted discovery bookkeeping reset, extracted from the owned-client on-connect handler and made public so a bring-your-own-transport tree-rooted controller can re-walk the tree from a broker reconnect it drives itself (an injected client bypasses the SDK's on-connect, where the reset is otherwise wired). A no-op in wildcard and single-device modes. Additive. (#13)
  • Bring-your-own-transport injection points are now typed by narrow structural Protocols instead of the concrete MqttClient: MqttTransport (the shared publish + subscribe base) and MqttControllerTransport (which adds unsubscribe, the only member the Controller path reaches beyond the base), both re-exported from ebus_sdk. Controller's mqttc= now accepts any object satisfying MqttControllerTransport, so a consumer injecting its own client keeps type-checking once ebus-mqtt-client ships py.typed (until then MqttClient resolves to Any, so injection already type-checks; the protocol keeps it working past that release, which is the whole point). The protocols deliberately omit start / stop; the SDK keeps the client it constructs on a separate _owned_client handle, so those lifecycle methods resolve on the concrete type and "the SDK never starts or stops a client it did not build" is a property of the types rather than a convention. Additive: the default (SDK-owned) path is unchanged. Thanks to @​cayossarian (GH #8). (#12)
  • MqttDeviceTransport: the structural (Protocol) type for a client injected into a root Device, re-exported from ebus_sdk. It derives from the shared MqttTransport base (publish + subscribe) and adds is_connected + is_running, the members the device publish path reads; it omits start / stop / publish_and_flush, which are owned-only. Device's mqttc= parameter is now annotated with it instead of the concrete MqttClient (parallel to Controller's MqttControllerTransport), so a consumer injecting its own client type-checks once ebus-mqtt-client ships py.typed (today MqttClient resolves to Any, so injection already type-checks; the protocol keeps it working past that release). The SDK keeps the client it builds on a separate Device._owned_client handle, so the owned-only lifecycle methods resolve on the concrete type and "the SDK never starts or stops a client it did not build" is enforced by the types rather than a convention. Additive.

Changed

  • A missing MQTT client is now logged at debug instead of warning when the device tree is TRANSPORT-FREE BY DESIGN — the root holds no client and was given no mqtt_cfg to build one from — because there "no client" is the requested state rather than a fault. A 31-device / 120-property transport-free tree emitted 2,553 warning lines (plus 633 info) reporting only that the caller got what it asked for; the same workload now emits the same 3,186 records at debug, so nothing is dropped and DEBUG still shows every one. Every other case keeps the severity it had: a root that was given a config, or handed a client, and holds none is a genuine fault ("you forgot to start the root") and stays as loud as before — so the blunt workaround (logging.getLogger("homie").setLevel(logging.ERROR)), which also hid those real warnings, is no longer needed. Bring-your-own-transport is deliberately NOT transport-free: the client is present, so its absence would still be an anomaly. devicePublishNoMqttClient, already info rather than warning, keeps info for the expected-a-client case rather than being promoted. Thanks to @​cayossarian (GH #11). (#20)

[0.15.0] — 2026-08-02

Added

  • SiteTopology.to_dot() and SiteTopology.to_mermaid(): serialize the assembled site graph to Graphviz DOT and Mermaid source. Both are pure, dependency-free text builders (they emit source; rendering to an image is the consumer's job, via dot -Tsvg or a Mermaid viewer), so they add no dependency and stay safe in a constrained build (e.g. the panel's Yocto image, where topology.py is consumer-side and typically not even imported). Conventions: a solid edge is a confirmed wiring edge and a dashed edge is one-sided; the service-entrance root, backed-up (island-side) nodes, and undiscovered (dangling-reference) placeholders are visually distinguished. This gives any consumer a one-line topology visualization; the ebus-topology CLI in the electrification-bus/tools repo is a thin wrapper over these. Additive.
  • Device(on_disconnect=...) and Controller.set_on_disconnect_callback(...): an optional consumer hook for push-based disconnect notification (mirror link state into a health readout, log, invalidate cached state) instead of polling is_connected(). The callback contract is transport-neutral: it receives a single clean: bool (True for an orderly/expected disconnect, False for an unexpected drop). The transport (paho) integer reason code is normalized to that boolean at the SDK boundary and never surfaced, so no paho type or value semantics leak into a consumer (paho stays a hidden transitive dependency behind ebus-mqtt-client). Best-effort: a consumer exception is logged, never propagated to the network loop. Fires on the client owner only (a device root; a controller that owns its client). Adopts ebus-mqtt-client 0.2.0's on_disconnect_callback, so the floor rises to ebus-mqtt-client>=0.2.0 (0.14.0 was already verified compatible). Additive. (SDK-al5)

[0.14.0] — 2026-08-02

Added

  • Property.set_format(): a public setter for a property's Homie $format, so an adapter with a dynamic enum/range format (for example an EVSE whose advertised current range changes at runtime) no longer has to assign the private _format. $format lives in the device $description, so the change reaches the wire on the next $description republish; call it inside a device.state_transition() for the batched INIT->READY republish. Additive.
  • examples/simple-tree-device: a runnable parent/child/grandchild device tree over one MQTT connection (a root distribution-enclosure with circuit and BESS children and a MID grandchild). It demonstrates building the whole tree inside one with root.state_transition(): so the root publishes a single INIT->READY, a settable property on a child whose /set routes back over the shared connection, and a --check mode that builds the tree and exits (works with the broker down, via the asynchronous connect).

Removed

  • examples/simple-controller: removed. It did flat, tree-unaware wildcard discovery, which is the dead model now that eBus devices are parent/child trees: it listed a panel and its children as unrelated devices and reported a child's own stale state instead of the effective state under a lost root. A tree-aware replacement that reclaims the simple-controller name is planned as a follow-up.

Fixed

  • Device(mqtt_cfg=None) (the documented default argument) no longer raises AttributeError. Constructing a root Device without a config now builds a TRANSPORT-FREE tree: it composes $description, resolves ids and topics, and holds property values, but never opens a socket, which is useful for tests, schema derivation, and hosts that publish through their own client. mqtt_cfg={} is unchanged (still connects on ebus-mqtt-client's defaults); only None, which previously raised, changes behaviour, so no working caller is affected. The child-attach guard is qualified to still fire for a configured root that has lost its client. Thanks to @​cayossarian (GH #9 / #10).

... (truncated)

Commits

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Bumps [ebus-sdk](https://github.com/electrification-bus/python-sdk) from 0.1.5 to 0.17.0.
- [Release notes](https://github.com/electrification-bus/python-sdk/releases)
- [Changelog](https://github.com/electrification-bus/python-sdk/blob/main/CHANGELOG.md)
- [Commits](https://github.com/electrification-bus/python-sdk/commits/v0.17.0)

---
updated-dependencies:
- dependency-name: ebus-sdk
  dependency-version: 0.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file python Pull requests that update python code labels Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file python Pull requests that update python code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants