Skip to content

fix(dev-setup): install ebus-emitter from its lock; bump to 1.0.12 - #30

Merged
cayossarian merged 3 commits into
release/v1.0.12from
fix/dev-setup-lock-emitter-deps
Jul 31, 2026
Merged

fix(dev-setup): install ebus-emitter from its lock; bump to 1.0.12#30
cayossarian merged 3 commits into
release/v1.0.12from
fix/dev-setup-lock-emitter-deps

Conversation

@cayossarian

Copy link
Copy Markdown
Member

Problem

Starting the simulator failed for every panel:

[homie] ERROR: reason=deviceConnectBrokerFailed,id=sim-40t-001
  File "ebus_sdk/homie.py", line 1955, in connect_broker
    self.mqttc = MqttClient.from_config(mqtt_cfg=self._mqtt_cfg, ...)
  File "ebus_mqtt_client/client.py", line 240, in from_config
    endpoint = mqtt_cfg.get("host", MQTT_DEFAULT_HOST)
AttributeError: 'NoneType' object has no attribute 'get'

Root cause

scripts/dev-setup.sh installed the emitter with a bare uv pip install --editable <path>. That re-resolves the emitter's dependency constraints against PyPI and ignores the emitter's own uv.lock (which pins ebus-sdk 0.1.5). A fresh bootstrap therefore resolved ebus-sdk>=0.1.5 to 0.12.0.

Per the SDK's own metadata: "0.2.0 introduces parent/child device trees and contains breaking changes to the Device constructor." Among them, mqtt_cfg's default changed from {} to None. The emitter's build_graph() constructs the root Device as a passive topic/schema model with no mqtt_cfg, so connect_broker() dereferenced None before the graph was ever built.

Minimal repro, no simulator involved:

ebus-sdk 0.12.0: ebus_sdk.Device('r', name='R', type='ebus.panel') -> AttributeError
ebus-sdk 0.1.5:  -> OK

Fix

  • dev-setup.sh now installs the emitter's locked runtime deps (uv export from its lock), then the emitter itself with --no-deps so nothing is re-resolved. The venv matches exactly what the emitter pins.
  • ebus-emitter carries a matching ebus-sdk>=0.1.5,<0.2 upper bound (committed separately in that repo), so the constraint is enforced declaratively too.
  • Version bumped to 1.0.12 to match this release branch; sync-version.sh propagated it to __init__.py, config.yaml, and the Dockerfile label.
  • Root CHANGELOG.md [unreleased] stamped as 1.0.12, and the add-on CHANGELOG.md — which had stopped at 1.0.11 — gets a 1.0.12 section covering the emitter cutover and the schema changes downstream consumers will notice.

Verification

  • tests/test_panel.py::TestPanelInstance::test_start_and_stop was failing with the identical AttributeError. It already covered this; it just had not been re-run after the venv drifted. Now passes.
  • Full suite: 227 passed. Emitter suite in its own venv: 150 passed.
  • Started the app end to end: Panel sim-40t-001 started (config=MAIN_40.yaml) / Reload complete: started=1, stopped=0, reloaded=0, errors=0.

Follow-up, not addressed here

The emitter uses ebus_sdk.Device purely as a topic/schema model — Publisher sends everything over the producer's aiomqtt client and only reads get_device_id()/get_node_id()/id(). But on 0.1.5, constructing that model also opens a second, unmanaged paho connection to the default broker and publishes $description/$state on it. That is pre-existing, not a regression, and it is the same knot that makes moving to ebus-sdk 0.2+ non-trivial: the newer SDK requires a root Device to own a broker connection, which conflicts with the emitter's "producer owns the connection" design. Worth settling before raising that version bound.

…lving

`uv pip install --editable <path>` re-resolves the emitter's dependency
constraints against PyPI and ignores the emitter's own uv.lock. Its
`ebus-sdk>=0.1.5` constraint therefore resolved to 0.12.0 on a fresh
bootstrap, pulling in an SDK the emitter has never been tested against.

ebus-sdk 0.2.0 changed the `Device` constructor's `mqtt_cfg` default from
`{}` to `None` (alongside the parent/child tree rework). The emitter's
build_graph() constructs the root Device as a passive topic/schema model
with no mqtt_cfg, so `connect_broker()` dereferenced None and every panel
failed to start:

    AttributeError: 'NoneType' object has no attribute 'get'

tests/test_panel.py::TestPanelInstance::test_start_and_stop already
covered this — it just had not been re-run after the venv drifted.

Install the emitter's locked runtime dependencies first (`uv export` from
its lock), then the emitter itself with `--no-deps` so nothing is
re-resolved. The venv now matches exactly what the emitter pins.

ebus-emitter carries a matching `ebus-sdk<0.2` upper bound so the
constraint is enforced declaratively as well.
Matches the release/v1.0.12 branch. sync-version.sh propagated the
pyproject version to __init__.py, config.yaml, and the Dockerfile label.

The root CHANGELOG's `[unreleased]` section — emitter live-schema
alignment and abstraction, plus the dev-bootstrap dependency-drift fix —
is now stamped as 1.0.12.
The add-on changelog stopped at 1.0.11, so 1.0.12 would have shipped with
no user-facing release notes. Covers the emitter cutover and the schema
changes downstream consumers will notice — flat Homie 5 layout, renamed
lugs and firmware-version keys, multi-EVSE publishing, per-panel MQTT
connections, retained-topic cleanup on stop — plus the startup failure
caused by the unpinned emitter dependency resolve.
@cayossarian
cayossarian merged commit a3dfa74 into release/v1.0.12 Jul 31, 2026
1 check passed
@cayossarian
cayossarian deleted the fix/dev-setup-lock-emitter-deps branch July 31, 2026 05:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant