Skip to content

Instantly share code, notes, and snippets.

@f0ff886f
Last active April 27, 2025 19:23
Show Gist options
  • Save f0ff886f/58642a648e2e50a32c7d028f40e9cd6e to your computer and use it in GitHub Desktop.
Save f0ff886f/58642a648e2e50a32c7d028f40e9cd6e to your computer and use it in GitHub Desktop.
blueprint:
# Media control via Philips Hue Tap Dial by gTunes
#
# Works only with Home Assistant and Zigbee2MQTT
#
# Version History:
# - v 0.1 Dec 2023 : Initial version
#
# 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
name: Z2M - Philips Hue Tap Dial Media Control
description:
'Control the selected media player (and any joined to it) with a Philips Hue Tap Dial controller via Zigbee2MQTT (Z2M)
Supports volume control by rotating the dial
A single press of Button 1 is Toggle Play / Pause
Single presses of Buttons 3 and 4 are Previous Track and Next Track
Single press of button 2 is configurable as are long presses of all buttons'
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_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_hold:
name: Button 3 (Long)
description: Action to run on a long press of Button 3
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
trigger:
- platform: 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:
## Commands
# Play / Pause
- conditions: "{{ trigger.payload == 'button_1_press_release' }}"
sequence:
- service: media_player.media_play_pause
entity_id: !input 'media_player'
# Next Track
- conditions: "{{ trigger.payload == 'button_4_press_release' }}"
sequence:
- service: media_player.media_next_track
entity_id: !input 'media_player'
# Previous Track
- conditions: "{{ trigger.payload == 'button_3_press_release' }}"
sequence:
- service: media_player.media_previous_track
entity_id: !input 'media_player'
# 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 }}
## Actions
# Button 1 Long Press
- conditions: "{{ trigger.payload == 'button_1_hold' }}"
sequence: !input button_1_hold
# Button 2 Short Press
- conditions: "{{ trigger.payload == 'button_2_press_release' }}"
sequence: !input button_2_short
# Button 2 Long Press
- conditions: "{{ trigger.payload == 'button_2_hold' }}"
sequence: !input button_2_hold
# Button 3 Long Press
- conditions: "{{ trigger.payload == 'button_3_hold' }}"
sequence: !input button_3_hold
# Button 4 Long Press
- conditions: "{{ trigger.payload == 'button_4_hold' }}"
sequence: !input button_4_hold
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment