Skip to content

Instantly share code, notes, and snippets.

@jsenecal
Last active August 13, 2025 13:28
Show Gist options
  • Select an option

  • Save jsenecal/8ab52daaf53092cce35fd6705274d12b to your computer and use it in GitHub Desktop.

Select an option

Save jsenecal/8ab52daaf53092cce35fd6705274d12b to your computer and use it in GitHub Desktop.
blueprint:
name: ERS Rotary Dial - Light Control (Event Entity)
description: >
Control light brightness, hue and color with an ERS rotary dial using event entities.
Device needs to be in COMMAND mode (triple-press the button to switch modes).
domain: automation
input:
light:
name: Light
description: The Light entity or Light Group entity to control
selector:
entity:
filter:
- domain: light
- domain: group
multiple: false
dial:
name: Rotary dial
description: Select the rotary dial event entity
selector:
entity:
filter:
- domain: event
multiple: false
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
mode: queued
max: 20
max_exceeded: silent
trigger:
- platform: state
entity_id: !input dial
attribute: event_type
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"
event_type: "{{ trigger.to_state.attributes.event_type if trigger.to_state and trigger.to_state.attributes else '' }}"
direction: "{{ trigger.to_state.attributes.direction if trigger.to_state and trigger.to_state.attributes else '' }}"
single_pressed: "{{ event_type == 'single' }}"
double_pressed: "{{ event_type == 'double' }}"
brightness_rotated: "{{ event_type == 'brightness_step' }}"
temp_rotated: "{{ event_type in ['color_temperature_step_up', 'color_temperature_step_down'] }}"
rotated: "{{ brightness_rotated or temp_rotated }}"
positive: "{{ direction == 'up' or event_type == 'color_temperature_step_up' }}"
# Fixed step size for event-based devices
step_size: 20
full_rotate_step_count: "{{ 13 * 20 | int }}"
current_mode: "{{ states(mode_tracker) | int(1) }}"
min_mode: "{{ state_attr(mode_tracker, 'min') | int(1) }}"
max_mode: "{{ state_attr(mode_tracker, 'max') | int(4) }}"
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(2000) }}"
max_hue: "{{ state_attr(hue_tracker, 'max') | int(6500) }}"
hue_steps: "{{ full_rotate_step_count * 2 | int }}"
hue_delta: "{{ (step_size | float(0) / hue_steps) * (max_hue - min_hue) }}"
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(0) }}"
max_brightness: "{{ state_attr(brightness_tracker, 'max') | int(255) }}"
brightness_steps: "{{ full_rotate_step_count * 2 | int }}"
brightness_delta: "{{ (step_size | float(0) / brightness_steps) * (max_brightness - min_brightness) }}"
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(0) }}"
max_color: "{{ state_attr(color_tracker, 'max') | int(1535) }}"
color_steps: "{{ full_rotate_step_count * 3 | int }}"
color_delta: "{{ (step_size | float(0) / color_steps) * (max_color - min_color) }}"
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 }}
- condition: template
value_template: "{{ event_type != '' and trigger.to_state is not none }}"
- choose:
# If event was a dial turn
- conditions:
- condition: template
value_template: "{{ rotated }}"
sequence:
- choose:
# Mode 1: Do nothing (prevent accidental changes)
- conditions:
- condition: template
value_template: "{{ current_mode == 1 }}"
sequence: []
# Mode 2: Brightness
- conditions:
- condition: template
value_template: "{{ current_mode == 2 }}"
sequence:
- service: input_number.set_value
entity_id: !input brightness_tracker
data:
value: "{{ next_brightness }}"
- service: light.turn_on
data:
brightness: "{{ states(brightness_tracker) | int }}"
target:
entity_id: !input light
# Mode 3: Color temperature
- conditions:
- condition: template
value_template: "{{ current_mode == 3 }}"
sequence:
- service: input_number.set_value
entity_id: !input hue_tracker
data:
value: "{{ next_hue }}"
- service: light.turn_on
data:
kelvin: "{{ states(hue_tracker) | int }}"
target:
entity_id: !input light
# Mode 4: RGB Color
- conditions:
- condition: template
value_template: "{{ current_mode == 4 }}"
sequence:
- service: input_number.set_value
entity_id: !input color_tracker
data:
value: "{{ next_color }}"
- service: light.turn_on
data:
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 -%}
[{{ r }}, {{ g }}, {{ b }}]
target:
entity_id: !input light
# Single press: change mode
- conditions:
- condition: template
value_template: "{{ single_pressed }}"
sequence:
- service: input_number.set_value
entity_id: !input mode_tracker
data:
value: "{{ next_mode }}"
# Double press: toggle light
- conditions:
- condition: template
value_template: "{{ double_pressed }}"
sequence:
- service: light.toggle
target:
entity_id: !input light
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment