Philips Tap Dial Switch with double tap and 4 dial actions

Integrate Philips Hue Tap Dial Switch into Home Assistant to create a powerful remote control, supporting single tap, double tap, hold, and (4 different) rotate functionality.

Here is our complete blog on how to integrate the tap dial switch in Home Assistant: Hue Tap Dial Switch: Unleash the ultimate Home Assistant switch

Since this project has garnered significant attention and is widely used, we’ve transformed the automation into a straightforward and user-friendly blueprint. Enjoy!

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

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

10 Likes

Hello @fleeman Congratulations on the great blog! I love the way you control the Tap Dial. I’d like to do something similar, but I don’t think it’s possible without being in developer mode. I’m using Z2M, and I haven’t seen any mention of double-click. I’d like to be able to control both single-click and double-click, and have the wheel handle the device associated with the button, just like you do. But I don’t think it’s possible with Z2M. If you have more information, I’d appreciate it. I’ll be receiving Button
this weekend.

That is not an issue, the automation and helpers take care of double click detection and different dial options. If the button presses work, my solution will work too.

1 Like

What should I put in place of the Deconz event types? I’m looking to transform them, but as I can see that you have a higher level of knowledge than I do, if you have already set up automation in Z2M, I would appreciate it. I have already created the two helpers. After that, you mentioned that the button allows you to switch between Hue scenes or the amplifier source. How do you do that? I don’t quite understand that part either. Thank you again for sharing; it’s great. If the double click function works, it really adds a lot to this button

Unfortunately, I do not use Z2M, but it should be pretty easy as all you need to do is replace the deCONZ events with the respectable Z2M events.

I started modifying your code. Single click and hold functions work well, but I’m having trouble modifying the triggers for rotate and double-click. Essentially, I have this MQTT path and actions that I want to use: “zigbee2mqtt/hue_tap_dial_switch/action”:

  • dial_rotate_right_fast
  • dial_rotate_right_step
  • dial_rotate_right_slow
  • dial_rotate_left_fast
  • dial_rotate_left_step
  • dial_rotate_left_slow
  • button_1_press_release
  • button_2_press_release
  • button_3_press_release
  • button_4_press_release
  • button_1_hold
  • button_2_hold
  • button_3_hold
  • button_4_hold.

If someone could help me finish, I would appreciate it because I’m a bit stuck right now.

alias: Philips Hue Dial Switch Z2M
description: ""
trigger:
  - platform: mqtt
    topic: zigbee2mqtt/hue_tap_dial_switch/action
condition: []
action:
  - choose:
      - conditions:
          - condition: template
            value_template: "{% set s = trigger.event.data.event | string %} {{ s[-1] == \"0\" }}"
            alias: Update Last Pressed
        sequence:
          - if:
              - condition: template
                value_template: >-
                  {% set s = trigger.event.data.event | string %} {{ s[0] !=
                  states('input_text.philips_hue_dial_switch_last_pressed') }}
            then:
              - service: counter.reset
                data: {}
                target:
                  entity_id: counter.philips_hue_dial_switch_button_counter
            alias: Prevent different buttons to be registered as double press
          - service: input_text.set_value
            data:
              value: "{% set s = trigger.event.data.event | string %} {{ s[0] }}"
            target:
              entity_id: input_text.philips_hue_dial_switch_last_pressed
            alias: Update the input text helper with the last pressed button number
          - service: counter.increment
            data: {}
            target:
              entity_id: counter.philips_hue_dial_switch_button_counter
            alias: Increase the counter helper with 1
          - delay:
              hours: 0
              minutes: 0
              seconds: 1
              milliseconds: 0
          - service: counter.reset
            data: {}
            target:
              entity_id: counter.philips_hue_dial_switch_button_counter
            alias: Reset the counter helper
      - conditions:
          - condition: template
            value_template: "{% set s = trigger.event.data.event | string %} {{ s[-1] == \"2\" }}"
            alias: Double xxx2 event
          - condition: numeric_state
            entity_id: counter.philips_hue_dial_switch_button_counter
            above: 1
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: "{{ trigger.payload == 'button_1_press_release' }}"
                    alias: Double press button 1
                sequence: 
                  - service: light.turn_on
                    target:
                      entity_id: light.table
              - conditions:
                  - condition: template
                    value_template: "{{ trigger.payload == 'button_2_press_release' }}"
                    alias: Double press button 2
                sequence: 
                  - service: light.turn_off
                    target:
                      entity_id: light.table
              - conditions:
                  - condition: template
                    value_template: "{{ trigger.payload == 'button_3_press_release' }}"
                    alias: Double press button 3
                sequence: []
              - conditions:
                  - condition: template
                    value_template: "{{ trigger.payload == 'button_4_press_release' }}"
                    alias: Double press button 4
                sequence: []
    default:
      - if:
          - condition: template
            value_template: "{% set s = trigger.event.data.event | string %} {{ s[-1] == \"2\" }}"
        then:
          - delay:
              hours: 0
              minutes: 0
              seconds: 0
              milliseconds: 250
          - if:
              - condition: numeric_state
                entity_id: counter.philips_hue_dial_switch_button_counter
                above: 1
            then:
              - stop: Double tap detected
        alias: Single xxx2 event
      - service: counter.reset
        data: {}
        target:
          entity_id: counter.philips_hue_dial_switch_button_counter
        enabled: true
      - choose:
          - conditions:
              - condition: trigger
                id:
                  - rotation
                alias: Rotation after button 1 was pressed
              - condition: state
                entity_id: input_text.philips_hue_dial_switch_last_pressed
                state: "1"
            sequence: []
          - conditions:
              - condition: trigger
                id:
                  - rotation
                alias: Rotation after button 2 was pressed
              - condition: state
                entity_id: input_text.philips_hue_dial_switch_last_pressed
                state: "2"
            sequence: []
          - conditions:
              - condition: trigger
                id:
                  - rotation
                alias: Rotation after button 3 was pressed
              - condition: state
                entity_id: input_text.philips_hue_dial_switch_last_pressed
                state: "3"
            sequence: []
          - conditions:
              - condition: trigger
                id:
                  - rotation
                alias: Rotation after button 4 was pressed
              - condition: state
                entity_id: input_text.philips_hue_dial_switch_last_pressed
                state: "4"
            sequence: []
          - conditions:
              - condition: template
                value_template: "{{ trigger.payload == 'button_1_press_release' }}"
                alias: Button 1 Single Click
            sequence: 
              - service: light.turn_on
                target:
                  entity_id: light.hue_derriere_ampli_salon
          - conditions:
              - condition: template
                value_template: "{{ trigger.payload == 'button_1_hold' }}"
                alias: Button 1 Hold
            sequence: 
              - service: light.turn_off
                target:
                  entity_id: light.hue_derriere_ampli_salon
          - conditions:
              - condition: template
                value_template: "{{ trigger.payload == 'button_2_press_release' }}"
                alias: Button 2 Single Click
            sequence: []
          - conditions:
              - condition: template
                value_template: "{{ trigger.payload == 'button_2_hold_release' }}"
                alias: Button 2 Hold
            sequence: []
          - conditions:
              - condition: template
                value_template: "{{ trigger.payload == 'button_3_press_release' }}"
                alias: Button 3 Single Click
            sequence: []
          - conditions:
              - condition: template
                value_template: "{{ trigger.payload == 'button_3_hold_release' }}"
                alias: Button 3 Hold
            sequence: []
          - conditions:
              - condition: template
                value_template: "{{ trigger.payload == 'button_4_press_release' }}"
                alias: Button 4 Single Click
            sequence: []
          - conditions:
              - condition: template
                value_template: "{{ trigger.payload == 'button_4_hold_release' }}"
                alias: Button 4 Hold
            sequence: []
        enabled: true
mode: parallel
max: 10

Well this is just awesome - thank you for the blog post! I’m a newcomer to HA but a veteran developer, and this is just the right depth for me to start digging into more complex automations.

The post says it should work with any Zigbee integration that can detect the device, but between this thread’s Z2M users and my own experience with ZHA, that should include the big caveat that event data structure is quite different between the three Zigbee integration options. The LOGIC is identical (probably) but the triggers and conditions all seem to require modification.

I’m trying to figure this out myself with the events debug log… but the examples of other integrations I see on github all seem to use more semantic identifiers and are generally more straightforward that what I find myself coming up against here. Am I approaching this wrong? Help and tips are welcome.

Based on the events debug log, here are the differences I’ve noted from the code sample in the blog post:

  1. s/deconz_/zha_.
  2. Both rotation and button events in ZHA are emitted in zha_events. I can’t find mention of any other event types in ZHA, and zha_relative_rotary_event is empty.
  3. Identifier for the device is different. All events from the switch come in with device_id=f1d484eecfd7ae66406d537f38c66ebf. Is that unique to this individual button? I can’t find anything else in the debug log that is a consistent identifier across all interactions from the device.
  4. Identifier for button presses vs rotation is different. looks like command: recall vs command: step_with_on_off is the way to do it.
  5. The 4 buttons are identified by the scene_id parameter: 1, 0, 5, or 4, respectively for buttons 1, 2, 3, 4.
  6. Long press triggers an event that looks like a rotation, with command: step_with_on_off and the same params structure as rotations.

