This blueprint is evolved from @gTunes blueprint. It has since been altered by others both to keep it working and for full customization instead of binding three buttons to media controls. The only thing I’ve done is to update the template syntax to the new syntax and translated some comments to english.
The blueprint supports controlling a media device and additional custom automations with a Philips Hue Tap Dial control. It is specific to Z2M.
Features of this blueprint:
-
Simple selection of a single media device to be controlled
-
User-specifiable volume steps - if your physical device uses a volume range other than 1-100, you can specify that range and this automation will scale incremental steps appropriately
-
The controller generates three rotation speed events : step, slow, and fast. This blueprint allows you to specify how many volume increments should be applied for each speed. You may, for example, set “step” to 1, slow to “3”, and fast to “5”. Because some volume ranges can result in rounding or truncation issues downstream of the automation, the blueprint allows these steps to be set as decimal values such as “1.5”
-
Both Short press and long press of all 4 buttons are configurable. Do whatever you like with them.
blueprint:
# Media control via Philips Hue Tap Dial - FULL CUSTOM BUTTONS
#
# Works only with Home Assistant and Zigbee2MQTT
#
# Version History:
# - v 0.1 dec 2023 : Initial version from Matt Rusiniak
# - v 0.2 nov 2025 : full custom buttons (short & long)
# - v 0.3 jan 2026 : translations, plus added automation domain
# - v 0.4 jan 2026 : updated trigger to new syntax
# Adapted form earlier work by:
# - https://gist.github.com/f0ff886f/58642a648e2e50a32c7d028f40e9cd6e Matt Rusiniak
#
# Based on earlier work by:
# - https://community.home-assistant.io/t/z2m-ikea-symfonisk-gen2-e2123-media-control-v1-53/559523 Shawsky
# - https://gist.github.com/erkr/a437ebcb98a2b5ba2deebabd02f5ffae Eric Kreuwels
# - https://gist.github.com/alexwmaustin/2c25cfa1a0ade1ab9fc1ef0940289672 Alex Austin
# - https://gist.github.com/LempK/0aee4ad2b3b5646665d0dc987e354dab
name: Philips Hue Tap Dial Media Control (Full Custom Buttons) - Z2M
description:
Control a media_player with the Hue Tap Dial wheel via Zigbee2MQTT
(volume with the dial) and define fully customized actions
for the 4 buttons, in short and long press.
domain: automation
input:
remote:
name: Remote
description: The Philips Hue Tap Dial to use
selector:
device:
filter:
integration: mqtt
manufacturer: Philips
model: Hue Tap dial switch
multiple: false
base_topic:
name: Zigbee2MQTT Base mqtt topic
description: The base topic configured in Zigbee2MQTT. If you haven't changed this, leave the default here ("zigbee2mqtt")
default: zigbee2mqtt
media_player:
name: Media Player
description: The media player to control with this automation
selector:
entity:
domain: media_player
multiple: false
volume_steps:
name: Volume number of steps
description: Controls the volume scale. Set this to the number of steps supported by your playback device
default: 100
selector:
number:
min: 5
max: 100
step: 1
unit_of_measurement: "Num"
mode: slider
step_rotation_increments:
name: '"Step" rotation volume increments'
description: Number of volume increments for a "step" rotation event
default: 1.0
selector:
number:
min: 0.1
max: 5.0
step: 0.1
unit_of_measurement: "Num"
mode: slider
slow_rotation_increments:
name: '"Slow" rotation volume increments'
description: Number of volume increments for a "slow" rotation event
default: 2
selector:
number:
min: 0.1
max: 5.0
step: 0.1
unit_of_measurement: "Num"
mode: slider
fast_rotation_increments:
name: '"Fast" rotation volume increments'
description: Number of volume increments for a "fast" rotation event
default: 3
selector:
number:
min: 0.1
max: 5.0
step: 0.1
unit_of_measurement: "Num"
mode: slider
button_1_short:
name: Button 1 (Short)
description: Action to run on a short press of Button 1
default: []
selector:
action: {}
button_1_hold:
name: Button 1 (Long)
description: Action to run on a long press of Button 1
default: []
selector:
action: {}
button_2_short:
name: Button 2 (Short)
description: Action to run on a short press of Button 2
default: []
selector:
action: {}
button_2_hold:
name: Button 2 (Long)
description: Action to run on a long press of Button 2
default: []
selector:
action: {}
button_3_short:
name: Button 3 (Short)
description: Action to run on a short press of Button 3
default: []
selector:
action: {}
button_3_hold:
name: Button 3 (Long)
description: Action to run on a long press of Button 3
default: []
selector:
action: {}
button_4_short:
name: Button 4 (Short)
description: Action to run on a short press of Button 4
default: []
selector:
action: {}
button_4_hold:
name: Button 4 (Long)
description: Action to run on a long press of Button 4
default: []
selector:
action: {}
mode: restart
max_exceeded: silent
trigger_variables:
base_topic: !input base_topic
controller: !input remote
triggers:
- trigger: mqtt
topic: "{{ base_topic }}/+/action"
action:
- variables:
controllertopic: "{{ base_topic }}/{{ device_attr(controller, 'name') }}/action"
player: !input media_player
steps: !input volume_steps
stepsize: "{{ 1.0 / steps }}"
stepmultiplier: {
"dial_rotate_right_step": !input step_rotation_increments,
"dial_rotate_left_step": !input step_rotation_increments,
"dial_rotate_right_slow": !input slow_rotation_increments,
"dial_rotate_left_slow": !input slow_rotation_increments,
"dial_rotate_right_fast": !input fast_rotation_increments,
"dial_rotate_left_fast": !input fast_rotation_increments
}
- choose:
- conditions:
- "{{ trigger.payload != '' }}"
- "{{ trigger.topic == controllertopic }}"
sequence:
- choose:
# ----- VOLUME (Wheel) -----
# Rotate Right (Volume Up)
- conditions: "{{ trigger.payload in ['dial_rotate_right_step', 'dial_rotate_right_slow', 'dial_rotate_right_fast'] }}"
sequence:
- service: media_player.volume_set
target:
entity_id: "{{ player }}"
data:
volume_level: >-
{% set volume = state_attr(player, "volume_level") + (stepsize * stepmultiplier[trigger.payload]) %}
{{ 1.0 if volume > 1.0 else volume }}
# Rotate Left (Volume Down)
- conditions: "{{ trigger.payload in ['dial_rotate_left_step', 'dial_rotate_left_slow', 'dial_rotate_left_fast'] }}"
sequence:
- service: media_player.volume_set
target:
entity_id: "{{ player }}"
data:
volume_level: >-
{% set volume = state_attr(player, "volume_level") - (stepsize * stepmultiplier[trigger.payload]) %}
{{ 0.0 if volume < 0.0 else volume }}
# ----- BUTTTONS : SHORT PRESS -----
- conditions: "{{ trigger.payload == 'button_1_press_release' }}"
sequence: !input button_1_short
- conditions: "{{ trigger.payload == 'button_2_press_release' }}"
sequence: !input button_2_short
- conditions: "{{ trigger.payload == 'button_3_press_release' }}"
sequence: !input button_3_short
- conditions: "{{ trigger.payload == 'button_4_press_release' }}"
sequence: !input button_4_short
# ----- BUTTONS : LONG PRESS -----
- conditions: "{{ trigger.payload == 'button_1_hold' }}"
sequence: !input button_1_hold
- conditions: "{{ trigger.payload == 'button_2_hold' }}"
sequence: !input button_2_hold
- conditions: "{{ trigger.payload == 'button_3_hold' }}"
sequence: !input button_3_hold
- conditions: "{{ trigger.payload == 'button_4_hold' }}"
sequence: !input button_4_hold
Setup:
Select your device, adjust your MQTT base topic if necessary, the choose the media_player to control.
Choose volume steps, increments, etc. and configure the custom sequences as desired.
Thanks to the creators of previous blueprints from which this is derived. They’re credited in the blueprint.

