-
-
Save CrazyCoder/28d660d9e2e8464458e591ad79b3698e to your computer and use it in GitHub Desktop.
| blueprint: | |
| name: Philips Hue Dimmer switch v2 (Zigbee2MQTT) | |
| description: | |
| "Tested with Philips Hue Smart Wireless Dimmer Switch V2 (929002398602). | |
| \n\n | |
| Uses MQTT device triggers — select your dimmer from the device picker. | |
| \n\n | |
| After you pair a dimmer, press and release each of the four buttons, then hold and release each of them. | |
| Zigbee2MQTT publishes a device trigger to Home Assistant only after the device sends that action once, so this registers all 16 triggers. | |
| A short press sends `*_press` and `*_press_release`. A hold sends `*_press`, then `*_hold`, then `*_hold_release`. | |
| Home Assistant logs no error for a trigger that does not exist yet. | |
| \n\n | |
| To have different actions on short press and on hold (long press), use 'button release' (`*_press_release`) and 'button hold once' (`*_hold_once`) commands, | |
| as 'press' (`*_press`) will always trigger before 'button hold' (`*_hold`). | |
| \n\n | |
| When you hold a button, 'button hold' (`*_hold`) command is repeated roughly once per second. This may not work as desired with actions like toggling light/switch. | |
| If you want an action to run only once when the button hold action is registered, use 'button hold once' (`*_hold_once`). It's better than using 'button hold release' | |
| as the result can be observed before releasing the button after holding. | |
| \n\n | |
| As 'button hold' action is repeated by the device while you keep holding the button, it's a good way to run actions which increment / decrement some value (such as light dimming). | |
| To make it react as soon as the button is pressed, copy the same action to 'button press' (press action occurs immediately, while hold action occurs after a delay). | |
| \n\n | |
| Make sure to manually create a separate Text Helper per dimmer device and define its entity in the automation. It's used to store the last controller event to filter possible | |
| empty events and handle 'button hold once' actions. | |
| " | |
| domain: automation | |
| homeassistant: | |
| min_version: 2025.4.0 | |
| input: | |
| controller_device: | |
| name: Hue Dimmer Switch | |
| description: > | |
| Select the Hue Dimmer Switch v2 device. | |
| selector: | |
| device: | |
| filter: | |
| # Each Zigbee2MQTT version reports a different model string; delete one | |
| # and the picker goes empty for anyone still on that version. model_id | |
| # never changes, but that selector key needs HA 2025.4 (see min_version). | |
| - integration: mqtt | |
| manufacturer: Philips | |
| model_id: "929002398602" | |
| - integration: mqtt | |
| manufacturer: Philips | |
| model: Hue dimmer switch gen 2 | |
| - integration: mqtt | |
| manufacturer: Philips | |
| model: Hue dimmer switch | |
| - integration: mqtt | |
| manufacturer: Philips | |
| model: Hue dimmer switch (929002398602) | |
| helper_last_controller_event: | |
| name: (Required) Helper - Last Controller Event | |
| description: | |
| Input Text used to store the last event fired by the controller. | |
| You will need to manually create a text input Helper entity for this. | |
| default: "" | |
| selector: | |
| entity: | |
| domain: input_text | |
| multiple: false | |
| automation_mode: | |
| name: Automation mode | |
| description: >- | |
| Leave this alone unless an action in this automation runs for longer | |
| than about a second. The device sends several events for one gesture, | |
| about a second apart, and 'button hold' repeats about once per second, | |
| so with 'restart' a later event cancels the action that an earlier | |
| event started. A long action belongs in a script that you call with | |
| 'script.turn_on', which no mode can interrupt. | |
| default: queued | |
| selector: | |
| select: | |
| mode: dropdown | |
| options: | |
| - single | |
| - restart | |
| - queued | |
| - parallel | |
| on_button: | |
| name: ON Button | |
| icon: mdi:power | |
| collapsed: true | |
| input: | |
| on_press: | |
| name: ON button press | |
| description: Action to run | |
| default: [] | |
| selector: | |
| action: {} | |
| on_press_release: | |
| name: ON button release | |
| description: Action to run | |
| default: [] | |
| selector: | |
| action: {} | |
| on_hold: | |
| name: ON button hold | |
| description: Action to run (repeat while holding) | |
| default: [] | |
| selector: | |
| action: {} | |
| on_hold_once: | |
| name: ON button hold once | |
| description: Action to run only once (no repeat) | |
| default: [] | |
| selector: | |
| action: {} | |
| on_hold_release: | |
| name: ON button hold release | |
| description: Action to run | |
| default: [] | |
| selector: | |
| action: {} | |
| up_button: | |
| name: UP Button | |
| icon: mdi:arrow-up-bold | |
| collapsed: true | |
| input: | |
| up_press: | |
| name: UP button press | |
| description: Action to run | |
| default: [] | |
| selector: | |
| action: {} | |
| up_press_release: | |
| name: UP button release | |
| description: Action to run | |
| default: [] | |
| selector: | |
| action: {} | |
| up_hold: | |
| name: UP button hold | |
| description: Action to run (repeat while holding) | |
| default: [] | |
| selector: | |
| action: {} | |
| up_hold_once: | |
| name: UP button hold once | |
| description: Action to run only once (no repeat) | |
| default: [] | |
| selector: | |
| action: {} | |
| up_hold_release: | |
| name: UP button hold release | |
| description: Action to run | |
| default: [] | |
| selector: | |
| action: {} | |
| down_button: | |
| name: DOWN Button | |
| icon: mdi:arrow-down-bold | |
| collapsed: true | |
| input: | |
| down_press: | |
| name: DOWN button press | |
| description: Action to run | |
| default: [] | |
| selector: | |
| action: {} | |
| down_press_release: | |
| name: DOWN button release | |
| description: Action to run | |
| default: [] | |
| selector: | |
| action: {} | |
| down_hold: | |
| name: DOWN button hold | |
| description: Action to run (repeat while holding) | |
| default: [] | |
| selector: | |
| action: {} | |
| down_hold_once: | |
| name: DOWN button hold once | |
| description: Action to run only once (no repeat) | |
| default: [] | |
| selector: | |
| action: {} | |
| down_hold_release: | |
| name: DOWN button hold release | |
| description: Action to run | |
| default: [] | |
| selector: | |
| action: {} | |
| off_button: | |
| name: OFF/HUE Button | |
| icon: mdi:power-off | |
| collapsed: true | |
| input: | |
| off_press: | |
| name: OFF/HUE button press | |
| description: Action to run | |
| default: [] | |
| selector: | |
| action: {} | |
| off_press_release: | |
| name: OFF/HUE button release | |
| description: Action to run | |
| default: [] | |
| selector: | |
| action: {} | |
| off_hold: | |
| name: OFF/HUE button hold | |
| description: Action to run (repeat while holding) | |
| default: [] | |
| selector: | |
| action: {} | |
| off_hold_once: | |
| name: OFF/HUE button hold once | |
| description: Action to run only once (no repeat) | |
| default: [] | |
| selector: | |
| action: {} | |
| off_hold_release: | |
| name: OFF/HUE button hold release | |
| description: Action to run | |
| default: [] | |
| selector: | |
| action: {} | |
| source_url: "https://gist.github.com/CrazyCoder/28d660d9e2e8464458e591ad79b3698e" | |
| mode: !input automation_mode | |
| max_exceeded: silent | |
| trigger: | |
| - trigger: device | |
| id: on_press | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: on_press | |
| - trigger: device | |
| id: on_hold | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: on_hold | |
| - trigger: device | |
| id: on_press_release | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: on_press_release | |
| - trigger: device | |
| id: on_hold_release | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: on_hold_release | |
| - trigger: device | |
| id: up_press | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: up_press | |
| - trigger: device | |
| id: up_hold | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: up_hold | |
| - trigger: device | |
| id: up_press_release | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: up_press_release | |
| - trigger: device | |
| id: up_hold_release | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: up_hold_release | |
| - trigger: device | |
| id: down_press | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: down_press | |
| - trigger: device | |
| id: down_hold | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: down_hold | |
| - trigger: device | |
| id: down_press_release | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: down_press_release | |
| - trigger: device | |
| id: down_hold_release | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: down_hold_release | |
| - trigger: device | |
| id: off_press | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: off_press | |
| - trigger: device | |
| id: off_hold | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: off_hold | |
| - trigger: device | |
| id: off_press_release | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: off_press_release | |
| - trigger: device | |
| id: off_hold_release | |
| domain: mqtt | |
| device_id: !input controller_device | |
| type: action | |
| subtype: off_hold_release | |
| action: | |
| - variables: | |
| helper_last_controller_event: !input helper_last_controller_event | |
| command: "{{ trigger.id }}" | |
| prev_command: "{{ states(helper_last_controller_event) }}" | |
| - service: input_text.set_value | |
| data: | |
| entity_id: !input helper_last_controller_event | |
| value: "{{ command }}" | |
| - choose: | |
| - conditions: | |
| - condition: trigger | |
| id: on_press | |
| sequence: !input on_press | |
| - conditions: | |
| - condition: trigger | |
| id: on_press_release | |
| sequence: !input on_press_release | |
| - conditions: | |
| - condition: trigger | |
| id: on_hold | |
| sequence: !input on_hold | |
| - conditions: | |
| - condition: trigger | |
| id: on_hold_release | |
| sequence: !input on_hold_release | |
| - conditions: | |
| - condition: trigger | |
| id: up_press | |
| sequence: !input up_press | |
| - conditions: | |
| - condition: trigger | |
| id: up_press_release | |
| sequence: !input up_press_release | |
| - conditions: | |
| - condition: trigger | |
| id: up_hold | |
| sequence: !input up_hold | |
| - conditions: | |
| - condition: trigger | |
| id: up_hold_release | |
| sequence: !input up_hold_release | |
| - conditions: | |
| - condition: trigger | |
| id: down_press | |
| sequence: !input down_press | |
| - conditions: | |
| - condition: trigger | |
| id: down_press_release | |
| sequence: !input down_press_release | |
| - conditions: | |
| - condition: trigger | |
| id: down_hold | |
| sequence: !input down_hold | |
| - conditions: | |
| - condition: trigger | |
| id: down_hold_release | |
| sequence: !input down_hold_release | |
| - conditions: | |
| - condition: trigger | |
| id: off_press | |
| sequence: !input off_press | |
| - conditions: | |
| - condition: trigger | |
| id: off_press_release | |
| sequence: !input off_press_release | |
| - conditions: | |
| - condition: trigger | |
| id: off_hold | |
| sequence: !input off_hold | |
| - conditions: | |
| - condition: trigger | |
| id: off_hold_release | |
| sequence: !input off_hold_release | |
| - choose: | |
| - conditions: | |
| - '{{ command == "on_hold" and prev_command == "on_press" }}' | |
| sequence: !input on_hold_once | |
| - conditions: | |
| - '{{ command == "up_hold" and prev_command == "up_press" }}' | |
| sequence: !input up_hold_once | |
| - conditions: | |
| - '{{ command == "down_hold" and prev_command == "down_press" }}' | |
| sequence: !input down_hold_once | |
| - conditions: | |
| - '{{ command == "off_hold" and prev_command == "off_press" }}' | |
| sequence: !input off_hold_once |
@MaJerle I am keeping device triggers in this blueprint. Your attribute names are correct, and an entity reference does survive a device replacement. Event entities are not available on a default Zigbee2MQTT install.
Zigbee2MQTT creates them only when homeassistant.experimental_event_entities is true. That setting is false by default:
"experimental_event_entities": {
"type": "boolean",
"description": "... The events and attributes are currently deemed experimental and subject to change.",
"default": false
}That block is in lib/util/settings.schema.json in 2.13.0. lib/extension/homeassistant.ts reads the flag before it adds the event discovery entry.
My own install runs 2.13.0 with the default Home Assistant settings. The topic homeassistant/event/# holds zero retained messages, and the entity registry contains no MQTT event entity. A blueprint that selects an event entity shows an empty picker for every user who did not set the flag.
Zigbee2MQTT marked the flag experimental in 1.41.0 (November 2024), and the description still says the attributes can change. The repository contains no deprecation notice for device triggers.
Two details in the code you posted. mode: restart cancels a running action when the next *_hold arrives, and the device repeats *_hold about once per second, so this blueprint uses queued. The rewrite also drops the per-button collapsed sections, which turns the editor into one long list.
I will publish an event-entity variant if the flag becomes the default.
Fair in regards to the experimential features. Let's see once they become fixed indeed. I have them enabled.
- I didn't modify the mode from last release I just modified the MQTT topic to event as a trigger
- device trigger is not obsolete, it is something HA specific, but it is hard to change the device if one breaks.
thank you works again for me.
The blueprint now has an automation_mode input. The default is queued, which is what the blueprint used before, so an existing automation keeps its behaviour.
Change it only if an action in one branch runs for longer than about a second. One gesture produces several events, and *_hold repeats about once per second. With restart a later event cancels the action that an earlier event started.
For an action that must outlive the gesture, do not rely on the mode at all. Put the action in a script and call it with action: script.turn_on and a target:. Home Assistant runs it in its own task, outside the caller's cancellation scope, so no mode can cancel it. script.turn_off still stops it when you want to abort.
I measured the failure on a different button that follows the same pattern. A blocking action: script.x started at 08:14:51 and was cancelled at 08:14:52 by the following event. The script never reached its delay: 10s step, and Home Assistant logged no error.
Home Assistant substitutes blueprint inputs before it validates the schema, so mode: !input automation_mode is valid.
The filter was wrong for anyone on Zigbee2MQTT 2.9.2 or newer, including me. The gist is fixed now.
Zigbee2MQTT renamed this device twice. The Home Assistant device selector compares the model string exactly, and the comparison is case-sensitive with no substring match.
modelHue dimmer switch (929002398602)Hue dimmer switchHue dimmer switch gen 2The old filter matched the middle row only. Zigbee2MQTT 2.0.0 moved the product code into a separate
model_idfield (PR #25389). zigbee-herdsman-converters PR #11748 added the generation suffix, and Zigbee2MQTT 2.9.2 shipped it first.@n1kunjp your value is correct for current Zigbee2MQTT. The blueprint now lists all three strings. Filter blocks are OR'd, so one match is enough. It also matches on
model_id: 929002398602, which does not change when the description changes. That key needs Home Assistant 2025.4 or later, so the blueprint now setsmin_version: 2025.4.0.Existing automations are not affected. The filter builds the picker list only. Home Assistant stores the device id in the automation and never compares it against the filter again. My own six automations kept working while the picker was empty, which is why I did not catch this before I published.
After you pair a dimmer, run both gestures on all four buttons:
A short press sends
pressandpress_release. A hold sendspress, thenholdabout once per second, thenhold_release. Zigbee2MQTT publishes the discovery topic for a device trigger only after it sees that action once. Both gestures are needed to register all 16 triggers. Home Assistant logs no error for a trigger that does not exist yet, and it subscribes when the topic arrives.