Skip to content

Add Matter support (generic event-entity connection type) + IKEA BILRESA scroll wheel - #59

Open
tz8 wants to merge 10 commits into
macpit:masterfrom
tz8:master
Open

Add Matter support (generic event-entity connection type) + IKEA BILRESA scroll wheel#59
tz8 wants to merge 10 commits into
macpit:masterfrom
tz8:master

Conversation

@tz8

@tz8 tz8 commented Aug 11, 2026

Copy link
Copy Markdown

Addresses #51 (request to add the IKEA BILRESA scroll-wheel remote) — here via Matter.

What this adds

Switch Manager currently receives events either over MQTT or from a dedicated bus
event (zha_event, deconz_event, …). Matter remotes fire neither — in Home
Assistant they surface as event.* entities, where a button press is a
state_changed and the action lives in new_state.attributes.event_type.

This PR adds a third, generic connection type keyed on event_type: state_changed,
so any blueprint can react to event.* entities. Matter is the first beneficiary,
but nothing here is Matter-specific: no new dependency, no frontend changes, and
auto-discovery works through the existing mechanism.

It also ships a ready-to-use visual blueprint for the IKEA BILRESA scroll-wheel
remote
(the device requested in #51) and two small, generic blueprint-action options
that make batched scroll events pleasant to use.

Backward compatibility

  • Existing MQTT and bus-event blueprints are untouched — the new branch only runs for
    blueprints that opt in with event_type: state_changed.
  • The two new action options (scale_field, repeat) are optional; actions without
    them behave exactly as before.
  • No changes to the frontend bundle or the stored config format.

1. state_changed connection type

  • schema.py: BLUEPRINT_STATE_SCHEMA (optional state_domain, default event).
  • __init__.py: blueprint schema dispatch recognises state_changed.
  • helpers.py: format_state_event() flattens a state_changed into
    { entity_id, device_id, endpoint, event_type, presses, state, …attrs };
    matter_endpoint_from_unique_id() derives the Matter endpoint from the HA unique_id.
  • models.py: Blueprint.is_state / state_domain; a state branch in
    create_event_listeners().

Listener scoping:

  • Config path uses async_track_state_change_event scoped to the target device's
    entities (identifier = HA device_id, resolved via the entity registry; a raw
    entity_id also works). It never falls back to a global listener, so an unresolved
    device cannot become a catch-all.
  • Discovery path listens to global state_changed, filtered by domain and to real
    triggers (skips restore/add) — same "press a button to discover it" UX as zha_event.

2. Two optional action options for batched events

Matter batches fast scrolling into a single multi_press_N event (N = notches, capped
at 8), unlike Zigbee which sends one event per notch. data.presses (parsed from
multi_press_N, else 1) exposes the count, and actions can use it without the end
user writing templates
:

  • scale_field: <field> (string or list) — runs the action once with the named
    service-data field(s) multiplied by data.presses. The BILRESA rotate actions use
    scale_field: brightness_step_pct, so a fast flick dims by step × notches in a
    single, race-free light.turn_on (repeated relative steps otherwise read stale
    brightness and don't accumulate). Scaling runs on a deep copy of the raw sequence,
    is type/sign preserving, leaves templates untouched, and never mutates the original.
  • repeat: <data-field> — runs the action's sequence N times (clamped, sequential).
    Better for discrete actions (scenes, counters, media next).

3. IKEA BILRESA blueprint (visual)

matter-ikea-bilresa-scroll-wheel.yaml (+ image). This is a visual blueprint: it
ships a transparent device photo with three positioned circular button regions (using
the blueprint circle shape), which the editor renders as clickable, colour-highlighted
markers (selected / pressed) — the same image-based style as the visual blueprints in
the repo, not the image-less list style.

The remote's three lower LEDs select one of three modes (pressing the lower part only
cycles the LED and emits no HA event); each mode exposes the same wheel gestures as
Matter generic-switch endpoints. The three buttons are the three LEDs (left = LED 1,
middle = LED 2, right = LED 3), each with six actions: press / press 2x / hold /
hold (released) / rotate right / rotate left. Matching uses template conditions on
data.endpoint / data.event_type.

Testing

Verified on a live BILRESA over Matter. Auto-discovery finds the device, and all nine
endpoints
(three LEDs × rotate right / rotate left / press) route to exactly the right
action across all three modes. Rotate dimming scales correctly — the debug log shows e.g.
Running scaled sequence (x7) for a fast flick, while press actions run unscaled.

Notes

  • The BILRESA endpoint→gesture numbering follows the common layout; if a unit differs,
    the monitor shows the real endpoint and the blueprint conditions can be adjusted.
  • hold / hold (released) are wired up but depend on the device emitting
    long_press / long_release; where unsupported those tabs are simply inactive.
  • The device image is 339×500 (within the 800×500 guideline); the three LED circle
    buttons are positioned over the LED dots and highlight on select/press.
  • If you'd rather review this in smaller pieces, I'm glad to split it into (a) the
    state_changed connection type, (b) the scale_field/repeat action options, and
    (c) the BILRESA blueprint.

Blueprint Checklist

  • You viewed the README and conformed to the naming conventions
  • You ordered the actions as stated in the README action order
  • All filenames are lowercase and uses '-' for spaces and not '_' while using {service-name}-{switch-name-or-type}.yaml format
  • Images are png
  • Image backgrounds are transparent and is cropped to the device boundaries
  • Images has a maximum width of 800px and maximum height of 500px
  • There are no missing buttons or actions
  • Your integration/service is running on the latest version
  • You have tested your blueprints and made sure each button and action works

Zigbee2MQTT

  • (older devices) You have ensured legacy is off/false for the device in the Z2M devices Settings (specific) page and that your actions matches those with legacy off?

N/A — this device is added over Matter, not Zigbee2MQTT.

tz8 and others added 10 commits August 11, 2026 20:25
Matter remotes (e.g. IKEA BILRESA scroll wheel) do not fire a dedicated bus
event like zha_event/deconz_event. They surface as Home Assistant event.*
entities, where a button press is a state_changed and the action lives in
new_state.attributes.event_type. Neither existing intake path (MQTT topic or
bus event matched by identifier_key) could handle this.

Adds a third, generic connection type keyed on `event_type: state_changed`:

- schema.py: BLUEPRINT_STATE_SCHEMA (optional `state_domain`, default `event`)
- __init__.py: blueprint schema dispatch recognises `state_changed`
- helpers.py: format_state_event() flattens a state_changed into
  {entity_id, device_id, endpoint, event_type, state, ...attrs};
  matter_endpoint_from_unique_id() derives the Matter endpoint
- models.py: Blueprint.is_state/state_domain; create_event_listeners() state
  branch — config path scopes async_track_state_change_event to the device's
  entities (identifier = HA device_id, resolved via the entity registry; an
  entity_id also works), discovery path listens to global state_changed
  filtered by domain. Identifier equality check skipped for state switches
  (already entity-scoped); never falls back to a global listener on the config
  path so an unresolved device can't become a catch-all.

The type is deliberately generic (any event.* entity) — no `matter` dependency
and no frontend changes; auto-discovery works via the existing mechanism.

Also adds blueprint matter-ikea-bilresa-scroll-wheel.yaml (one button, nine
Endpoint actions — the editor has no button selector without a background
image), and bumps the version to 4.4.0 so blueprints redeploy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now that a device image is present, rework the blueprint from the one-button /
nine-actions fallback into nine real buttons (the editor only offers a button
selector when a background image exists). The nine Matter endpoints are laid out
as a neutral 3x3 grid over the 474x700 remote image; endpoint-to-gesture mapping
is still TBD per unit, so buttons stay labelled by endpoint. Each button carries
the standard action set (press / press 2x / press 3x / hold / hold released) via
a shared YAML anchor.

- blueprints/matter-ikea-bilresa-scroll-wheel.png: device image (RGBA, transparent)
- blueprints/matter-ikea-bilresa-scroll-wheel.yaml: 9 buttons with grid coords

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rework the BILRESA blueprint to match how the device actually works: the three
LEDs on the lower part select one of three modes (pressing the lower part only
cycles the LED and emits no HA event), and per mode the wheel offers scroll
left / press / scroll right - nine generic-switch endpoints total.

Since Switch Manager buttons have no titles (they're identified only by their
position on the background image), the endpoint meaning is baked into the image:
the device photo plus a labelled 3x3 control matrix (rows = LED 1/2/3, columns =
scroll left / press / scroll right, cells = EP1..EP9). The nine button rects sit
on the matrix cells.

Actions are now gesture-specific: scroll cells have a single "Scroll" action
(fires per turn; notch count in data.totalNumberOfPressesCounted), press cells
have Press / Hold / Hold (released), mirroring the familiar Rodret layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Redesign per the device's real interaction model (matches the Rodret/e2201 UX in
Switch Manager): the three lower LEDs become three clickable circular buttons
(left = LED 1, middle = LED 2, right = LED 3), each highlighting blue on select /
press like any other switch.

Each LED button aggregates that mode's three Matter endpoints (rotate right /
rotate left / press) into six actions: Press, Press 2x, Hold, Hold (released),
Rotate right, Rotate left. Buttons and actions use template conditions on
data.endpoint / data.event_type to route each endpoint+event to exactly one
action (verified: no ambiguous or double matches; unknown endpoints ignored).

Restores the clean device photo as the background (the previous commit had
replaced it with a labelled composite) and places three circle buttons over the
LED dots.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Matter batches fast scrolling into a single multi_press_N event (N = notches,
capped at 8). format_state_event now derives an integer data.presses (parsed from
multi_press_N, else 1) so a rotate sequence can react proportionally, e.g.
`repeat: count: {{ data.presses }}` or `brightness_step_pct: {{ data.presses * 5 }}`.

Blueprint info documents the repeat pattern and notes that very fast turns can be
dropped by Matter below HA (not recoverable at this layer; smooth real-time scroll
would need the Matter-server MultiPressOngoing path).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…aling)

Blueprint actions can declare `repeat: <data-field>`; when set, Switch Manager
runs that action's sequence N times where N is the integer value of that field in
the event data (clamped 1..50, run sequentially so steps apply in order). The
user's action stays a single clean step defined in the visual editor - no
templates, no manual multi_press handling.

