Skip to content

Multistate actions proposal - #380

Open
RealByron wants to merge 6 commits into
romasku:mainfrom
RealByron:multistate_pb
Open

Multistate actions proposal#380
RealByron wants to merge 6 commits into
romasku:mainfrom
RealByron:multistate_pb

Conversation

@RealByron

Copy link
Copy Markdown
Contributor

Multi-press action reporting

Summary

Replaces the previous action-dispatch system (which hardcoded relay/binding actions per press count) with pure reporting: the firmware detects how many times a button was pressed and reports it as a multistate value. All automation logic lives in Z2M/ZHA.

This makes multi-press fully generic — supporting triple press (or more) requires zero firmware changes, just setting max_press_count = 3 via ZCL.

New multistate values:

Value Event Notes
2 long_press N=1 hold — backward compatible
5 single_press timer_confirm fired, N=1
6 single_release released after hold, N=1
7 double_press timer_confirm fired, N=2
8 double_hold timer_hold fired, N=2
9 double_release released after hold, N=2
10 triple_press N=3 (needs max_press_count=3)
formula: 3n+1 / 3n+2 / 3n+3 for n≥2

New ZCL attributes (OnOff Switch Config cluster, per endpoint):

  • 0xFF06 confirm_release_ms — window after release before confirming press count (default 200 ms)
  • 0xFF07 max_press_count — maximum press count to detect (default 2, set to 3 for triple-press)

Both attributes are persisted in NVM and survive reboots.

Backward compatibility: relay_mode, binded_mode, and the legacy long_press (value 2) are fully preserved.

Changes

  • src/zigbee/switch_cluster.c — generic MULTISTATE_N_* formula, dynamic multistate_num_of_states, removed action-dispatch system
  • src/zigbee/switch_cluster.h — removed 6 action fields, reduced attr_infos[16][10]
  • src/zigbee/consts.h — removed SWITCH_ACTION_* enum and ACTION_N_* attribute IDs
  • src/device_config/config_parser.cI handler uses ordered counter for switch indicator assignment; relay_clusters assignment guarded by relay_clusters_cnt > 0
  • device_db.yamlREMOTE_MOES_SWITCH_TS0044 config_str updated (S tokens + BTC5)
  • helper_scripts/templates/switch_custom.js.jinja — updated pressAction lookup (up to triple press), added confirmReleaseMs and maxPressCount converter functions
  • tests/ — removed action-dispatch tests, added single_press, double_press, triple_press multistate value tests

@RealByron

Copy link
Copy Markdown
Contributor Author

WIP, need some tests on real device (TBD soon)
But we can discuss on the general idea

@romasku

romasku commented Mar 28, 2026

Copy link
Copy Markdown
Owner

Hi,
Thank you for the PR! I didn't do a full review, but from what I saw, it looks very nice.

I have some questions:

  • Am I right that after N presses, multistate will be in MULTISTATE_N_PRESS(N) indefinitely, and will never return to MULTISTATE_RELEASED? It seems a bit wrong to me.
  • Why do you call switch_cluster_flash_indicator in switch_cluster_binding_action_on/switch_cluster_binding_action_off/...? I thought calling it from a switch_cluster_on_button_press should be enough.

@andrei-lazarov

Copy link
Copy Markdown
Collaborator

If we rework this, we should definitely switch to the cleaner mqtt events (actions), like all other remotes from Z2M.
The current action sensor is confusing for users. We should drop it and warn users about the breaking change.

image image

Not sure how it would affect the firmware, this is mostly about the converter / UX.

There would be one 'action' field per device, so the possible values would include the gang (1,2,3,4) corresponding to the button pressed.

  • single press: n_initial (press), n_single (release + confirm period)
  • single long press: n_initial (press), n_hold (reached long press duration), n_long (released)
  • double press: n_initial (press), n_double (release after second press + confirm period)
  • triple press: n_initial (press), n_triple (release after third press)

Double/triple initial-press and hold are overkill imo, but I'm not totally against them..

And for rocker switches it would be just n_on and n_off.

We must also keep in mind what happens if multiple buttons are pressed at once.
I expect 1_initial, 2_initial, 1_single, 2_single in quick succession.

@RealByron

RealByron commented Mar 28, 2026

Copy link
Copy Markdown
Contributor Author

Hi, Thank you for the PR! I didn't do a full review, but from what I saw, it looks very nice.

I have some questions:

  • Am I right that after N presses, multistate will be in MULTISTATE_N_PRESS(N) indefinitely, and will never return to MULTISTATE_RELEASED? It seems a bit wrong to me.

I add a test for that, at last release (passing 200 ms timeout) only the the max bp action will be fired

  • Why do you call switch_cluster_flash_indicator in switch_cluster_binding_action_on/switch_cluster_binding_action_off/...? I thought calling it from a switch_cluster_on_button_press should be enough.

I though that a flash led on bp event should be nice, to see that event are fired. But we can simplify.

@RealByron

RealByron commented Mar 28, 2026

Copy link
Copy Markdown
Contributor Author

If we rework this, we should definitely switch to the cleaner mqtt events (actions), like all other remotes from Z2M. The current action sensor is confusing for users. We should drop it and warn users about the breaking change.

image image
Not sure how it would affect the firmware, this is mostly about the converter / UX.

There would be one 'action' field per device, so the possible values would include the gang (1,2,3,4) corresponding to the button pressed.

  • single press: n_initial (press), n_single (release + confirm period)
  • single long press: n_initial (press), n_hold (reached long press duration), n_long (released)
  • double press: n_initial (press), n_double (release after second press + confirm period)
  • triple press: n_initial (press), n_triple (release after third press)

fully agreed with that but I was not able to deduce impact for users... Too risky for me as I'm not very familiar with usage of this firmware.

@RealByron

Copy link
Copy Markdown
Contributor Author

We must also keep in mind what happens if multiple buttons are pressed at once.
I expect 1_initial, 2_initial, 1_single, 2_single in quick succession.

I just added a test for that

@Grz3hu

Grz3hu commented May 31, 2026

Copy link
Copy Markdown

Hello, any progress on this? Would love to see this feature :)

@neneonline

neneonline commented Jul 17, 2026

Copy link
Copy Markdown

Picking up @andrei-lazarov's suggestion earlier in this thread about publishing button presses as a Z2M action — I'd like to kindly double down on it.

It might feel like a semantic matter, since the heavy lifting is already there thanks to @romasku's genMultistateInput handling. But action is where the ecosystem has been moving: Z2M's own Home Assistant guide now recommends MQTT device triggers for button presses and has deprecated the older action-sensor approach in favour of them, and both Z2M and ZHA represent presses as events/triggers. On top of that, an action gives a more ingrained way to use HA triggers today — HA auto-generates the "when this button is pressed…" device triggers from it (no
hand-written YAML), and it plugs these switches into the existing button/remote blueprints. IMHO it would be the right call.

I know that dropping press_action outright would break things for a lot of people. So instead of replacing it, let me suggest keeping both for a while — a planned phase-out: add the action now, keep press_action working, mark it deprecated and call it out in the release notes (and on the entity itself), then remove it a few releases down the line once
people have migrated. Nothing breaks today, and there's a clear runway.

If you guys are up for it, I already have a tested implementation (real hardware, both publishing side by side with no interference) that I'd be glad to PR.

neneonline added a commit to neneonline/tuya-zigbee-switch that referenced this pull request Jul 19, 2026
Firmware compiled on GitHub Actions; committing artifacts here so the OTA
index URLs resolve (the workflow's own push-back was blocked by the fork's
read-only Actions token).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@neneonline

Copy link
Copy Markdown

Following up on my comment above — I've now tested this on real hardware and wanted to share results.

I built firmware from @RealByron's work in this PR (with a few tweaks) and flashed it onto two real BSEED TS0726 switches — a 4-gang (EC-GL86ZPCS41) and a 1-gang (EC-GL86ZPCS11) — then validated end to end:

  • Gestures decode correctly on-device. Single / double / long all map cleanly from genMultistateInput presentValue (5 / 7 / 2, plus 8 for double-hold), on every gang of the 4-gang and on the 1-gang.
  • @andrei-lazarov's action suggestion is in and working. Presses publish both as a Z2M action and as Home Assistant MQTT device triggers (homeassistant/device_automation/<ieee>/action_switch_single_hold/config), so they show up as first-class triggers in the HA automation UI rather than the confusing action sensor.
  • Non-regressive by default. max_press_count defaults to 1 → single + long only, reported instantly on release with no confirmation wait; users opt in to 2/3 for double/triple, which is the only path that adds the confirm_release_ms delay. So anyone flashing keeps today's behaviour with no latency surprise, and multi-press is purely additive.

On the Z2M side I kept press_action alongside the new action (so existing setups don't break) and discriminate the two firmwares on numberOfStates; a couple of converter fixes were needed to get z-h-c to accept the definition (e.presets.action(...) and isModernExtend: true).

It's all on a branch off this PR's work if any of it is useful: https://github.com/neneonline/tuya-zigbee-switch/tree/feat/multigesture-fixes

Happy to open a PR against main, or to split it up however is least disruptive to @RealByron's PR — whatever you all prefer. Thanks @romasku for the genMultistateInput groundwork and @RealByron for the multi-press work; this was mostly a matter of proving it on real devices and wiring up the action output.

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.

5 participants