501 lines
16 KiB
YAML
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
|