Zigbee2MQTT - Philips Hue Tap Dial Switch by SmartHomeGeeks.io

Integrate the Philips Hue Tap Dial Switch into Home Assistant to create a powerful remote control, supporting single tap, double tap, hold, and conditional rotate functionality. (Zigbee2MQTT version, deCONZ version here)

This blueprint originated from an automation example that received a lot of love from the Home Assistant community. Given its popularity, we chose to enhance it further and convert it into a user-friendly blueprint.

For more information, visit: SmartHomeGeeks.io

Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.

blueprint:
  name: Zigbee2MQTT - Philips Hue Tap Dial Switch by SmartHomeGeeks.io
  author: SmartHomeGeeks.io
  description: Integrate Philips Hue Tap Dial Switch into Home Assistant to create
    a powerful remote control, supporting single tap, double tap, hold, and conditional
    rotate functionality. <br><br> **For more information, visit:** [SmartHomeGeeks.io](https://smarthomegeeks.io/hue-tap-dial-switch/)
  domain: automation
  input:
    mqtt_topic:
      name: MQTT Topic
      description: Topic of the Philips Hue Tap Dial Switch
      default: zigbee2mqtt/<device name>
    tap_counter:
      name: Counter Helper
      description: Create a counter helper within Home Assistant to monitor the number
        of button presses on the switch, distinguishing between single and multiple
        clicks. This helper can be used for various switches, provided that they are
        not pressed at the same time
      selector:
        entity:
          domain:
          - counter
          multiple: false
    last_pressed:
      name: Last Pressed Input Text Helper
      description: Create an input text helper within Home Assistant to keep track
        of the last button pressed. This will facilitate implementing different rotation
        actions based on the last button pressed. **Each switch requires a unique
        helper!**
      selector:
        entity:
          domain:
          - input_text
          multiple: false
    button_1_single_press:
      name: Button 1 Single Press
      default: []
      selector:
        action: {}
    button_1_double_press:
      name: Button 1 Double Press
      default: []
      selector:
        action: {}
    button_1_hold:
      name: Button 1 Hold
      default: []
      selector:
        action: {}
    button_1_rotate_clockwise:
      name: Button 1 Rotate Clockwise
      default: []
      selector:
        action: {}
    button_1_rotate_counter_clockwise:
      name: Button 1 Rotate Counter-Clockwise
      default: []
      selector:
        action: {}
    button_2_single_press:
      name: Button 2 Single Press
      default: []
      selector:
        action: {}
    button_2_double_press:
      name: Button 2 Double Press
      default: []
      selector:
        action: {}
    button_2_hold:
      name: Button 2 Hold
      default: []
      selector:
        action: {}
    button_2_rotate_clockwise:
      name: Button 2 Rotate Clockwise
      default: []
      selector:
        action: {}
    button_2_rotate_counter_clockwise:
      name: Button 2 Rotate Counter-Clockwise
      default: []
      selector:
        action: {}
    button_3_single_press:
      name: Button 3 Single Press
      default: []
      selector:
        action: {}
    button_3_double_press:
      name: Button 3 Double Press
      default: []
      selector:
        action: {}
    button_3_hold:
      name: Button 3 Hold
      default: []
      selector:
        action: {}
    button_3_rotate_clockwise:
      name: Button 3 Rotate Clockwise
      default: []
      selector:
        action: {}
    button_3_rotate_counter_clockwise:
      name: Button 3 Rotate Counter-Clockwise
      default: []
      selector:
        action: {}
    button_4_single_press:
      name: Button 4 Single Press
      default: []
      selector:
        action: {}
    button_4_double_press:
      name: Button 4 Double Press
      default: []
      selector:
        action: {}
    button_4_hold:
      name: Button 4 Hold
      default: []
      selector:
        action: {}
    button_4_rotate_clockwise:
      name: Button 4 Rotate Clockwise
      default: []
      selector:
        action: {}
    button_4_rotate_counter_clockwise:
      name: Button 4 Rotate Counter-Clockwise
      default: []
      selector:
        action: {}
  source_url: https://community.home-assistant.io/t/zigbee2mqtt-philips-hue-tap-dial-switch-by-smarthomegeeks-io/696229
mode: parallel
max: 10
max_exceeded: silent
trigger:
- platform: mqtt
  topic: !input mqtt_topic
condition:
- condition: template
  value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action != '''' }}'
action:
- variables:
    input_text_var: !input last_pressed
    counter_var: !input tap_counter
- choose:
  - conditions:
    - condition: template
      value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action is match(''^button_[1-4]_press$'')
        }}'
    sequence:
    - if:
      - condition: template
        value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action | regex_replace(find=''[^1-4]'',
          replace='''') != states(input_text_var) }}'
      then:
      - service: counter.reset
        target:
          entity_id: '{{ counter_var }}'
    - service: input_text.set_value
      data:
        value: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action | regex_replace(find=''[^1-4]'', replace='''')
          }}'
      target:
        entity_id: '{{ input_text_var }}'
    - service: counter.increment
      target:
        entity_id: '{{ counter_var }}'
    - delay:
        seconds: 1
    - service: counter.reset
      target:
        entity_id: '{{ counter_var }}'
  - conditions:
    - condition: template
      value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action is match(''button_[1-4]_press_release'')
        and states(counter_var) > ''1'' }}'
    sequence:
    - choose:
      - conditions:
        - condition: template
          value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action == ''button_1_press_release''
            }}'
        sequence: !input button_1_double_press
      - conditions:
        - condition: template
          value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action == ''button_2_press_release''
            }}'
        sequence: !input button_2_double_press
      - conditions:
        - condition: template
          value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action == ''button_3_press_release''
            }}'
        sequence: !input button_3_double_press
      - conditions:
        - condition: template
          value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action == ''button_4_press_release''
            }}'
        sequence: !input button_4_double_press
  default:
  - if:
    - condition: template
      value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action is match(''button_[1-4]_press_release'')
        }}'
    then:
    - delay:
        milliseconds: 250
    - if:
      - condition: template
        value_template: '{{ states(counter_var) > ''1'' }}'
      then:
      - stop: Double tap detected
  - choose:
    - conditions:
      - condition: template
        value_template: '{{ states(input_text_var) == ''1'' and ''action'' in trigger.payload_json and trigger.payload_json.action
          is match(''^dial_rotate_'') }}'
      sequence:
      - choose:
        - conditions:
          - condition: template
            value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action is match(''^dial_rotate_left_'')
              }}'
          sequence: !input button_1_rotate_counter_clockwise
        - conditions:
          - condition: template
            value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action is match(''^dial_rotate_right_'')
              }}'
          sequence: !input button_1_rotate_clockwise
    - conditions:
      - condition: template
        value_template: '{{ states(input_text_var) == ''2'' and ''action'' in trigger.payload_json and trigger.payload_json.action
          is match(''^dial_rotate_'') }}'
      sequence:
      - choose:
        - conditions:
          - condition: template
            value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action is match(''^dial_rotate_left_'')
              }}'
          sequence: !input button_2_rotate_counter_clockwise
        - conditions:
          - condition: template
            value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action is match(''^dial_rotate_right_'')
              }}'
          sequence: !input button_2_rotate_clockwise
    - conditions:
      - condition: template
        value_template: '{{ states(input_text_var) == ''3'' and ''action'' in trigger.payload_json and trigger.payload_json.action
          is match(''^dial_rotate_'') }}'
      sequence:
      - choose:
        - conditions:
          - condition: template
            value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action is match(''^dial_rotate_left_'')
              }}'
          sequence: !input button_3_rotate_counter_clockwise
        - conditions:
          - condition: template
            value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action is match(''^dial_rotate_right_'')
              }}'
          sequence: !input button_3_rotate_clockwise
    - conditions:
      - condition: template
        value_template: '{{ states(input_text_var) == ''4'' and ''action'' in trigger.payload_json and trigger.payload_json.action
          is match(''^dial_rotate_'') }}'
      sequence:
      - choose:
        - conditions:
          - condition: template
            value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action is match(''^dial_rotate_left_'')
              }}'
          sequence: !input button_4_rotate_counter_clockwise
        - conditions:
          - condition: template
            value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action is match(''^dial_rotate_right_'')
              }}'
          sequence: !input button_4_rotate_clockwise
    - conditions:
      - condition: template
        value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action == ''button_1_press_release''
          }}'
      sequence: !input button_1_single_press
    - conditions:
      - condition: template
        value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action == ''button_1_hold'' }}'
      sequence: !input button_1_hold
    - conditions:
      - condition: template
        value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action == ''button_2_press_release''
          }}'
      sequence: !input button_2_single_press
    - conditions:
      - condition: template
        value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action == ''button_2_hold'' }}'
      sequence: !input button_2_hold
    - conditions:
      - condition: template
        value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action == ''button_3_press_release''
          }}'
      sequence: !input button_3_single_press
    - conditions:
      - condition: template
        value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action == ''button_3_hold'' }}'
      sequence: !input button_3_hold
    - conditions:
      - condition: template
        value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action == ''button_4_press_release''
          }}'
      sequence: !input button_4_single_press
    - conditions:
      - condition: template
        value_template: '{{ ''action'' in trigger.payload_json and trigger.payload_json.action == ''button_4_hold'' }}'
      sequence: !input button_4_hold

3 Likes

Hi @fleeman

If i use your new Version of the automation, based on mqtt and disable the homeassistant legacy_triggers (preparation for z2m 2.0), I got the following warning after a restart of HA in the HA log (for example):

Logger: homeassistant.helpers.template
Quelle: helpers/template.py:2748

Template variable warning: 'dict object' has no attribute 'action' when rendering '{{ trigger.payload_json.action is match('^button_[1-4]_press$') }}'
Template variable warning: 'dict object' has no attribute 'action' when rendering '{{ trigger.payload_json.action is match('button_[1-4]_press_release') and states(counter_var) > '1' }}'
Template variable warning: 'dict object' has no attribute 'action' when rendering '{{ trigger.payload_json.action is match('button_[1-4]_press_release') }}'
Template variable warning: 'dict object' has no attribute 'action' when rendering '{{ states(input_text_var) == '3' and trigger.payload_json.action is match('^dial_rotate_') }}'

It affects all template lines that contain trigger.payload_json.action

Edit:

Another user had a good tip, with the addition the warning messages no longer appear.

1 Like

Hi!
I’m a complete newbie to HA and z2m. But this is great and was simple to bring up.

However, I’d like to be able to change the white-light temperature of my bulb.
E.g.
Button 1 → lamp 1
Single tap: toggle on/off
Turn left/right: brightness
Double tap and then turn left/right: color temperature

I have a Philips Hue (Philips 929002294203 control via MQTT | Zigbee2MQTT) and an Ikea bulb (IKEA LED2201G8 control via MQTT | Zigbee2MQTT).

Any hints on how I could do that?

Hi. Thank you for this blueprint, it works fine with z2m 2.0. However, I find it significantly slower (lights turn on after 500ms) than another blueprint I’ve tried, which comes from Philips Hue Tap Dial Switch - Zigbee2MQTT

Any idea why ?

edit : I’ve read the blueprint and it seems delays are added to detect double tap. but double tap is not very practical on this switch. I will try to make a quicker version without double tap detection.

Thanks :wink:

Hi, thank you for doing this, its just what I needed. Any idea how I can edit this so it takes less rotations of the dial to control brightness? At the moment it takes around 2 full rotations to go from 0-100% brightness. I was hoping to do this in half a rotation. Thanks in advance

2 Likes

Hey!
Thanks for this bluprint
I succeeded to make buttons work but I don’t know how to dim light after press a button. Can someone help me please?
Thanks for your help!

You need to set the action for rotate, to turn lights on, and as data you specify the brightness_step_pct parameter.

I have this for my living room, 15% increase on clockwise

action: light.turn_on
metadata: {}
data:
  brightness_step_pct: 15
  transition: 1
target:
  entity_id: light.group_lights_living_room

and -15% on every counter clockwise:

action: light.turn_on
metadata: {}
data:
  brightness_step_pct: -15
  transition: 1
target:
  entity_id: light.group_lights_living_room

Hey thanks for your help! it works well

I’ve installed the blueprint. I’m not sure how or what I put in:

  • MQTT Topic
  • Counter Helper
  • Last Pressed Input Text Helper

In MQTT topic I tried the name of the light switch I just get "message malformed: missing input last_press, tap_counter when I try and save it.

For more information, visit: SmartHomeGeeks.io

2 Likes

Great, sorted thanks

This is working perfectly my lights On/Off, rotate to dim/brighten. Any idea how I get it to open a blind to varying degrees using the dial, please?

Same as brightness increase/decrease?

Yeah, I select the Front Blind the yaml looks nothing like the light one I copied

The light

action: light.turn_on
metadata: {}
data:
  brightness_step_pct: 15
  transition: 1
target:
  entity_id: light.living_room_downlights

The blind

type: turn_on
device_id: 7a6c9a7afa7e9070785d4afcfddb7648
entity_id: 9770ef14146dde9c5dbf363edf2722a9
domain: switch

I tried adding

data:
  brightness_step_pct: 15
  transition: 1

It didn’t like it

For switch you woudn’t use the light.turn_on operation (or switch.turn_on) but something similar.

Set cover position:

Like this?

action: cover.set_cover_position
metadata: {}
data:
  brightness_step_pct: 15
  transition: 1
target:
  entity_id: 5552d13295f11f98df875a244462a767

No, you have an example on the link I have provided. You have to set the position value.

Nevermind, some of this stuff is beyond my autistic brain.

I have these warnings too. But I don’t understand what causes it and how to fix it.
The blueprint hasn’t been updated to address this, or has it?

To be honest, I don’t know exactly. I had already linked a suggested solution from tilz0R in my post above, to which you replied.

Edit:
I have no longer had these messages in the log since the end of 2024.

I don’t use the Blueprint, but the automation offered by the guys, but the change is the same. Try the following template for the conditions for the triggers:

conditions:
  - condition: template
    value_template: >-
      {{ 'action' in trigger.payload_json and trigger.payload_json.action != ''
      }}

And don’t forget to restart the automations and template engine after the change.

1 Like