Zigbee Tuya Smart Rotary Knob

Just bought one of these Rotary switches to play with from Ali Express, £5
https://www.aliexpress.com/item/1005006765315209.html?spm=a2g0o.order_list.order_list_main.137.2e951802qeg2jv
I’ve integrated it into my Zigbe2MQTT setup…installs as Tuya ERS-10TZBVK-AA.

I wanted to be able to control an RGB light locally therefore used via Home Assistant and manual control rather than relying on the cloud Tuya.

Anyway it is set up as follows:-
single press – on
double press – off
Long press change mode
turn right – mode1 move forwards through colours
turn left – mode1 move backwards though colours
turn right – mode2 increase brightness
turn left – mode2 decrease brightness

All sounds so simple, but i struggled with the coding (certainly no coder – too old!)…therefore with the help from ChatGPT I got it working as required, here is the code if it helps anyone else…have fun.

Automation:-

alias: Upgraded Rotary Control - Faster Brightness Control

# 📌 Trigger: Listens for MQTT messages from the Rotary Button
triggers:
  - topic: zigbee2mqtt/Rotary Button/action
    trigger: mqtt

actions:
  - choose:
      # 🟢 Single Press → Turns the light ON
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'single' }}"
        sequence:
          - target:
              entity_id: light.dining_room
            action: light.turn_on
            data: {}

      # 🔴 Double Press → Turns the light OFF
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'double' }}"
        sequence:
          - target:
              entity_id: light.dining_room
            action: light.turn_off
            data: {}

      # 🔄 Long Press → Toggles between Brightness Mode & Color Mode
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'hold' }}"
        sequence:
          - target:
              entity_id: input_boolean.rotary_button_mode
            action: input_boolean.toggle
            data: {}

      # 🔵 Rotate Right → Adjusts brightness (if in brightness mode) or cycles colors forward (if in color mode)
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'rotate_right' }}"
        sequence:
          - choose:
              # ✨ Brightness Mode (rotary_button_mode OFF): Increase brightness in steps of 50
              - conditions:
                  - condition: state
                    entity_id: input_boolean.rotary_button_mode
                    state: "off"
                sequence:
                  - variables:
                      current_brightness: >-
                        {{ state_attr('light.dining_room', 'brightness') | default(255) }}
                      max_brightness: 255
                      step: 50
                  - target:
                      entity_id: light.dining_room
                    data:
                      brightness: "{{ [current_brightness + step, max_brightness] | min }}"
                    action: light.turn_on
                  - delay: "00:00:01"

              # 🎨 Color Mode (rotary_button_mode ON): Cycle colors forward
              - conditions:
                  - condition: state
                    entity_id: input_boolean.rotary_button_mode
                    state: "on"
                sequence:
                  - variables:
                      colors: >-
                        {{ [[255,0,0], [0,255,0], [0,0,255], [255,255,0], [255,0,255], [0,255,255]] }}
                      current_color: >-
                        {{ state_attr('light.dining_room', 'rgb_color') | default([255,0,0]) }}
                      current_color_fixed: "{{ current_color | list }}"
                      index: "{{ colors.index(current_color_fixed) if current_color_fixed in colors else 0 }}"
                      new_color: "{{ colors[(index + 1) % colors | length] }}"
                  - target:
                      entity_id: light.dining_room
                    data:
                      brightness: 255
                      rgb_color: "{{ new_color }}"
                    action: light.turn_on
                  - delay: "00:00:01"

      # 🔴 Rotate Left → Adjusts brightness (if in brightness mode) or cycles colors backward (if in color mode)
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'rotate_left' }}"
        sequence:
          - choose:
              # ✨ Brightness Mode (rotary_button_mode OFF): Decrease brightness in steps of 50
              - conditions:
                  - condition: state
                    entity_id: input_boolean.rotary_button_mode
                    state: "off"
                sequence:
                  - variables:
                      current_brightness: >-
                        {{ state_attr('light.dining_room', 'brightness') | default(255) }}
                      min_brightness: 25
                      step: 50
                  - target:
                      entity_id: light.dining_room
                    data:
                      brightness: "{{ [current_brightness - step, min_brightness] | max }}"
                    action: light.turn_on
                  - delay: "00:00:01"

              # 🎨 Color Mode (rotary_button_mode ON): Cycle colors backward
              - conditions:
                  - condition: state
                    entity_id: input_boolean.rotary_button_mode
                    state: "on"
                sequence:
                  - variables:
                      colors: >-
                        {{ [[255,0,0], [0,255,0], [0,0,255], [255,255,0], [255,0,255], [0,255,255]] }}
                      current_color: >-
                        {{ state_attr('light.dining_room', 'rgb_color') | default([255,0,0]) }}
                      current_color_fixed: "{{ current_color | list }}"
                      index: "{{ colors.index(current_color_fixed) if current_color_fixed in colors else 0 }}"
                      new_color: "{{ colors[(index - 1) % colors | length] }}"
                  - target:
                      entity_id: light.dining_room
                    data:
                      brightness: 255
                      rgb_color: "{{ new_color }}"
                    action: light.turn_on
                  - delay: "00:00:01"

mode: restart  # Ensures that rapid rotations don't conflict with each other


Had to also create a helper :-

- alias: "Rotary Button Mode Toggle"
  trigger:
    - platform: mqtt
      topic: "zigbee2mqtt/Rotary Button/action"
      payload: "hold"
  action:
    - service: input_boolean.toggle
      entity_id: input_boolean.rotary_color_mode

or
if you prefer not to edit YAML, you can create this helper in the UI:

:one: Go to :gear: SettingsDevices & ServicesHelpers
:two: Click :heavy_plus_sign: Create Helper
:three: Select “Toggle”
:four: Enter:

  • Name: Rotary Color Mode
  • Entity ID: input_boolean.rotary_color_mode
  • Icon: mdi:palette
    :five: Click “Create” :white_check_mark:

Have fun I know I did, Using ChatGPT is fantastic, there is no way I could have coded this…Have fun.

1 Like

when I try to create that automation I get

Message malformed: extra keys not allowed @ data[‘triggers’]

i replaced all your dining room light references with the entity I am trying to control (a smart rgbw bulb)

any thoughts?

Sounds like a formatting problem, I’ve copied my working code again just incase:- I take it you are using the automation editor withing HA, then view yaml, and paste in there

alias: Upgraded Rotary Control - Faster Brightness Control
triggers:
  - topic: zigbee2mqtt/Rotary Button/action
    trigger: mqtt
actions:
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'single' }}"
        sequence:
          - target:
              entity_id: light.dining_room
            action: light.turn_on
            data: {}
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'double' }}"
        sequence:
          - target:
              entity_id: light.dining_room
            action: light.turn_off
            data: {}
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'hold' }}"
        sequence:
          - target:
              entity_id: input_boolean.rotary_button_mode
            action: input_boolean.toggle
            data: {}
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'rotate_right' }}"
        sequence:
          - choose:
              - conditions:
                  - condition: state
                    entity_id: input_boolean.rotary_button_mode
                    state: "off"
                sequence:
                  - variables:
                      current_brightness: >-
                        {{ state_attr('light.dining_room', 'brightness') |
                        default(255) }}
                      max_brightness: 255
                      step: 50
                  - target:
                      entity_id: light.dining_room
                    data:
                      brightness: "{{ [current_brightness + step, max_brightness] | min }}"
                    action: light.turn_on
                  - delay: "00:00:01"
              - conditions:
                  - condition: state
                    entity_id: input_boolean.rotary_button_mode
                    state: "on"
                sequence:
                  - variables:
                      colors: >-
                        {{ [[255,0,0], [0,255,0], [0,0,255], [255,255,0],
                        [255,0,255], [0,255,255]] }}
                      current_color: >-
                        {{ state_attr('light.dining_room', 'rgb_color') |
                        default([255,0,0]) }}
                      current_color_fixed: "{{ current_color | list }}"
                      index: >-
                        {{ colors.index(current_color_fixed) if
                        current_color_fixed in colors else 0 }}
                      new_color: "{{ colors[(index + 1) % colors | length] }}"
                  - target:
                      entity_id: light.dining_room
                    data:
                      brightness: 255
                      rgb_color: "{{ new_color }}"
                    action: light.turn_on
                  - delay: "00:00:01"
      - conditions:
          - condition: template
            value_template: "{{ trigger.payload == 'rotate_left' }}"
        sequence:
          - choose:
              - conditions:
                  - condition: state
                    entity_id: input_boolean.rotary_button_mode
                    state: "off"
                sequence:
                  - variables:
                      current_brightness: >-
                        {{ state_attr('light.dining_room', 'brightness') |
                        default(255) }}
                      min_brightness: 25
                      step: 50
                  - target:
                      entity_id: light.dining_room
                    data:
                      brightness: "{{ [current_brightness - step, min_brightness] | max }}"
                    action: light.turn_on
                  - delay: "00:00:01"
              - conditions:
                  - condition: state
                    entity_id: input_boolean.rotary_button_mode
                    state: "on"
                sequence:
                  - variables:
                      colors: >-
                        {{ [[255,0,0], [0,255,0], [0,0,255], [255,255,0],
                        [255,0,255], [0,255,255]] }}
                      current_color: >-
                        {{ state_attr('light.dining_room', 'rgb_color') |
                        default([255,0,0]) }}
                      current_color_fixed: "{{ current_color | list }}"
                      index: >-
                        {{ colors.index(current_color_fixed) if
                        current_color_fixed in colors else 0 }}
                      new_color: "{{ colors[(index - 1) % colors | length] }}"
                  - target:
                      entity_id: light.dining_room
                    data:
                      brightness: 255
                      rgb_color: "{{ new_color }}"
                    action: light.turn_on
                  - delay: "00:00:01"
mode: restart

for info i just used an RGB bulb