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