Skip to content

Instantly share code, notes, and snippets.

@CrazyCoder
Last active September 14, 2026 06:44
Show Gist options
  • Select an option

  • Save CrazyCoder/28d660d9e2e8464458e591ad79b3698e to your computer and use it in GitHub Desktop.

Select an option

Save CrazyCoder/28d660d9e2e8464458e591ad79b3698e to your computer and use it in GitHub Desktop.
Philips Hue Dimmer switch v2 (Zigbee2MQTT)
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
@CrazyCoder

CrazyCoder commented Sep 1, 2026

Copy link
Copy Markdown
Author

@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.

@MaJerle

MaJerle commented Sep 1, 2026

Copy link
Copy Markdown

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.

@finneus

finneus commented Sep 1, 2026

Copy link
Copy Markdown

thank you works again for me.

@CrazyCoder

Copy link
Copy Markdown
Author

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.

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