- Improve calculations for ZHA ERS light dial (remove double-click power)
- Add Z2M port of ERS light dial
This commit is contained in:
		| @ -1,9 +1,11 @@ | ||||
| # Instructions: https://community.home-assistant.io/t/zha-control-light-color-hue-brightness-with-ers-dial/595002 | ||||
| # 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 | ||||
|   name: ERS Rotary Dial - Light Control (Z2M) - EVENT mode | ||||
|   description: Control light brightness, hue and color with an ERS rotary dial | ||||
| 
 | ||||
|   source_url: https://raw.githubusercontent.com/nwithan8/configs/main/home_assistant/blueprints/automations/ers_rotary_dial_light_control_z2m.yaml | ||||
| 
 | ||||
|   domain: automation | ||||
| 
 | ||||
|   input: | ||||
| @ -71,7 +73,17 @@ variables: | ||||
|   hue_tracker: !input "hue_tracker" | ||||
|   brightness_tracker: !input "brightness_tracker" | ||||
|   color_tracker: !input "color_tracker" | ||||
|   positive: "{{ trigger.event.data.command == 'step' and trigger.event.data.params.step_mode == 0 }}" | ||||
|   command: "{{ trigger.to_state.state }}" | ||||
|   single_pressed: "{{ command == 'single' }}" | ||||
|   double_pressed: "{{ command == 'double' }}" | ||||
|   rotated: "{{ command in ['rotate_right', 'rotate_left'] }}" | ||||
|   positive: "{{ command == 'rotate_right' }}" | ||||
|   step_size: > | ||||
|     {% if rotated %} | ||||
|       {{ trigger.to_state.attributes.action_step_size | int }} | ||||
|     {% else %} | ||||
|       {{ 0 }} | ||||
|     {% endif %} | ||||
|   # 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 }}" | ||||
| @ -88,12 +100,7 @@ variables: | ||||
|   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: > | ||||
|     {% if trigger.event.data.command != 'step' %} | ||||
|       {{ 0 }} | ||||
|     {% else %} | ||||
|       {{ (trigger.event.data.params.step_size | float(0) / hue_steps) * (max_hue - min_hue) }} | ||||
|     {% endif %} | ||||
|   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) -%} | ||||
| @ -112,12 +119,7 @@ variables: | ||||
|   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: > | ||||
|     {% if trigger.event.data.command != 'step' %} | ||||
|       {{ 0 }} | ||||
|     {% else %} | ||||
|       {{ (trigger.event.data.params.step_size | float(0) / brightness_steps) * (max_brightness - min_brightness) }} | ||||
|     {% endif %} | ||||
|   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) -%} | ||||
| @ -136,12 +138,7 @@ variables: | ||||
|   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: > | ||||
|     {% if trigger.event.data.command != 'step' %} | ||||
|       {{ 0 }} | ||||
|     {% else %} | ||||
|       {{ (trigger.event.data.params.step_size | float(0) / color_steps) * (max_color - min_color) }} | ||||
|     {% endif %} | ||||
|   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) -%} | ||||
| @ -170,7 +167,7 @@ action: | ||||
|       # If event was a dial turn | ||||
|       - conditions: | ||||
|           - condition: template | ||||
|             value_template: "{{ trigger.event.data.command == 'step' }}" | ||||
|             value_template: "{{ rotated }}" | ||||
|             alias: Dial turned (0 for right, 1 for left) | ||||
|         sequence: | ||||
|           # Process different actions based on current mode | ||||
| @ -180,7 +177,7 @@ action: | ||||
|                   - condition: template | ||||
|                     value_template: "{{ current_mode == 1 }}" | ||||
|                     alias: In Mode 1 (Power) | ||||
|                 sequence: [] | ||||
|                 sequence: [ ] | ||||
|               # In Mode 2 (Brightness) | ||||
|               - conditions: | ||||
|                   - condition: template | ||||
| @ -270,7 +267,7 @@ action: | ||||
|       # If event was a dial button short push | ||||
|       - conditions: | ||||
|           - condition: template | ||||
|             value_template: "{{ trigger.event.data.command == 'toggle' }}" | ||||
|             value_template: "{{ single_pressed }}" | ||||
|         sequence: | ||||
|           # Change mode (cycling back if reached the end) | ||||
|           - service: input_number.set_value | ||||
| @ -281,11 +278,11 @@ action: | ||||
|       # If event was a dial button long press | ||||
|       - conditions: | ||||
|           - condition: template | ||||
|             value_template: "{{ trigger.event.data.command == 'move_saturation' }}" # dumb: https://github.com/zigpy/zha-device-handlers/blob/92c9fbc6d01a5d86f78d64183302b906aa7d8215/zhaquirks/tuya/ts004f.py#L146C37-L146C37 | ||||
|             value_template: "{{ double_pressed }}" | ||||
|         sequence: | ||||
|           # Toggle light power | ||||
|           - service: light.toggle | ||||
|             data: {} | ||||
|             data: { } | ||||
|             target: | ||||
|               entity_id: !input light | ||||
|             alias: Toggle light power | ||||
| @ -0,0 +1,277 @@ | ||||
| # 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 (ZHA) - COMMAND mode | ||||
|   description: Control light brightness, hue and color with an ERS rotary dial | ||||
|  | ||||
|   source_url: https://raw.githubusercontent.com/nwithan8/configs/main/home_assistant/blueprints/automations/ers_rotary_dial_light_control_zha.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: zha | ||||
|             model: TS004F | ||||
|  | ||||
|     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 | ||||
|  | ||||
| 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" | ||||
|   command: "{{ trigger.event.data.command }}" | ||||
|   single_pressed: "{{ command == 'toggle' }}" | ||||
|   # double_pressed: "{{ command == 'move_saturation' }}" # dumb: https://github.com/zigpy/zha-device-handlers/blob/92c9fbc6d01a5d86f78d64183302b906aa7d8215/zhaquirks/tuya/ts004f.py#L146C37-L146C37 | ||||
|   rotated: "{{ command == 'step' }}" | ||||
|   positive: "{{ rotated and trigger.event.data.params.step_mode == 0 }}" | ||||
|   step_size: > | ||||
|     {% if rotated %} | ||||
|       {{ trigger.event.data.params.step_size | float(0) }} | ||||
|     {% else %} | ||||
|       {{ 0 }} | ||||
|     {% endif %} | ||||
|   # 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 }} | ||||
|  | ||||
| trigger: | ||||
|   - platform: event | ||||
|     event_type: zha_event | ||||
|     event_data: | ||||
|       device_id: !input dial | ||||
|  | ||||
| action: | ||||
|   # Process current event | ||||
|   - 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 | ||||
		Reference in New Issue
	
	Block a user