For reference, here’s what the different kinds of events look like in my debug log:

Press button 1:

event_type: zha_event
data:
  device_ieee: 00:17:88:01:0d:35:9c:f1
  unique_id: 00:17:88:01:0d:35:9c:f1:1:0x0005
  device_id: f1d484eecfd7ae66406d537f38c66ebf
  endpoint_id: 1
  cluster_id: 5
  command: recall
  args:
    - 15983
    - 1
  params:
    group_id: 15983
    scene_id: 1
    transition_time: null
origin: LOCAL
time_fired: "2023-10-29T10:05:00.634827+00:00"
context:
  id: 01HDXE9PTTG42NXBBCC3SNE3BC
  parent_id: null
  user_id: null

Rotation:

event_type: zha_event
data:
  device_ieee: 00:17:88:01:0d:35:9c:f1
  unique_id: 00:17:88:01:0d:35:9c:f1:1:0x0008
  device_id: f1d484eecfd7ae66406d537f38c66ebf
  endpoint_id: 1
  cluster_id: 8
  command: step_with_on_off
  args:
    - 0
    - 8
    - 4
  params:
    step_mode: 0
    step_size: 8
    transition_time: 4
origin: LOCAL
time_fired: "2023-10-29T10:05:19.303376+00:00"
context:
  id: 01HDXEA927VW3HQHMVNGAV2MHS
  parent_id: null
  user_id: null

Long press button 1:

event_type: zha_event
data:
  device_ieee: 00:17:88:01:0d:35:9c:f1
  unique_id: 00:17:88:01:0d:35:9c:f1:1:0x0008
  device_id: f1d484eecfd7ae66406d537f38c66ebf
  endpoint_id: 1
  cluster_id: 8
  command: step_with_on_off
  args:
    - 1
    - 255
    - 8
  params:
    step_mode: 1
    step_size: 255
    transition_time: 8
origin: LOCAL
time_fired: "2023-10-29T10:05:58.847051+00:00"
context:
  id: 01HDXEBFNZVMTPPFXT01JJJ7TG
  parent_id: null
  user_id: null

This idea of the structure of events is corroborated by this blueprint: https://github.com/nnmalex/ha-blueprints/blob/24507e90e89ea6471f5f3ecaa98ba91aef77c445/philips_tap_dial_switch.yaml (which works fine for me). Dammit. I don’t see any way from the “button hold” event to tell which button was held. They’re all the same. Does this mean I have to contribute to GitHub - zigpy/zha-device-handlers: ZHA device handlers bridge the functionality gap created when manufacturers deviate from the ZCL specification, handling deviations and exceptions by parsing custom messages to and from Zigbee devices. ?

So I separated the automations into two for now. One handles the buttons, and I only consider the “press release” event for the first click, as otherwise, I get both “press” and “press release” events. The other handles the “hold” action. The other automation only takes the “dial” actions to separate the three settings: brightness step slow and fast. It’s working well, although it’s not optimized, but I’m taking it step by step. I’d like to add the double-click, so I think I would need to create an automation that doesn’t trigger the other one with a single click, but it’s not easy. If you have any ideas, please let me know.

alias: Philips Hue Dial Switch - Actions de Rotation
description: Gérer les actions de rotation du dial
trigger:
  - platform: mqtt
    topic: zigbee2mqtt/hue_tap_dial_switch/action
condition:
  - condition: template
    value_template: >-
      {{ trigger.payload in ['dial_rotate_right_fast', 'dial_rotate_right_step',
      'dial_rotate_right_slow', 'dial_rotate_left_fast',
      'dial_rotate_left_step', 'dial_rotate_left_slow'] }}