The BILRESA rotate actions use `repeat: presses`, so a batched scroll of N notches
(multi_press_N) runs the user's "-5%" step N times. Press/hold actions are
untouched (they don't declare repeat).

- schema.py: BLUEPRINT_ACTION_SCHEMA gains optional `repeat`
- models.py: BlueprintButtonAction.repeat; _processIncoming computes the count;
  ManagedSwitchConfigButtonAction.run loops sequentially
- blueprint: repeat: presses on all six rotate actions; info simplified

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Existing blueprints (e.g. zigbee2mqtt-ers-10tzbvk-aa-event, the Tuya smart knob)
use lowercase action titles. Align the BILRESA titles: press / press 2x / hold /
hold (released) / rotate right / rotate left. Titles are display-only (matching is
by condition, configs reference actions by index), so this is cosmetic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… in one call

New blueprint action property `scale_field` (string or list). When set, the matched
action runs ONCE with the named service-data field(s) multiplied by the event's
notch count (data.presses), built on the fly from a deep copy of the user's raw
sequence. This dims by (step * notches) in a single, race-free light.turn_on call
instead of many relative brightness_step_pct steps that a bulb can't accumulate.

The user's action stays a plain, template-free step (e.g. brightness_step_pct: 5) -
scaling is a blueprint concern, invisible in the visual editor.

- schema.py: BLUEPRINT_ACTION_SCHEMA gains optional `scale_field`
- models.py: scale_sequence_fields()/_scale_number() (recursive, type/sign
  preserving, templates untouched); BlueprintButtonAction.scale_field;
  ManagedSwitchConfigButtonAction._make_script() + run(scale=...); _processIncoming
  prefers scale_field over repeat
- blueprint: rotate actions now scale_field: brightness_step_pct (was repeat);
  info recommends queued mode for slow scrolling

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add Matter support (generic event-entity connection type) + IKEA BILRESA scroll wheel
The device image now conforms to the repo's 800x500 guideline (339x500). The
three LED circle buttons are repositioned to the new image space
(centers 124/148/173 x 336, r 10).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant