Forked from seamus65/zha_moes_smart_knob_lights.yaml
Last active
February 21, 2024 21:14
-
-
Save misuzu/1f1d4f4f9e8b82bda2fa4b5078cde6b7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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://gist.github.com/misuzu/1f1d4f4f9e8b82bda2fa4b5078cde6b7 | |
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: | |
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment