diff --git a/home_assistant/blueprints/automations/ers_rotary_dial_light_control.yaml b/home_assistant/blueprints/automations/ers_rotary_dial_light_control.yaml new file mode 100644 index 0000000..2002d94 --- /dev/null +++ b/home_assistant/blueprints/automations/ers_rotary_dial_light_control.yaml @@ -0,0 +1,278 @@ +# Instructions: https://community.home-assistant.io/t/zha-control-light-color-hue-brightness-with-ers-dial/595002 + +blueprint: + name: ERS Rotary Dial - Light Control + 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.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 + +# Only single, otherwise could flood with attempts +mode: single +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" + positive: "{{ trigger.event.data.command == 'step' and trigger.event.data.params.step_mode == 0 }}" + # 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: "{{ (trigger.event.data.params.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: "{{ (trigger.event.data.params.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: "{{ (trigger.event.data.params.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: "{{ trigger.event.data.command == 'step' }}" + 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: "{{ trigger.event.data.command == 'toggle' }}" + 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: "{{ trigger.event.data.command == 'move_saturation' }}" # dumb: https://github.com/zigpy/zha-device-handlers/blob/92c9fbc6d01a5d86f78d64183302b906aa7d8215/zhaquirks/tuya/ts004f.py#L146C37-L146C37 + sequence: + # Toggle light power + - service: light.toggle + data: {} + target: + entity_id: !input light + alias: Toggle light power