Repository navigation
Let a /set callback refuse a value #102
Description
A settable property's set_callback cannot tell the SDK it refused a value. Property._settable_callback() uses the callback's return value only to detect a coroutine and schedule it on async_loop (homie.py:1604-1610); for an async callback, the done-callback inspects only the exception (homie.py:1614-1625). The SDK's own refusal, a payload invalid for the datatype, is logged as reason=propertySetRejected (homie.py:1587-1591); an application refusal (out of range for the hardware's current mode, interlocked, busy) is invisible to the SDK.
The C++ core used by esp32-sdk has this: settable callbacks return bool, true when the value was accepted and false when refused (cpp-sdk homie_settable.h:15-17, esp32-sdk NodeEntity.h:43-44). A refused value is restored, nothing is published, and the refusal is logged (cpp-sdk homie_settable.cpp:217-224).
A plain False return cannot be the signal in Python. The documented set_callback is partial(model.set_entity, capability, prop_id) (building-a-proxy.md:219), and set_entity returns the value it was given (property.py:180-196), so /set false on a boolean property already returns False.
Proposal
- A callback (sync, or the awaited coroutine) refuses a value by raising a dedicated
ebus_sdk.SetRefused. The SDK logsreason=propertySetRefused,propertyID=...at info level, distinct frompropertySetCallbackException. Property.set_entity()re-raisesSetRefusedfrom itsentity_setter; today it logs and swallows every exception (property.py:190-193).- Return values keep their current meaning, so existing callbacks are unaffected.
- Document the contract on
set_callbackand indoc/building-a-proxy.md.
Convention
The convention ties $target to acceptance: the exact value goes to $target for a target "received (and accepted)" (convention.md:470), a controller "can only assume that the command it sent to the set topic was received and accepted" (:474), and controllers retry "until the value set is being accepted" (:475).
Related: #91. supports_target=True has been inert since 0.25.0 (#95) because the $target stub ran before the payload was accepted; this refusal is the decision point a future $target publish follows.
🤖 Generated with Claude Code
Activity