feat: publish a device tree under any Homie 5 domain - #62
Merged
Conversation
The SDK could CONSUME any Homie 5 tree and PRODUCE only an eBus one. Controller has always taken homie_domain=, uses it for subscriptions, set_property and $broadcast, and even parses the domain back out of a received topic. Device had no such parameter: every topic it derived came from the EBUS_HOMIE_DOMAIN constant at ten sites across Device, Node and Property, plus the Last Will. Its own docstring carried the stub "homie_domains config for future use, not currently supported by this code", which is now replaced by what to actually do. Nothing about eBus changes. Energy devices keep publishing under `ebus`, which the specification mandates and which remains the default, so a publisher that never mentions the parameter is byte-identical on the wire. What this buys is that the same SDK can also publish non-energy devices under the standard `homie` domain: the difference between an eBus library and a Homie 5 library that defaults to eBus. The domain covers everything a tree derives: property values, /set subscriptions, $state, $description, the retraction topics delete() and delete_all_from_mqtt() clear, and both will() and the LWT installed on an owned client. Inbound /set validation had to follow. Property._settable_callback compared the received domain against EBUS_HOMIE_DOMAIN, so a device published under `homie` would have subscribed to the right topic and then silently rejected every command that arrived. It is a property of the TREE, not of a device, exactly like the connection and the QoS: a child under a different domain would sit outside its own root's subtree, and the root's Last Will (one retained publish on the root's $state) could not cover it. Only a root stores it, descendants read it through the new Device.homie_domain(), and a child passing its own is refused with a ValueError as a child passing its own mqtt_cfg= already is. Refused even when the value would have matched: the rule is structural, and a silently-dropped domain surfaces as topics on the wrong prefix rather than as an error. One test-double fix: _make_wired_property builds a MagicMock device, which returned a MagicMock from homie_domain() and broke three settable-callback tests. The double now answers it, rather than the production code being made defensive about mocks. Closes #61 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #61.
Devicegainshomie_domain=, defaulting toebus. Ten publisher-side topic constructions now resolve it instead of the module constant.What the domain now covers
/setsubscription topics$stateand$descriptiondelete()anddelete_all_from_mqtt()clearwill()and the LWT installed on an SDK-owned client, which stay identical to each other by constructionVerified by listing every topic construction in
homie.py: the nineDevice/Propertysites all resolve the domain now, and the ones left on the constant areController(already configurable) and thehomieversion field, which is not a topic.The inbound
/setcheckProperty._settable_callbackcompared the received topic's domain againstEBUS_HOMIE_DOMAIN. Left alone, a device published underhomiewould have subscribed to the correct/settopic and then silently rejected every command that arrived, which is a worse failure than not supporting the domain at all. It now compares against the tree's own domain, with a test in each direction: accepted on the tree's domain, ignored on a foreign one.It belongs to the tree
Like the MQTT connection and the QoS. A child under a different domain would sit outside its own root's subtree, and the root's Last Will (a single retained publish on the root's
$state) could not cover it.So only a root stores it, descendants read it through the new
Device.homie_domain(), and a child passing its own gets aValueError, matching the existingparent=plusmqtt_cfg=rule. It is refused even when the value would have matched (test_a_child_of_a_default_domain_root_is_also_refused), because the rule is structural rather than a value check, and a silently-dropped domain would surface as topics on the wrong prefix rather than as an error.Compatibility
The default is unchanged,
EBUS_HOMIE_DOMAINstays exported, and a publisher that never mentions the parameter is byte-identical on the wire.test_default_domain_is_ebusandtest_explicit_ebus_is_the_same_as_omitting_itpin that.The
Devicedocstring'shomie_domains config for future use, not currently supported by this codestub is replaced with what to actually do.One test-double fix
_make_wired_propertyintest_homie_device.pybuilds aMagicMockdevice, which returned aMagicMockfromhomie_domain()and broke three settable-callback tests. The double now answers it. Fixing the double rather than making production code defensive about mocks.Out of scope
Multi-domain on one
Controller. Today oneControllermonitors one domain, so watching both trees means two of them. Noted in #61 as a separate question.Verification
ruff checkandruff format --checkcleanCHANGELOG.mdandREADME.md🤖 Generated with Claude Code