Skip to content

Add a button event entity for remotes - #959

Draft
zigpy-review-bot wants to merge 6 commits into
devfrom
zigpy-bot/zha-event-button-entity
Draft

zigpy-review-bot wants to merge 6 commits into
devfrom
zigpy-bot/zha-event-button-entity

Conversation

@zigpy-review-bot

@zigpy-review-bot zigpy-review-bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Note

Part of the standardized button event entities work tracked in #961: all PRs, design choices and alternatives, open decisions.

Summary

Remotes report button presses as cluster commands, which ZHA relays as zha_events, and quirks map those to device automation triggers. This adds ZhaEventButton, a button event entity that maps the same zha_events to the standard button event types (home-assistant/architecture#1502), so quirks can give every physical button an event entity. The quirk side, QuirkBuilder.button_event() and the device conversions, is in zigpy/zha-device-handlers#5480 and the PRs stacked on it.

Changes

  • ZhaEventMatch: an event type, the zha_event data it fires for, and optional event attributes (press_count for multi_press). The data is matched like the data of a ZHA device automation trigger: every key has to be equal, nested mappings such as params only need to contain the expected keys, and lists and tuples compare by their items. A quirk can pass a trigger's data as is, so the event fires for the zha_events that trigger's data matches.

  • ZhaEventButton: a ZclPlatformEntity attached to one of the button's clusters (Make PlatformEntity device bound and add ZclPlatformEntity #919), listening to its device's zha_events. It fires the event type of the first match, so one event from the device fires at most one event type. Its only state is whether the button is held, which the proposal allows for two things (rule 7):

    • repeats=True on a long_press_start fires it once per hold when the device repeats it while the button is held (Philips Hue remotes);
    • while_held=True on a long_press_end only matches after a long_press_start of the same entity, ahead of the entity's other matches. This handles a release that doesn't say which button was held, like the arrow release of the IKEA STYRBAR, or that is the same for a short press and a hold.

    Both rely on the device's releases. If one gets lost, the whole next hold of a repeats entity is taken for repeats until its release, and with while_held the next release the device doesn't tell apart is taken for the end of the hold once.

  • The entity is validated when it is created, not when an event fires: a multi_press needs a valid press_count, while_held needs a long_press_start on the same entity, and repeats needs another event type that ends the hold.

  • An error while matching or firing an event is logged with the entity and the zha_event data. Since Contain listener errors in EventBase.emit() #930, emit() keeps the zha_event flowing to the device's other listeners, such as HA's device triggers, either way.

  • EntityPlatform.EVENT, so quirks can expose event entities.

Alternatives

Testing

tests/test_platform_event.py: the matching rules, an end-to-end test where real ZCL frames of an Innr RC 250 fixture fire the entity, first-match-wins, repeated holds, releases while held (one button, two buttons sharing a release, and in any order), errors in listeners and while firing, other devices' events, removal and validation.

@codecov

codecov Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.52941% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 97.24%. Comparing base (738ee63) to head (484f0f0).

Files with missing lines Patch % Lines
zha/application/platforms/event/__init__.py 98.50% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##              dev     #959   +/-   ##
=======================================
  Coverage   97.23%   97.24%           
=======================================
  Files          61       61           
  Lines       10719    10785   +66     
=======================================
+ Hits        10423    10488   +65     
- Misses        296      297    +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Base automatically changed from zigpy-bot/button-event-types to dev October 26, 2026 21:08
ZhaEventButton maps the zha_events of its device to standard button event
types with ZhaEventMatch entries, matched like the data of a ZHA device
automation trigger. The first match fires, so one event from the device fires
at most one event type, and a long_press_start the device repeats while held
fires once per hold.
A ZhaEventMatch with while_held only matches after a long_press_start of the
entity, which ends the hold for a release that does not tell the button or a
short press apart (home-assistant/architecture#1502 rule 7).
An error firing an event no longer keeps the zha_event from the device's other
listeners, a release matching while held wins regardless of its position, and
an entity matching while held needs a long_press_start.
@TheJulianJES
TheJulianJES force-pushed the zigpy-bot/zha-event-button-entity branch from d749a72 to 684b4be Compare October 26, 2026 21:08
@TheJulianJES

TheJulianJES commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

@zigpy-review-bot #958 was just merged. Update this if needed. Investigate test failure and possibly update PR or open new one(s) after review rounds.

…ntities

EventBase.emit() now contains listener errors itself (#930), so the entity's
own handler only sees errors from matching and firing.
@zigpy-review-bot

Copy link
Copy Markdown
Collaborator Author

The test failure came from #930, not from #958: EventBase.emit() now catches and logs listener errors itself ("Error calling listener …"), so test_zha_event_button_error_keeps_zha_event no longer saw the entity's own "Failed to handle zha_event" message. The PR was already based on the merged #958, so there was nothing to rebase.

Pushed 9a66875 and 484f0f0:

  • the listener-error test now asserts the emit() log, and a new test covers an error while matching or firing (the entity's own log line);
  • the comment on the entity's try/except no longer claims it is what keeps the zha_event flowing, since emit() does that now;
  • PR body: dropped "Stacked on Update button event types to the revised standard #958" and reworded the error-handling bullet and the testing line.

Full suite passes locally (1523), ruff and mypy are clean. One Fable review round on the delta found only the comment and description inaccuracies above, now fixed. I skipped the Copilot pass because the change is test and comment only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature Adds new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants