# Instructions: https://community.home-assistant.io/t/zha-z2m-control-light-color-hue-brightness-with-ers-dial/595002

blueprint:
  name: ERS Rotary Dial - Light Control (Z2M) - COMMAND mode
  description: >
    Control light brightness, hue and color with an ERS rotary dial.
    Device needs to be in COMMAND mode (triple-press the button to switch modes).
    Needs "Home Assistant legacy entity attributes" checked.

  source_url: https://raw.githubusercontent.com/nwithan8/configs/main/home_assistant/blueprints/automations/ers_rotary_dial_light_control_z2m.yaml

  domain: automation

  input:
    light:
      name: Light
      description: The Light entity or Light Group entity to control
      selector:
        entity:
          filter:
            - domain:
                - light
                - group
          multiple: false

    dial:
      name: Rotary dial
      description: Select the rotary dial you wish to use to control the light
      selector:
        device:
          filter:
            integration: mqtt

    mode_tracker:
      name: Mode tracker
      description: An input number helper to store the current mode in (1-4)
      selector:
        entity:
          filter:
            domain: input_number

    color_tracker:
      name: Color tracker
      description: An input number helper to store the current RGB value in (0-1535)
      selector:
        entity:
          filter:
            domain: input_number

    hue_tracker:
      name: Hue tracker
      description: An input number helper to store the current hue in (2000-6500)
      selector:
        entity:
          filter:
            domain: input_number

    brightness_tracker:
      name: Brightness tracker
      description: An input number helper to store the current brightness in (0-255)
      selector:
        entity:
          filter:
            domain: input_number

# Queued, to ignore non-matched events but capture existing ones
mode: queued
max: 20
max_exceeded: silent

trigger:
  - platform: state
    entity_id: !input dial
    not_to: ""
    attribute: action

action:
  - variables:
      light: !input "light"
      dial: !input "dial"
      mode_tracker: !input "mode_tracker"
      hue_tracker: !input "hue_tracker"
      brightness_tracker: !input "brightness_tracker"
      color_tracker: !input "color_tracker"
      dial_topic: "{{ base_topic }}/{{ device_attr(dial, 'name') }}/action"
      # Ref: https://community.home-assistant.io/t/zigbee2mqtt-tuya-moes-smart-knob-ers-10tzbvk-aa/419989/26
      command: "{{ trigger.to_state.state }}"
      single_pressed: "{{ command == 'toggle' }}"
      double_pressed: "{{ command == 'double' }}"
      rotated: "{{ command in ['brightness_step_up', 'brightness_step_down', 'color_temperature_step_up', 'color_temperature_step_down'] }}"
      positive: "{{ command in ['brightness_step_up', 'color_temperature_step_up'] }}"
      step_size: "{{ trigger.to_state.attributes.action_step_size }}"
      # How many steps to go full rotation (min 13 per step * 20 steps per full rotation)
      full_rotate_step_count: "{{ 13 * 20 | int }}"
      current_mode: "{{ states(mode_tracker) | int }}"
      min_mode: "{{ state_attr(mode_tracker, 'min') | int }}"
      max_mode: "{{ state_attr(mode_tracker, 'max') | int }}"
      # always increment, looping
      next_mode: >
        {% set val = current_mode + 1 %} 
        {% if val > max_mode %} 
          {% set val = min_mode %}
        {% endif %}
        {{ val | int }}
      min_hue: "{{ state_attr(hue_tracker, 'min') | int }}"
      max_hue: "{{ state_attr(hue_tracker, 'max') | int }}"
      # How many steps to go full min-max spectrum
      hue_steps: "{{ full_rotate_step_count * 2 | int }}"
      hue_delta: "{{ (step_size | float(0) / hue_steps) * (max_hue - min_hue) }}"
      # don't loop when reached max or min
      next_hue: >
        {%- set val = states(hue_tracker) | float(0) -%}
        {%- set delta = hue_delta -%}
        {%- if not positive -%}
          {%- set delta = delta * -1 -%}
        {%- endif -%}
        {%- set val = val + delta | int -%}
        {%- if positive and val > max_hue -%}
          {%- set val = max_hue -%}
        {%- elif not positive and val < min_hue -%}
          {%- set val = min_hue -%}
        {%- endif -%}
        {{ val | int }}
      min_brightness: "{{ state_attr(brightness_tracker, 'min') | int }}"
      max_brightness: "{{ state_attr(brightness_tracker, 'max') | int }}"
      # How many steps to go full min-max spectrum
      brightness_steps: "{{ full_rotate_step_count * 2 | int }}"
      brightness_delta: "{{ (step_size | float(0) / brightness_steps) * (max_brightness - min_brightness) }}"
      # don't loop when reached max or min
      next_brightness: >
        {%- set val = states(brightness_tracker) | float(0) -%}
        {%- set delta = brightness_delta -%}
        {%- if not positive -%}
          {%- set delta = delta * -1 -%}
        {%- endif -%}
        {%- set val = val + delta | int -%}
        {%- if positive and val > max_brightness -%} 
          {%- set val = max_brightness -%}
        {%- elif not positive and val < min_brightness -%}
          {%- set val = min_brightness -%}
        {%- endif -%}
        {{ val | int }}
      min_color: "{{ state_attr(color_tracker, 'min') | int }}"
      max_color: "{{ state_attr(color_tracker, 'max') | int }}"
      # How many steps to go full min-max spectrum
      color_steps: "{{ full_rotate_step_count * 3 | int }}"
      color_delta: "{{ (step_size | float(0) / color_steps) * (max_color - min_color) }}"
      # loop when reached max or min
      next_color: >
        {%- set val = states(color_tracker) | float(0) -%}
        {%- set delta = color_delta -%}
        {%- if not positive -%}
          {%- set delta = delta * -1 -%}
        {%- endif -%}
        {%- set val = val + delta | int -%}
        {%- if positive and val > max_color -%}
          {%- set val = val % max_color -%}
        {%- elif not positive and val < min_color -%}
          {%- set diff = min_color - val -%}
          {%- set val = max_color - (diff % max_color) -%}
        {%- endif -%}
        {{ val | int }}
  # Process current event
  - choose:
      # Verify that the dial is the one that triggered the event
      - conditions:
          - "{{ trigger.payload != ''}}"
          - "{{ trigger.topic == dial_topic }}"
        sequence:
          - choose:
              # If event was a dial turn
              - conditions:
                  - condition: template
                    value_template: "{{ rotated }}"
                    alias: Dial turned (0 for right, 1 for left)
                sequence:
                  # Process different actions based on current mode
                  - choose:
                      # In Mode 1 (don't do anything; prevent accidental knob rotation)
                      - conditions:
                          - condition: template
                            value_template: "{{ current_mode == 1 }}"
                            alias: In Mode 1 (Power)
                        sequence: [ ]
                      # In Mode 2 (Brightness)
                      - conditions:
                          - condition: template
                            value_template: "{{ current_mode == 2 }}"
                            alias: In Mode 2 (Brightness)
                        sequence:
                          - service: input_number.set_value
                            entity_id: !input brightness_tracker
                            data:
                              value: "{{ next_brightness }}"
                            alias: Update brightness tracker
                          - service: light.turn_on
                            data:
                              brightness: "{{ states(brightness_tracker) | int }}"
                            target:
                              entity_id: !input light
                            alias: Change light brightness
                      # In Mode 3 (Hue)
                      - conditions:
                          - condition: template
                            value_template: "{{ current_mode == 3 }}"
                            alias: In Mode 3 (Hue)
                        sequence:
                          - service: input_number.set_value
                            entity_id: !input hue_tracker
                            data:
                              value: "{{ next_hue }}"
                            alias: Update hue tracker
                          - service: light.turn_on
                            data:
                              kelvin: "{{ states(hue_tracker) | int }}"
                            target:
                              entity_id: !input light
                            alias: Change light hue
                      # In Mode 4 (Color)
                      - conditions:
                          - condition: template
                            value_template: "{{ current_mode == 4 }}"
                            alias: In Mode 4 (Color)
                        sequence:
                          - service: input_number.set_value
                            entity_id: !input color_tracker
                            data:
                              value: "{{ next_color }}"
                            alias: Update color tracker
                          - service: light.turn_on
                            data:
                              # Calculate RGB codes based on 0-1535 index (max 2 channels, one always off)
                              rgb_color: >
                                {%- set r = 0 -%}
                                {%- set g = 0 -%}
                                {%- set b = 0 -%}
                                {%- set index = states(color_tracker) | int -%}
                                {%- if index > 0 and index <= 256 -%}
                                  {%- set r = 255 -%}
                                  {%- set g = index -%}
                                  {%- set b = 0 -%}
                                {%- elif index > 256 and index <= 512 -%}
                                  {%- set r = 512 - index -%}
                                  {%- set g = 255 -%}
                                  {%- set b = 0 -%}
                                {%- elif index > 512 and index <= 768 -%}
                                  {%- set r = 0 -%}
                                  {%- set g = 255 -%}
                                  {%- set b = index - 512 -%}
                                {%- elif index > 768 and index <= 1024 -%}
                                  {%- set r = 0 -%}
                                  {%- set g = 1024 - index -%}
                                  {%- set b = 255 -%}
                                {%- elif index > 1024 and index <= 1280 -%}
                                  {%- set r = index - 1024 -%}
                                  {%- set g = 0 -%}
                                  {%- set b = 255 -%}
                                {%- elif index > 1280 and index <= 1536 -%}
                                  {%- set r = 255 -%}
                                  {%- set g = 0 -%}
                                  {%- set b = 1536 - index -%}
                                {%- endif -%}
                                {%- set r = r | string -%}
                                {%- set g = g | string -%}
                                {%- set b = b | string -%}
                                {{ r + "," + g + "," + b }}
                            target:
                              entity_id: !input light
                            alias: Change light color
                    alias: Determine action based on mode
              # If event was a dial button short push
              - conditions:
                  - condition: template
                    value_template: "{{ single_pressed }}"
                sequence:
                  # Change mode (cycling back if reached the end)
                  - service: input_number.set_value
                    entity_id: !input mode_tracker
                    data:
                      value: "{{ next_mode }}"
                    alias: Switch to next mode
              # If event was a dial button long press
              - conditions:
                  - condition: template
                    value_template: "{{ double_pressed }}"
                sequence:
                  # Toggle light power
                  - service: light.toggle
                    data: { }
                    target:
                      entity_id: !input light
                    alias: Toggle light power
