Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to `ebus-sdk` are recorded here. Format follows [Keep a Chan

## [Unreleased]

### Changed

- `utility-meter` adds `homie_domain`, `homie_version` and `homie_roles` to its `_ebus._tcp` advertisement, from the domain its Homie device publishes under. The `mdns` extra requires `ebus-service-discovery[zeroconf]>=0.5.1`, which added those keys (electrification-bus/python-service-discovery#8).

## [0.26.0] — 2026-10-09

Adopts [`ebus-service-discovery`](https://github.com/electrification-bus/python-service-discovery) 0.5.0 for mDNS ([#100](https://github.com/electrification-bus/python-sdk/issues/100)); requires ebus-mqtt-client 0.8.0.
Expand Down
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Add `--discover` to find the broker over mDNS, with [`ebus-service-discovery`](h
./utility-meter --config ./utility-meter-cfg.example.json --broker-config /path/to/broker-cfg.json --discover
```

With the `mdns` extra installed, the meter also advertises `_ebus._tcp` and `_device-info._tcp` (role `device`; manufacturer, model, serial number and firmware version from the config's `info`), as the eBus framework requires of every entity. `--no-advertise` turns this off. On a host with several network interfaces, see [electrification-bus/python-service-discovery#3](https://github.com/electrification-bus/python-service-discovery/issues/3).
With the `mdns` extra installed, the meter also advertises `_ebus._tcp` and `_device-info._tcp` (role `device`; manufacturer, model, serial number and firmware version from the config's `info`; `homie_domain`, `homie_version` and `homie_roles` from its Homie device), as the eBus framework requires of every entity. `--no-advertise` turns this off. On a host with several interfaces on one subnet, the advertisement uses one of them and moves to another if that one goes down (ebus-service-discovery's default `interfaces="one-per-subnet"`).

Set a DOE envelope at runtime. The body is a single envelope object or an array of them (the retained schedule); it is set atomically as one `json` value:

Expand Down
24 changes: 17 additions & 7 deletions examples/utility-meter
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,10 @@ class UtilityMeterAdapter:
if self._homie_device is not None:
self._homie_device.stop()

def homie_domain(self) -> str:
"""The Homie domain the meter publishes under."""
return self._homie_device.homie_domain()

@staticmethod
def load_broker_config(config_file: str) -> Dict[str, Any]:
with open(config_file, "r") as fp:
Expand Down Expand Up @@ -752,8 +756,11 @@ def _discover_broker(mqtt_cfg: Dict[str, Any], timeout: float, log: logging.Logg
return None


def _identity_from_info(device_id: str, info: Dict[str, Any]):
"""The `_ebus._tcp` / `_device-info._tcp` identity from the meter's `info` config."""
def _identity_from_info(device_id: str, info: Dict[str, Any], homie_domain: str):
"""The `_ebus._tcp` / `_device-info._tcp` identity from the meter's `info` config.

`homie_domain` adds the `homie_domain`, `homie_version` and `homie_roles` TXT keys.
"""
from ebus_service_discovery.ebus import Identity

return Identity(
Expand All @@ -764,10 +771,11 @@ def _identity_from_info(device_id: str, info: Dict[str, Any]):
serial_number=str(info.get("serial-number") or device_id),
fw_version=str(info["firmware-version"]) if info.get("firmware-version") else None,
device_type=DEVICE_TYPE,
homie_domain=homie_domain,
)


def _start_advertiser(device_id: str, info: Dict[str, Any], log: logging.Logger):
def _start_advertiser(device_id: str, info: Dict[str, Any], homie_domain: str, log: logging.Logger):
"""Advertise `_ebus._tcp` and `_device-info._tcp` (framework.md requirement 6).

Returns the running `mdns.Advertiser`, or None when the `mdns` extra is not
Expand All @@ -779,9 +787,7 @@ def _start_advertiser(device_id: str, info: Dict[str, Any], log: logging.Logger)
log.warning("reason=advertiseSkipped,detail=mdns extra not installed (pip install 'ebus-sdk[mdns]')")
return None
try:
# Named for the device: on macOS the OS already advertises a
# `_device-info._tcp` instance under the host label, the default name.
advertiser = mdns.Advertiser(_identity_from_info(device_id, info), instance_name=device_id).start()
advertiser = mdns.Advertiser(_identity_from_info(device_id, info, homie_domain)).start()
except Exception as exc:
log.warning("reason=advertiseFailed,error=%s", exc)
return None
Expand Down Expand Up @@ -894,7 +900,11 @@ def main():
)
start_doe_endpoint(meter, args.doe_port)
start_tick_thread(meter, args.tick_interval)
advertiser = None if args.no_advertise else _start_advertiser(device_id, meter_cfg.get("info", {}), log)
advertiser = (
None
if args.no_advertise
else _start_advertiser(device_id, meter_cfg.get("info", {}), adapter.homie_domain(), log)
)

log.info(
"reason=utilityMeterReady,deviceId=%s,deviceType=%s",
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ dependencies = [
[project.optional-dependencies]
# mDNS broker discovery and _ebus._tcp advertising for the examples, through
# ebus-service-discovery (which brings zeroconf).
mdns = ["ebus-service-discovery[zeroconf]>=0.5.0"]
mdns = ["ebus-service-discovery[zeroconf]>=0.5.1"]
# Optional: JSONSchema validation of `json`-datatype property `$format`. Absent
# it, the SDK still runs and $format validation is gracefully skipped (with a
# one-time warning). Kept optional so a constrained build (e.g. Yocto) can omit
Expand Down
Loading