action:
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload in ['dial_rotate_right_fast'] }}"
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '1' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.allcentris
                    data:
                      brightness_step: 80
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '2' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.hue_color_lamp_1
                    data:
                      brightness_step: 80
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '3' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.table
                    data:
                      brightness_step: 80
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '4' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.allhue
                    data:
                      brightness_step: 80
                      transition: 1
            default:
              - service: light.turn_on
                target:
                  entity_id: all
                data:
                  brightness_step: 80
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload in ['dial_rotate_right_step'] }}"
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '1' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.allcentris
                    data:
                      brightness_step: 5
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '2' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.hue_color_lamp_1
                    data:
                      brightness_step: 5
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '3' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.table
                    data:
                      brightness_step: 5
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '4' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.allhue
                    data:
                      brightness_step: 5
                      transition: 1
            default:
              - service: light.turn_on
                target:
                  entity_id: all
                data:
                  brightness_step: 5
                  transition: 1
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload in ['dial_rotate_right_slow'] }}"
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '1' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.allcentris
                    data:
                      brightness_step: 20
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '2' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.hue_color_lamp_1
                    data:
                      brightness_step: 20
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '3' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.table
                    data:
                      brightness_step: 20
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '4' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.allhue
                    data:
                      brightness_step: 20
                      transition: 1
            default:
              - service: light.turn_on
                target:
                  entity_id: all
                data:
                  brightness_step: 20
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload in ['dial_rotate_left_fast'] }}"
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '1' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.allcentris
                    data:
                      brightness_step: -80
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '2' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.hue_color_lamp_1
                    data:
                      brightness_step: -80
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '3' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.table
                    data:
                      brightness_step: -80
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '4' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.allhue
                    data:
                      brightness_step: -80
                      transition: 1
            default:
              - service: light.turn_on
                target:
                  entity_id: all
                data:
                  brightness_step: -80
                  transition: 1
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload in ['dial_rotate_left_step'] }}"
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '1' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.allcentris
                    data:
                      brightness_step: -5
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '2' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.hue_color_lamp_1
                    data:
                      brightness_step: -5
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '3' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.table
                    data:
                      brightness_step: -5
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '4' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.allhue
                    data:
                      brightness_step: -5
                      transition: 1
            default:
              - service: light.turn_on
                target:
                  entity_id: all
                data:
                  brightness_step: -5
                  transition: 1
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload in ['dial_rotate_left_slow'] }}"
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '1' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.allcentris
                    data:
                      brightness_step: -20
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '2' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.hue_color_lamp_1
                    data:
                      brightness_step: -20
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '3' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.table
                    data:
                      brightness_step: -20
                      transition: 1
              - conditions:
                  - condition: template
                    value_template: >-
                      {{
                      states('input_text.philips_hue_dial_switch_last_pressed')
                      == '4' }}
                sequence:
                  - service: light.turn_on
                    target:
                      entity_id: light.allhue
                    data:
                      brightness_step: -20
                      transition: 1
            default:
              - service: light.turn_on
                target:
                  entity_id: all
                data:
                  brightness_step: -20
                  transition: 1
mode: parallel
max: 10

alias: Philips Hue Dial Switch juste bouton
description: Update last pressed button
trigger:
  - platform: mqtt
    topic: zigbee2mqtt/hue_tap_dial_switch/action
condition:
  - condition: template
    value_template: "{{ 'dial' not in trigger.payload }}"
action:
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'button_1_press_release' }}"
            alias: Button 1 Press/Release
        sequence:
          - service: light.toggle
            target:
              entity_id: light.allcentris
            data: {}
          - service: input_text.set_value
            data:
              value: "1"
            target:
              entity_id: input_text.philips_hue_dial_switch_last_pressed
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'button_1_hold' }}"
            alias: Button 1 Hold
        sequence:
          - service: scene.turn_on
            target:
              entity_id: scene.salon_lumiere_naturelle
            data: {}
          - service: input_text.set_value
            data:
              value: "1"
            target:
              entity_id: input_text.philips_hue_dial_switch_last_pressed
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'button_2_press_release' }}"
            alias: Button 2 Press/Release
        sequence:
          - service: light.toggle
            target:
              entity_id: light.hue_color_lamp_1
            data: {}
          - service: input_text.set_value
            data:
              value: "2"
            target:
              entity_id: input_text.philips_hue_dial_switch_last_pressed
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'button_2_hold' }}"
            alias: Button 2 Hold
        sequence:
          - service: scene.turn_on
            target:
              entity_id: scene.salon_concentration
            data: {}
          - service: input_text.set_value
            data:
              value: "2"
            target:
              entity_id: input_text.philips_hue_dial_switch_last_pressed
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'button_3_press_release' }}"
            alias: Button 3 Press/Release
        sequence:
          - service: light.toggle
            target:
              entity_id: light.table
            data: {}
          - service: input_text.set_value
            data:
              value: "3"
            target:
              entity_id: input_text.philips_hue_dial_switch_last_pressed
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'button_3_hold' }}"
            alias: Button 3 Hold
        sequence:
          - service: scene.turn_on
            target:
              entity_id: scene.salon_hocus_pocus
            data: {}
          - service: input_text.set_value
            data:
              value: "3"
            target:
              entity_id: input_text.philips_hue_dial_switch_last_pressed
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'button_4_press_release' }}"
            alias: Button 4 Press/Release
        sequence:
          - service: light.toggle
            target:
              entity_id: light.allhue
            data: {}
          - condition: template
            value_template: "{{ is_state('light.centris_centre', 'on') }}"
          - service: input_text.set_value
            data:
              value: "4"
            target:
              entity_id: input_text.philips_hue_dial_switch_last_pressed
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'button_4_hold' }}"
            alias: Button 4 Hold
        sequence:
          - service: scene.turn_on
            target:
              entity_id: scene.salon_festive_fun
            data: {}
          - service: input_text.set_value
            data:
              value: "4"
            target:
              entity_id: input_text.philips_hue_dial_switch_last_pressed
mode: parallel
max: 10

I can’t figure out the double press, and the single one doesn’t work with this code. I can’t find where my error is. If someone could give me advice on where the error might be, I have error logs for data values every time it runs, and the code stops even though my counter is incrementing, and the input text corresponds to the value changes, but it won’t execute the actions. Thank you.

alias: Philips Hue Dial Switch double action 2
description: ""
trigger:
  - platform: mqtt
    topic: zigbee2mqtt/hue_tap_dial_switch/action
    alias: Button
    id: button
condition: []
action:
  - choose:
      - conditions:
          - condition: template
            value_template: >-
              {{ trigger.payload in
              ['button_1_press_release','button_2_press_release','button_3_press_release','button_4_press_release','button_1_hold',
              'button_2_hold' ,'button_3_hold' , 'button_4_hold'] }}
        alias: Update Last Pressed
        sequence:
          - if:
              - condition: template
                value_template: >-
                  {% set s = trigger.payload.split('_')[1] %} {{ s !=
                  states('input_text.philips_hue_dial_switch_last_pressed') }}
            then:
              - service: counter.reset
                data: {}
                target:
                  entity_id: counter.philips_hue_dial_switch_button_counter
            alias: Prevent different buttons to be registered as double press
          - service: input_text.set_value
            data:
              #value: 1
              value: "{% set s = trigger.payload.split('_')[1] %} {{ 's' }}"
            target:
              entity_id: input_text.philips_hue_dial_switch_last_pressed
            alias: Update the input text helper with the last pressed button number
          - service: counter.increment
            data: {}
            target:
              entity_id: counter.philips_hue_dial_switch_button_counter
            alias: Increase the counter helper with 1
          - delay:
              hours: 0
              minutes: 0
              seconds: 0
              milliseconds: 500
      - conditions:
          - condition: template
            value_template: >-
              {{ trigger.payload in ['button_1_press_release',
              'button_2_press_release','button_3_press_release',
              'button_4_press_release', 'button_1_hold', 'button_2_hold' ,
              'button_3_hold' , 'button_4_hold'] }}
          - condition: numeric_state
            entity_id: counter.philips_hue_dial_switch_button_counter
            above: 1
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: "{{ 'button_1_press_release' == trigger.payload }}"
                    alias: Double press button 1
                sequence:
                  - service: light.toggle
                    target:
                      entity_id: light.hue_go_1
                    data: {}
              - conditions:
                  - condition: template
                    value_template: "{{ 'button_2_press_release' == trigger.payload }}"
                    alias: Double press button 2
                sequence:
                  - service: light.toggle
                    target:
                      entity_id: light.hue_go_2
                    data: {}
              - conditions:
                  - condition: template
                    value_template: "{{ 'button_3_press_release' == trigger.payload }}"
                    alias: Double press button 3
                sequence:
                  - service: light.toggle
                    target:
                      entity_id: light.buffet
                    data: {}
              - conditions:
                  - condition: template
                    value_template: "{{ 'button_4_press_release' == trigger.payload }}"
                    alias: Double press button 4
                sequence:
                  - service: light.toggle
                    target:
                      entity_id: light.hue_derriere_ampli_salon
                    data: {}
      - conditions:
          - condition: template
            value_template: "{{ 'button_1_press_release' == trigger.payload }}"
        sequence:
          - service: light.toggle
            target:
              entity_id: light.allcentris
            data: {}
      - conditions:
          - condition: template
            value_template: "{{ 'button_2_press_release' == trigger.payload }}"
        sequence:
          - service: light.toggle
            target:
              entity_id: light.hue_color_lamp_1
            data: {}
      - conditions:
          - condition: template
            value_template: "{{ 'button_3_press_release' == trigger.payload }}"
        sequence:
          - service: light.toggle
            target:
              entity_id: light.table
            data: {}
      - conditions:
          - condition: template
            value_template: "{{ 'button_4_press_release' == trigger.payload }}"
        sequence:
          - service: light.toggle
            target:
              entity_id: light.allhue
            data: {}
  - service: counter.reset
    target:
      entity_id: counter.philips_hue_dial_switch_button_counter
    data: {}
  - service: input_text.set_value
    data: {}
mode: parallel
max: 10

This morning, I went shopping and bought a Home Assistant SkyConnect and installed Zigbee2MQTT aside Phoscon specifically to rewrite this automation for all you Z2M users. I’ve also updated the blog post on smarthomegeeks.io. Enjoy:

