From 301c07b58dabd8d0c28e944fac742095a96f3872 Mon Sep 17 00:00:00 2001 From: Donald Clark Jackson Date: Wed, 12 Aug 2026 23:25:43 -0700 Subject: [PATCH] docs: correct four stale claims and two markdownlint errors Found while writing the 0.20.0 docs; none is caused by that work, and each would have shipped a third release saying something untrue. - README Requirements pinned `ebus-mqtt-client >= 0.3.0` while pyproject.toml has required >= 0.4.0 since 0.18.0. Also notes what 0.4.0 added (asyncio_driver), matching how the other floors are annotated. - Two comments in homie.py said the Homie 5 empty-string encoding was "not yet implemented (SDK-ef1 / known limitation)". It is implemented, a few lines below the first of them, via encode_empty_string(). The distinction the comments draw is still the point and is kept: a zero-length payload retracts a retained topic, a 1-character 0x00 payload is an empty-string VALUE. - adapter.py said registering the mirror means "every value change republishes to MQTT". Still true of a change, but the Homie layer now gates a republish whose wire payload is unchanged, and this docstring is the documented seam between the two dedup layers, so it should say which is which. - markdownlint: a code fence at column 0 broke ordered-list continuity in ha-discovery-bridge.md (MD029, "2." read as a new list starting at 2), and fences inside list items lacked surrounding blank lines in packaging-for-legacy-setuptools.md (MD031). Both diffs are whitespace only. Ruff's extend-exclude keeps *.md out of check and format, so nothing in CI would ever have caught these. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 2 +- doc/ha-discovery-bridge.md | 18 +++++++++--------- doc/packaging-for-legacy-setuptools.md | 4 ++++ src/ebus_sdk/adapter.py | 4 +++- src/ebus_sdk/homie.py | 9 +++++---- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 3694b5b..eb80363 100644 --- a/README.md +++ b/README.md @@ -339,7 +339,7 @@ See [`examples/README.md`](examples/README.md) for example scripts demonstrating ## Requirements - Python 3.10+ -- [`ebus-mqtt-client`](https://github.com/electrification-bus/ebus-mqtt-client) >= 0.3.0 (the MQTT transport layer; it pins `paho-mqtt`, so the SDK does not depend on paho directly. 0.3.0 ships the `py.typed` marker, so a downstream type checker resolves the re-exported `MqttClient` to the concrete class rather than `Any`; 0.2.0 adds the `on_disconnect_callback` the SDK's disconnect hook adopts; and, since 0.1.8, it carries the asynchronous, down-broker-tolerant connect the resilient-connect behavior relies on) +- [`ebus-mqtt-client`](https://github.com/electrification-bus/ebus-mqtt-client) >= 0.4.0 (the MQTT transport layer; it pins `paho-mqtt`, so the SDK does not depend on paho directly. 0.4.0 provides `MqttClient.asyncio_driver()`, the loop-native alternative to paho's background thread; 0.3.0 ships the `py.typed` marker, so a downstream type checker resolves the re-exported `MqttClient` to the concrete class rather than `Any`; 0.2.0 adds the `on_disconnect_callback` the SDK's disconnect hook adopts; and, since 0.1.8, it carries the asynchronous, down-broker-tolerant connect the resilient-connect behavior relies on) Optional extras: diff --git a/doc/ha-discovery-bridge.md b/doc/ha-discovery-bridge.md index 86398b2..0c084c8 100644 --- a/doc/ha-discovery-bridge.md +++ b/doc/ha-discovery-bridge.md @@ -116,17 +116,17 @@ Two mechanisms preserve the `entity_id`: 1. **Set `default_entity_id` at creation.** `HAComponent` carries a typed `default_entity_id` field (emitted as HA's `default_entity_id`); set it to the OLD integration's `entity_id` and Home Assistant uses that id when it first creates the entity. This is the clean path for a device that has not been discovered yet. Set it from an override hook (or the customizer table): -```python -OLD_IDS = {("meter", "active-power"): "sensor.panel_main_power"} # prior entity_ids + ```python + OLD_IDS = {("meter", "active-power"): "sensor.panel_main_power"} # prior entity_ids -def preserve_ids(component, ctx): - old = OLD_IDS.get((ctx.node_id, ctx.prop_id)) - if old: - component.default_entity_id = old - return component + def preserve_ids(component, ctx): + old = OLD_IDS.get((ctx.node_id, ctx.prop_id)) + if old: + component.default_entity_id = old + return component -HaDiscoveryBridge(controller, default_override=preserve_ids) -``` + HaDiscoveryBridge(controller, default_override=preserve_ids) + ``` `default_entity_id` applies only when the entity is first created; it has no effect on an entity that already exists. diff --git a/doc/packaging-for-legacy-setuptools.md b/doc/packaging-for-legacy-setuptools.md index 9e53b03..b25c047 100644 --- a/doc/packaging-for-legacy-setuptools.md +++ b/doc/packaging-for-legacy-setuptools.md @@ -74,16 +74,20 @@ setup() 2. Extract the sdist: `tar xzf dist/-X.Y.Z.tar.gz -C /tmp`. 3. Confirm `setup.py` (or `setup.cfg [metadata]`) is present in the extracted tree. 4. Build a wheel using an **old** setuptools to simulate the legacy environment: + ```bash python -m venv /tmp/legacy-env /tmp/legacy-env/bin/pip install 'setuptools<61' wheel cd /tmp/-X.Y.Z /tmp/legacy-env/bin/python -m build --wheel --no-isolation ``` + 5. Inspect the produced wheel: + ```bash unzip -l dist/-X.Y.Z-py3-none-any.whl ``` + 6. Confirm the wheel name is `-X.Y.Z-...` (NOT `UNKNOWN-0.0.0-...`) and contains your actual `.py` files (not just `dist-info/` metadata). If you have a Yocto consumer, a more authoritative test is to build the package via the bitbake recipe (e.g., on dsw-build-01) and inspect the produced `.deb` with `dpkg-deb -c`. diff --git a/src/ebus_sdk/adapter.py b/src/ebus_sdk/adapter.py index 7a9d3a1..f6015be 100644 --- a/src/ebus_sdk/adapter.py +++ b/src/ebus_sdk/adapter.py @@ -24,7 +24,9 @@ def set_homie_property_from_python_property(homie_property: HomieProperty, pytho This is the on-change adapter that mirrors the observable model to Homie. Register it as the ``GroupedPropertyDict`` on-change callback for a - ``(group, property_id)`` pair so every value change republishes to MQTT:: + ``(group, property_id)`` pair so every value change reaches MQTT (subject to + the Homie layer's own publish-on-change gate, which drops a republish whose + wire payload is unchanged -- see ``homie.Property.set_value``):: properties.add_property_on_change_callback( group, diff --git a/src/ebus_sdk/homie.py b/src/ebus_sdk/homie.py index 6df8720..2a9766f 100644 --- a/src/ebus_sdk/homie.py +++ b/src/ebus_sdk/homie.py @@ -848,8 +848,9 @@ def publish_value(self, *, force: bool = False) -> bool: # NOTE: this clears the topic (empty MQTT payload). It does NOT # represent an actual empty-string *value*, which the Homie 5 # convention encodes as a 1-character 0x00 payload — see the module - # header "empty string values" note; that encoding is not yet - # implemented (SDK-ef1 / known limitation). + # header "empty string values" note. That encoding IS implemented, + # below, via encode_empty_string(); the two payloads are distinct and + # only the zero-length one retracts a retained topic. logger.debug( f"reason=propertyPublishValueIsNoneClearing,deviceID={device_id},nodeID={node_id},propertyID={self._id}" ) @@ -903,8 +904,8 @@ def clear_value(self) -> bool: previously-published property (see ``publish_value``). This clears the topic; it does NOT publish an actual empty-string - *value* (which the Homie 5 convention encodes as a 1-character 0x00 - payload — not yet implemented, see the module header note). + *value*, which the Homie 5 convention encodes as a 1-character 0x00 + payload and ``publish_value()`` emits via ``encode_empty_string()``. No-ops (returns True) if the property was never published, to avoid creating a phantom retained-empty topic. Returns True on success, else