Files
HomeAssistantVS/blueprints/automation/mcinnes01/ikea-e2490-bilresa-scroll-wheel.yaml
T
2026-07-14 23:57:03 -04:00

501 lines
16 KiB
YAML

blueprint:
name: IKEA E2490 BILRESA Scroll Wheel (Zigbee2MQTT + ZHA)
description: 'Unified controller blueprint for IKEA E2490 BILRESA scroll wheel working
with Zigbee2MQTT and ZHA.
- Buttons: on, off, on_double, off_double
- Scroll: brightness_move_to_level with action_level from Zigbee2MQTT main topic,
or ZHA move_to_level args
Supports light brightness, media_player volume, light color_temp, light hue.
Version: 2026-01-18
'
domain: automation
input:
controller_device:
name: Controller Device (Zigbee2MQTT or ZHA)
description: Select the E2490 device integrated via Zigbee2MQTT or ZHA.
selector:
device:
filter:
- integration: mqtt
- integration: zha
multiple: false
scroll_target:
name: Scroll wheel target entity (light)
selector:
entity:
filter:
- domain:
- light
multiple: false
reorder: false
scroll_mode:
name: Default scroll mode
description: Default mode when automation starts or after auto-reset timeout
default: brightness
selector:
select:
mode: dropdown
options:
- label: Brightness (light)
value: brightness
- label: Volume (media_player)
value: volume
- label: Color temperature (light)
value: color_temp
- label: Hue (light)
value: hue
sort: false
custom_value: false
multiple: false
volume_target:
name: Volume target (media_player)
default:
selector:
entity:
filter:
- domain:
- media_player
multiple: false
reorder: false
color_temp_target:
name: Color temperature target (light)
default:
selector:
entity:
filter:
- domain:
- light
multiple: false
reorder: false
hue_target:
name: Hue target (light)
default:
selector:
entity:
filter:
- domain:
- light
multiple: false
reorder: false
scroll_mode_helper:
name: (Optional) Scroll mode helper (input_select)
description: 'Provide an input_select to allow cycling scroll modes at runtime
with double ON. Must have options: brightness, volume, color_temp, hue (in
that order)'
default:
selector:
entity:
filter:
- domain:
- input_select
multiple: false
reorder: false
last_activity_helper:
name: (Optional) Last activity helper (input_datetime)
description: Provide an input_datetime to enable auto-reset after inactivity.
Works with scroll mode helper and reset timeout.
default:
selector:
entity:
filter:
- domain:
- input_datetime
multiple: false
reorder: false
reset_timeout:
name: (Optional) Auto-reset timeout (seconds)
description: Time in seconds of inactivity before resetting to default scroll
mode. Set to 0 to disable auto-reset. Requires both scroll mode helper and
last activity helper.
default: 60
selector:
number:
min: 0.0
max: 3600.0
step: 1.0
unit_of_measurement: seconds
mode: box
volume_max:
name: (Optional) Max volume
description: Upper limit for volume mode (0.0 - 1.0). Values from the wheel
will be clamped to this maximum.
default: 1.0
selector:
number:
min: 0.0
max: 1.0
step: 0.01
mode: slider
button_on_short:
name: Button ON - short press (optional)
default: []
selector:
action: {}
button_off_short:
name: Button OFF - short press (optional)
default: []
selector:
action: {}
button_on_double:
name: Button ON - double press (optional)
default: []
selector:
action: {}
button_off_double:
name: Button OFF - double press (optional)
default: []
selector:
action: {}
source_url: https://gist.github.com/mcinnes01/399132520034291b89a82659653b2893
alias: IKEA E2490 BILRESA Scroll Wheel (Zigbee2MQTT + ZHA)
variables:
controller_device: !input controller_device
friendly_name: '{{ device_attr(controller_device, ''name'') | default('''') }}'
z2m_topic: zigbee2mqtt/{{ friendly_name }}
scroll_mode: !input scroll_mode
scroll_mode_helper: !input scroll_mode_helper
last_activity_helper: !input last_activity_helper
reset_timeout: !input reset_timeout
effective_mode: '{{ states(scroll_mode_helper) if scroll_mode_helper is not none
and states(scroll_mode_helper) != '''' else scroll_mode }}'
brightness_target: !input scroll_target
volume_target: !input volume_target
color_temp_target: !input color_temp_target
hue_target: !input hue_target
volume_max: !input volume_max
available_modes: '{% set modes = [] %} {% if brightness_target is not none %}{%
set modes = modes + [''brightness''] %}{% endif %} {% if volume_target is not
none %}{% set modes = modes + [''volume''] %}{% endif %} {% if color_temp_target
is not none %}{% set modes = modes + [''color_temp''] %}{% endif %} {% if hue_target
is not none %}{% set modes = modes + [''hue''] %}{% endif %} {{ modes }}
'
button_on_short_seq: !input button_on_short
button_off_short_seq: !input button_off_short
button_on_double_seq: !input button_on_double
button_off_double_seq: !input button_off_double
trigger:
- platform: device
id: z2m-on
domain: mqtt
device_id: !input controller_device
type: action
subtype: 'on'
- platform: device
id: z2m-off
domain: mqtt
device_id: !input controller_device
type: action
subtype: 'off'
- platform: device
id: z2m-on-double
domain: mqtt
device_id: !input controller_device
type: action
subtype: on_double
- platform: device
id: z2m-off-double
domain: mqtt
device_id: !input controller_device
type: action
subtype: off_double
- platform: mqtt
id: z2m-payload
topic: zigbee2mqtt/#
- platform: event
id: zha-on
event_type: zha_event
event_data:
device_id: !input controller_device
command: 'on'
- platform: event
id: zha-off
event_type: zha_event
event_data:
device_id: !input controller_device
command: 'off'
- platform: event
id: zha-level
event_type: zha_event
event_data:
device_id: !input controller_device
command: move_to_level
condition: []
action:
- if:
- condition: template
value_template: "{{ scroll_mode_helper is not none \n and last_activity_helper
is not none \n and reset_timeout > 0\n and states(last_activity_helper)
not in ['unknown', 'unavailable', ''] \n and (now() - (states(last_activity_helper)
| as_datetime)).total_seconds() > reset_timeout }}\n"
then:
- service: input_select.select_option
target:
entity_id: '{{ scroll_mode_helper }}'
data:
option: '{{ scroll_mode }}'
- choose:
- conditions:
- condition: template
value_template: "{{ trigger.id == 'z2m-payload' \n and trigger.topic == z2m_topic
\n and trigger.payload_json is defined \n and trigger.payload_json.action
== 'brightness_move_to_level' }}\n"
sequence:
- variables:
lvl: '{{ trigger.payload_json.action_level | default(none) }}'
trans: '{{ trigger.payload_json.action_transition_time | default(0) }}'
- choose:
- conditions:
- condition: template
value_template: '{{ effective_mode == ''brightness'' and brightness_target
is not none }}'
sequence:
- service: light.turn_on
target:
entity_id: '{{ brightness_target }}'
data:
brightness: '{% if lvl is none %}255{% else %}{{ ((lvl|float/241.0)*255.0)|round(0)|int
}}{% endif %}
'
transition: '{{ trans }}'
- if:
- condition: template
value_template: '{{ last_activity_helper is not none }}'
then:
- service: input_datetime.set_datetime
target:
entity_id: '{{ last_activity_helper }}'
data:
timestamp: '{{ now().timestamp() }}'
- conditions:
- condition: template
value_template: '{{ effective_mode == ''volume'' and volume_target is not
none }}'
sequence:
- service: media_player.volume_set
target:
entity_id: '{{ volume_target }}'
data:
volume_level: "{% if lvl is none %}\n {{ volume_max }}\n{% else %}\n
\ {{ ((lvl|float/241.0) * volume_max)|round(3) }}\n{% endif %}\n"
- if:
- condition: template
value_template: '{{ last_activity_helper is not none }}'
then:
- service: input_datetime.set_datetime
target:
entity_id: '{{ last_activity_helper }}'
data:
timestamp: '{{ now().timestamp() }}'
- conditions:
- condition: template
value_template: '{{ effective_mode == ''color_temp'' and color_temp_target
is not none }}'
sequence:
- variables:
min_mireds: '{{ state_attr(color_temp_target, ''min_mireds'') | default(153)
}}'
max_mireds: '{{ state_attr(color_temp_target, ''max_mireds'') | default(500)
}}'
- service: light.turn_on
target:
entity_id: '{{ color_temp_target }}'
data:
color_temp: "{% set minm = min_mireds|int %} {% set maxm = max_mireds|int
%} {% if lvl is none %}\n {{ maxm }}\n{% else %}\n {% set ratio =
((lvl|float - 1.0)/240.0) %}\n {{ (minm + ratio*(maxm-minm))|round(0)|int
}}\n{% endif %}\n"
- if:
- condition: template
value_template: '{{ last_activity_helper is not none }}'
then:
- service: input_datetime.set_datetime
target:
entity_id: '{{ last_activity_helper }}'
data:
timestamp: '{{ now().timestamp() }}'
- conditions:
- condition: template
value_template: '{{ effective_mode == ''hue'' and hue_target is not none
}}'
sequence:
- variables:
current_hs: '{{ state_attr(hue_target, ''hs_color'') | default([0, 100])
}}'
current_sat: '{{ current_hs[1] if current_hs is sequence and current_hs|length
> 1 else 100 }}'
hue_val: '{% if lvl is none %}360{% else %}{{ ((lvl|float/241.0)*360.0)|round(0)|int
}}{% endif %}
'
- service: light.turn_on
target:
entity_id: '{{ hue_target }}'
data:
hs_color: '[ {{ hue_val }}, {{ current_sat }} ]'
- if:
- condition: template
value_template: '{{ last_activity_helper is not none }}'
then:
- service: input_datetime.set_datetime
target:
entity_id: '{{ last_activity_helper }}'
data:
timestamp: '{{ now().timestamp() }}'
- conditions:
- condition: template
value_template: '{{ trigger.id == ''zha-level'' }}'
sequence:
- variables:
lvl: '{% set a = trigger.event.data.args | default([]) %} {% if a is sequence
and a|length > 0 %}{{ a[0] }}{% else %}{{ none }}{% endif %}
'
trans: '{% set a = trigger.event.data.args | default([]) %} {% if a is sequence
and a|length > 1 %}{{ a[1] }}{% else %}0{% endif %}
'
- choose:
- conditions:
- condition: template
value_template: '{{ effective_mode == ''brightness'' and brightness_target
is not none }}'
sequence:
- service: light.turn_on
target:
entity_id: '{{ brightness_target }}'
data:
brightness: '{% if lvl is none %}255{% else %}{{ (lvl|int)|min(255)|max(1)
}}{% endif %}
'
transition: '{{ trans }}'
- if:
- condition: template
value_template: '{{ last_activity_helper is not none }}'
then:
- service: input_datetime.set_datetime
target:
entity_id: '{{ last_activity_helper }}'
data:
timestamp: '{{ now().timestamp() }}'
- conditions:
- condition: template
value_template: '{{ trigger.id in [''z2m-on'',''zha-on''] }}'
sequence:
- choose:
- conditions:
- condition: template
value_template: '{{ button_on_short_seq | length > 0 }}'
sequence: !input button_on_short
default:
- choose:
- conditions:
- condition: template
value_template: '{{ effective_mode == ''volume'' and volume_target is
not none }}'
sequence:
- service: media_player.turn_on
target:
entity_id: '{{ volume_target }}'
- conditions: []
sequence:
- service: light.turn_on
target:
entity_id: !input scroll_target
- conditions:
- condition: template
value_template: '{{ trigger.id in [''z2m-off'',''zha-off''] }}'
sequence:
- choose:
- conditions:
- condition: template
value_template: '{{ button_off_short_seq | length > 0 }}'
sequence: !input button_off_short
default:
- choose:
- conditions:
- condition: template
value_template: '{{ effective_mode == ''volume'' and volume_target is
not none }}'
sequence:
- service: media_player.turn_off
target:
entity_id: '{{ volume_target }}'
- conditions: []
sequence:
- service: light.turn_off
target:
entity_id: !input scroll_target
- conditions:
- condition: template
value_template: '{{ trigger.id == ''z2m-on-double'' }}'
sequence:
- choose:
- conditions:
- condition: template
value_template: '{{ scroll_mode_helper is not none }}'
sequence:
- variables:
current_mode: '{{ states(scroll_mode_helper) }}'
modes_list: '{{ available_modes }}'
current_index: '{{ modes_list.index(current_mode) if current_mode in modes_list
else 0 }}'
next_index: '{{ (current_index + 1) % (modes_list | length) }}'
next_mode: '{{ modes_list[next_index] if modes_list | length > 0 else
''brightness'' }}'
- service: input_select.select_option
target:
entity_id: '{{ scroll_mode_helper }}'
data:
option: '{{ next_mode }}'
- if:
- condition: template
value_template: '{{ last_activity_helper is not none }}'
then:
- service: input_datetime.set_datetime
target:
entity_id: '{{ last_activity_helper }}'
data:
timestamp: '{{ now().timestamp() }}'
- choose:
- conditions:
- condition: template
value_template: '{{ button_on_double_seq | length > 0 }}'
sequence: !input button_on_double
- conditions: []
sequence:
- choose:
- conditions:
- condition: template
value_template: '{{ button_on_double_seq | length > 0 }}'
sequence: !input button_on_double
- conditions: []
sequence:
- service: light.turn_on
target:
entity_id: !input scroll_target
data:
brightness: 255
- conditions:
- condition: template
value_template: '{{ trigger.id == ''z2m-off-double'' }}'
sequence:
- choose:
- conditions:
- condition: template
value_template: '{{ button_off_double_seq | length > 0 }}'
sequence: !input button_off_double
default:
- service: light.turn_off
target:
entity_id: !input scroll_target
mode: restart
max_exceeded: silent