alias: Philips Hue Dial Switch (Zigbee2MQTT)
description: ""
trigger:
  - alias: Button
    platform: event
    event_data:
      entity_id: sensor.hue_tap_dial_switch_action
    event_type: state_changed
    id: button
condition:
  - condition: template
    value_template: "{{ trigger.event.data.new_state.state is match('^(dial_rotate_|button_[1-4]_(press|hold))') and trigger.event.data.new_state.state != '' }}"
action:
  - choose:
      - conditions:
          - alias: Update Last Pressed
            condition: template
            value_template: "{{ trigger.event.data.new_state.state is match('^button_[1-4]_press$') }}"
        sequence:
          - alias: Prevent different buttons to be registered as double press
            if:
              - condition: template
                value_template: "{{ trigger.event.data.new_state.state | regex_replace(find='[^1-4]', replace='') != states('input_text.philips_hue_dial_switch_last_pressed') }}"
            then:
              - service: counter.reset
                data: {}
                target:
                  entity_id: counter.philips_hue_dial_switch_button_counter
                enabled: true
            enabled: true
          - service: input_text.set_value
            data:
              value: "{{ trigger.event.data.new_state.state | regex_replace(find='[^1-4]', replace='') }}"
            target:
              entity_id: input_text.philips_hue_dial_switch_last_pressed
            alias: Update the input text helper with the last pressed button number
          - service: counter.increment
            data: {}
            target:
              entity_id: counter.philips_hue_dial_switch_button_counter
            alias: Increase the counter helper with 1
          - delay:
              hours: 0
              minutes: 0
              seconds: 1
              milliseconds: 0
          - service: counter.reset
            data: {}
            target:
              entity_id: counter.philips_hue_dial_switch_button_counter
            alias: Reset the counter helper
            enabled: true
      - conditions:
          - alias: Double xxx2 event
            condition: template
            value_template: "{{ trigger.event.data.new_state.state is match('button_[1-4]_press_release') }}"
          - condition: numeric_state
            entity_id: counter.philips_hue_dial_switch_button_counter
            above: 1
        sequence:
          - choose:
              - conditions:
                  - alias: Double press button 1
                    condition: template
                    value_template: "{{ trigger.event.data.new_state.state == 'button_1_press_release' }}"
                sequence: []
              - conditions:
                  - alias: Double press button 2
                    condition: template
                    value_template: "{{ trigger.event.data.new_state.state == 'button_2_press_release' }}"
                sequence: []
              - conditions:
                  - alias: Double press button 3
                    condition: template
                    value_template: "{{ trigger.event.data.new_state.state == 'button_3_press_release' }}"
                sequence: []
              - conditions:
                  - alias: Double press button 4
                    condition: template
                    value_template: "{{ trigger.event.data.new_state.state == 'button_4_press_release' }}"
                sequence: []
    default:
      - alias: Single xxx2 event
        if:
          - condition: template
            value_template: "{{ trigger.event.data.new_state.state is match('button_[1-4]_press_release') }}"
        then:
          - delay:
              hours: 0
              minutes: 0
              seconds: 0
              milliseconds: 250
          - if:
              - condition: numeric_state
                entity_id: counter.philips_hue_dial_switch_button_counter
                above: 1
            then:
              - stop: Double tap detected
      - service: counter.reset
        data: {}
        target:
          entity_id: counter.philips_hue_dial_switch_button_counter
        enabled: true
      - choose:
          - conditions:
              - condition: state
                entity_id: input_text.philips_hue_dial_switch_last_pressed
                state: "1"
              - condition: template
                value_template: "{{ trigger.event.data.new_state.state is match('^dial_rotate_') }}"
            sequence:
              - choose:
                  - conditions:
                      - condition: template
                        value_template: "{{ trigger.event.data.new_state.state is match('^dial_rotate_left') }}"
                    sequence: []
                  - conditions:
                      - condition: template
                        value_template: "{{ trigger.event.data.new_state.state is match('^dial_rotate_right') }}"
                    sequence: []
          - conditions:
              - condition: state
                entity_id: input_text.philips_hue_dial_switch_last_pressed
                state: "2"
              - condition: template
                value_template: "{{ trigger.event.data.new_state.state is match('^dial_rotate_') }}"
            sequence:
              - choose:
                  - conditions:
                      - condition: template
                        value_template: "{{ trigger.event.data.new_state.state is match('^dial_rotate_left') }}"
                    sequence: []
                  - conditions:
                      - condition: template
                        value_template: "{{ trigger.event.data.new_state.state is match('^dial_rotate_right') }}"
                    sequence: []
          - conditions:
              - condition: state
                entity_id: input_text.philips_hue_dial_switch_last_pressed
                state: "3"
              - condition: template
                value_template: "{{ trigger.event.data.new_state.state is match('^dial_rotate_') }}"
            sequence:
              - choose:
                  - conditions:
                      - condition: template
                        value_template: "{{ trigger.event.data.new_state.state is match('^dial_rotate_left') }}"
                    sequence: []
                  - conditions:
                      - condition: template
                        value_template: "{{ trigger.event.data.new_state.state is match('^dial_rotate_right') }}"
                    sequence: []
          - conditions:
              - condition: state
                entity_id: input_text.philips_hue_dial_switch_last_pressed
                state: "4"
              - condition: template
                value_template: "{{ trigger.event.data.new_state.state is match('^dial_rotate_') }}"
            sequence:
              - choose:
                  - conditions:
                      - condition: template
                        value_template: "{{ trigger.event.data.new_state.state is match('^dial_rotate_left') }}"
                    sequence: []
                  - conditions:
                      - condition: template
                        value_template: "{{ trigger.event.data.new_state.state is match('^dial_rotate_right') }}"
                    sequence: []
          - conditions:
              - alias: Button 1 Single Click
                condition: template
                value_template: "{{ trigger.event.data.new_state.state == 'button_1_press_release' }}"
            sequence: []
          - conditions:
              - alias: Button 1 Hold
                condition: template
                value_template: "{{ trigger.event.data.new_state.state == 'button_1_hold' }}"
            sequence: []
          - conditions:
              - alias: Button 2 Single Click
                condition: template
                value_template: "{{ trigger.event.data.new_state.state == 'button_2_press_release' }}"
            sequence: []
          - conditions:
              - alias: Button 2 Hold
                condition: template
                value_template: "{{ trigger.event.data.new_state.state == 'button_2_hold' }}"
            sequence: []
          - conditions:
              - alias: Button 3 Single Click
                condition: template
                value_template: "{{ trigger.event.data.new_state.state == 'button_3_press_release' }}"
            sequence: []
          - conditions:
              - alias: Button 3 Hold
                condition: template
                value_template: "{{ trigger.event.data.new_state.state == 'button_3_hold' }}"
            sequence: []
          - conditions:
              - alias: Button 4 Single Click
                condition: template
                value_template: "{{ trigger.event.data.new_state.state == 'button_4_press_release' }}"
            sequence: []
          - conditions:
              - alias: Button 4 Hold
                condition: template
                value_template: "{{ trigger.event.data.new_state.state == 'button_4_hold' }}"
            sequence: []
        enabled: true
