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

    # Button 1
    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
    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
    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
    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:

mode: parallel
max: 10
max_exceeded: silent

trigger:
  - platform: mqtt
    topic: !input mqtt_topic

condition:
  - condition: template
    value_template: "{{ trigger.payload_json.action != '' }}"

action:
  - variables:
      input_text_var: !input last_pressed
      counter_var: !input tap_counter

  - choose:
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload_json.action is match('^button_[1-4]_press$') }}"
        sequence:
          - if:
              - condition: template
                value_template: "{{ 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: "{{ 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: "{{ trigger.payload_json.action is match('button_[1-4]_press_release') and states(counter_var) > '1' }}"
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: "{{ trigger.payload_json.action == 'button_1_press_release' }}"
                sequence: !input button_1_double_press
              - conditions:
                  - condition: template
                    value_template: "{{ trigger.payload_json.action == 'button_2_press_release' }}"
                sequence: !input button_2_double_press
              - conditions:
                  - condition: template
                    value_template: "{{ trigger.payload_json.action == 'button_3_press_release' }}"
                sequence: !input button_3_double_press
              - conditions:
                  - condition: template
                    value_template: "{{ trigger.payload_json.action == 'button_4_press_release' }}"
                sequence: !input button_4_double_press
    default:
      - if:
          - condition: template
            value_template: "{{ 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 trigger.payload_json.action is match('^dial_rotate_') }}"
            sequence:
              - choose:
                  - conditions:
                      - condition: template
                        value_template: "{{ trigger.payload_json.action is match('^dial_rotate_left_') }}"
                    sequence: !input button_1_rotate_counter_clockwise
                  - conditions:
                      - condition: template
                        value_template: "{{ 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 trigger.payload_json.action is match('^dial_rotate_') }}"
            sequence:
              - choose:
                  - conditions:
                      - condition: template
                        value_template: "{{ trigger.payload_json.action is match('^dial_rotate_left_') }}"
                    sequence: !input button_2_rotate_counter_clockwise
                  - conditions:
                      - condition: template
                        value_template: "{{ 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 trigger.payload_json.action is match('^dial_rotate_') }}"
            sequence:
              - choose:
                  - conditions:
                      - condition: template
                        value_template: "{{ trigger.payload_json.action is match('^dial_rotate_left_') }}"
                    sequence: !input button_3_rotate_counter_clockwise
                  - conditions:
                      - condition: template
                        value_template: "{{ 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 trigger.payload_json.action is match('^dial_rotate_') }}"
            sequence:
              - choose:
                  - conditions:
                      - condition: template
                        value_template: "{{ trigger.payload_json.action is match('^dial_rotate_left_') }}"
                    sequence: !input button_4_rotate_counter_clockwise
                  - conditions:
                      - condition: template
                        value_template: "{{ trigger.payload_json.action is match('^dial_rotate_right_') }}"
                    sequence: !input button_4_rotate_clockwise
          - conditions:
              - condition: template
                value_template: "{{ trigger.payload_json.action == 'button_1_press_release' }}"
            sequence: !input button_1_single_press
          - conditions:
              - condition: template
                value_template: "{{ trigger.payload_json.action == 'button_1_hold' }}"
            sequence: !input button_1_hold
          - conditions:
              - condition: template
                value_template: "{{ trigger.payload_json.action == 'button_2_press_release' }}"
            sequence: !input button_2_single_press
          - conditions:
              - condition: template
                value_template: "{{ trigger.payload_json.action == 'button_2_hold' }}"
            sequence: !input button_2_hold
          - conditions:
              - condition: template
                value_template: "{{ trigger.payload_json.action == 'button_3_press_release' }}"
            sequence: !input button_3_single_press
          - conditions:
              - condition: template
                value_template: "{{ trigger.payload_json.action == 'button_3_hold' }}"
            sequence: !input button_3_hold
          - conditions:
              - condition: template
                value_template: "{{ trigger.payload_json.action == 'button_4_press_release' }}"
            sequence: !input button_4_single_press
          - conditions:
              - condition: template
                value_template: "{{ 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.