mode: parallel
max: 10
3 Likes

Thank you very much. I had an issue with my direct MQTT trigger, but it works better when passing through the Home Assistant sensor. In any case, thank you very much. However, I think there might be an issue with the “hold” functionality because it seems to filter it out and only lets the “release” events pass through, if I’m not mistaken. The double click works great, though. Thanks again, you’re amazing!

I think you need to make this small correction to the trigger. With this, everything works perfectly for me

    value_template: >-
      {{ trigger.event.data.new_state.state is match('^(dial_rotate_|button_[1-4]_(press|hold))') and
      trigger.event.data.new_state.state != '' }}
1 Like

Thanks for the feedback, I’ve corrected the automation.

1 Like

Thank you to you especially.

For the light adjustment, what parameters did you set to make it smooth? I have Hue lights controlled by the Hue Bridge, and it works fine. I kept my three speed settings in my dial code, but I might switch to just one speed if I find a value that suits me

Within the Phoscon automation, I do it like this:

              - service: light.turn_on
                data:
                  brightness_step: >
                    {% set adjustment = trigger.event.data.rotation / 2 | round() %} {% set current = state_attr('light.lamp','brightness') %} {% if current + adjustment < 1 -%}
                      {{ 1 - current }}
                    {%- else -%}
                      {{  adjustment }}
                    {%- endif %}

I haven’t looked at the Z2M event. If it has a rotation value, you could use it to do the same.

Looks like you can use “Action time”:
image

1 Like

Thank you, I’ll test this during the day

1 Like

@ohthehugemanatee ohthehugemanatee Your post is almost a month old but I wanted to reply because I’m currently implementing ZHA automations for the Tap Dial Switch. There are ZHA blueprints available for this device and those work fine. My issue right now is that ZHA support for this device is limited. For example long press buttons are not available right now (see below). I also wanted to provide some feedback on your questions.

That is correct.

No.

Correct.

That is also correct. There is currently only 1 long press trigger which is the same for all buttons. This seems to be a ZHA problem. I have create a bug report for it here:
https://github.com/home-assistant/core/issues/104491

@starob thank you for replying! I had a busy month and couldn’t get back to it until now, so your post was timely even a month after my OP. :slight_smile:

I gather we have better working blueprints for Z2M. But all my other devices are already working through ZHA and AFAIK I can only use one or the other… and I don’t think I have it in me to completely redo all my devices just for the sake of the tap dial switch haha. Please correct me if I’m wrong!

Anyway it turned out to be pretty simple to take care of the different signals from the device. I added a step to map the values to a human-readable “button_pressed” local variable:

  - variables:
      map_buttons:
        "0": 2
        "1": 1
        "4": 4
        "5": 3
      pushed_button: "{{ map_buttons.get( trigger.event.data.params.scene_id | string, 0) }}"

Great: now I can refer to the pushed_button variable for an integer which is directly comparable to the number variable I have stored.

So now I have the capabilities I need for single and double press, at least. But the logic is confusing to me. How SHOULD this work? Here’s my faux code understanding… but this does not map to what I see in the example.

if (input_number.button_last_pressed == pushed_button && counter.button_counter > 0) {
  // This is a double press. 
  counter.button_counter++
  // Switch on pushed_button value for action.
  // stop execution
}
if (counter.button_counter == 0) {
  // This is a single press. 
  sleep 1
  if (counter.button_counter > 1) {
    // A second press happened in a parallel thread. Let the action happen there.
    // stop execution
  } 
  if (counter.button_counter == 1) {
    // no double press happened in a parallel thread.
    // Switch on pushed_button value for action.
    // stop execution
  } 

Thanks for the continued help!

You can run ZHA and Zigbee2MQTT in parallel if you have a second coordinator (e.g. Zigbee USB stick). Since I had a spare Zigbee stick available I did this to check the support of Z2M for this device and it is far better than ZHA. It supports every possible short & long press etc. Also the implementation to update the firmware of zigbee devices is much better in my opinion.

Since I have ZHA and Z2M running in parallel I might migrate to Z2M over time or just keep it running until ZHA improves the support for this device and then remove Z2M again.

I highly recommend transitioning to Z2M as it offers compatibility with a broader range of devices and more attributes per device. In my own setup, I’m using two coordinators to gradually migrate from deconz to Z2M. This approach allows me to switch one device at a time, ensuring my smart home remains functional during the transition. Investing in an additional Zigbee stick is worth the expense for a smoother changeover.

1 Like

That’s interesting advice… I have read elsewhere that ZHA is usually recommended because it has the weight of homeassistant’s community behind it and is easier to work with in the GUI. I’ve really appreciated how all my lights “just worked” so far. My only complaint is sometimes messages aren’t received by all devices… which I assume is a limitation of the zigbee protocol .

I sat down to think this through more in fake code, and came up with a simpler and more flexible version.

This version

  • only needs one global variable, a string that records the whole sequence
  • is (I think) easier for users to add custom behaviors, because there’s just one place for all your actions. New actions are as easy as e.g. if button_sequence == "222"
  • lets you respond to any combination of buttons and rotations. Want to respond to “1224, then fast left turn, then 321, then slow right turn”? You can do that.
// mock code only

GLOBAL button_sequence string = ""

function button_is_pushed(button_pushed string) {
  // No matter what, add to the sequence string

  // Separate rotation codes with a space.
  if (button_pushed == "R" || button_pushed == "L") {
    rotation = true;
  }
  if (rotation) {
    button_sequence = button_sequence + " ";
  }
  button_sequence = button_sequence + button_pushed;
  // Separate rotation codes with a space.
  if (rotation) {
    button_sequence = button_sequence + " ";
  }
  // Track the state of the button sequence locally to this run.
  local_button_sequence = button_sequence;

  
  // Wait 1 second and see if any further pushes have modified the sequence.
  sleep 1
  if (local_button_sequence != button_sequence) {
    // Another parallel run added to the button sequence. Let execution happen there.
    exit 0;
  }
  // Parse the string into an array for convenience.
  sequence_array = string_split(local_button_sequence, " ");

  /**
  * All your actions go here. 
  * You can use either the string version of the sequence (e.g. "1224 R34"),
  * or the array is easier when rotations are involved. (e.g. [ "1224", "R34"] )
  */
  // Example single tap.
  if (local_button_sequence == "1") {
    scene_trigger("living_room_overhead_light_on");
  } 
  // Example double tap.
  if (local_button_sequence == "11") {
    scene_trigger("living_room_all_lights_on");
  }
  // Example combination taps and rotation.
  if (sequence_array[0] == "1224" && substr(sequence_array[1], 0, 1) == "R" && sequence_array[2] = "4221") {
    text_to_speech("Access granted");
    automation_trigger("secret_lair_mode_activate");
  }
  
  // reset the button sequence.
  button_sequence = ""
}