updated apps

This commit is contained in:
2026-07-14 23:57:03 -04:00
parent 6cc7212cef
commit 010e828e9c
797 changed files with 45153 additions and 4246 deletions
@@ -0,0 +1,484 @@
blueprint:
name: Ikea BILRESA scroll wheel (sensors for scroll, events for press)
description: "Ikea BILRESA blueprint met 3 kanalen.\n\n- Scroll links/rechts gebruikt
sensor.*current_switch_position_* (pulse 0/1).\n Alleen rising edge (0->1) triggert,
dus per klik exact 1 stap en direct.\n- Druk op scrollwiel blijft event.* gebruiken
zodat single/double/long/hold blijft werken.\n"
domain: automation
input:
remote:
name: Remote
description: Ikea BILRESA remote control device
selector:
device:
multiple: false
channel1_section:
name: Kanaal 1
collapsed: true
input:
click_action_ch1:
name: 'Kanaal 1: click actie'
default: []
selector:
action: {}
double_click_action_ch1:
name: 'Kanaal 1: double click actie'
default: []
selector:
action: {}
long_click_action_ch1:
name: 'Kanaal 1: long press actie'
default: []
selector:
action: {}
on_hold_action_ch1:
name: 'Kanaal 1: on-hold actie'
description: Herhaalt totdat losgelaten, zie on_hold_delay
default: []
selector:
action: {}
scroll_wheel_target_ch1:
name: 'Kanaal 1: scroll target(s)'
description: Light of media_player die je met het wiel wilt bedienen
default: []
selector:
entity:
multiple: true
filter:
- domain:
- light
- media_player
reorder: false
scroll_wheel_mode_ch1:
name: 'Kanaal 1: scroll mode'
default: 'lights: dim'
selector:
select:
options:
- 'lights: dim'
- 'lights: color temp and color hue'
- 'lights: color temp only'
- 'lights: color hue only'
- 'media: volume control'
custom_value: false
multiple: false
sort: false
channel2_section:
name: Kanaal 2
collapsed: true
input:
click_action_ch2:
name: 'Kanaal 2: click actie'
default: []
selector:
action: {}
double_click_action_ch2:
name: 'Kanaal 2: double click actie'
default: []
selector:
action: {}
long_click_action_ch2:
name: 'Kanaal 2: long press actie'
default: []
selector:
action: {}
on_hold_action_ch2:
name: 'Kanaal 2: on-hold actie'
description: Herhaalt totdat losgelaten, zie on_hold_delay
default: []
selector:
action: {}
scroll_wheel_target_ch2:
name: 'Kanaal 2: scroll target(s)'
default: []
selector:
entity:
multiple: true
filter:
- domain:
- light
- media_player
reorder: false
scroll_wheel_mode_ch2:
name: 'Kanaal 2: scroll mode'
default: 'lights: dim'
selector:
select:
options:
- 'lights: dim'
- 'lights: color temp and color hue'
- 'lights: color temp only'
- 'lights: color hue only'
- 'media: volume control'
custom_value: false
multiple: false
sort: false
channel3_section:
name: Kanaal 3
collapsed: true
input:
click_action_ch3:
name: 'Kanaal 3: click actie'
default: []
selector:
action: {}
double_click_action_ch3:
name: 'Kanaal 3: double click actie'
default: []
selector:
action: {}
long_click_action_ch3:
name: 'Kanaal 3: long press actie'
default: []
selector:
action: {}
on_hold_action_ch3:
name: 'Kanaal 3: on-hold actie'
description: Herhaalt totdat losgelaten, zie on_hold_delay
default: []
selector:
action: {}
scroll_wheel_target_ch3:
name: 'Kanaal 3: scroll target(s)'
default: []
selector:
entity:
multiple: true
filter:
- domain:
- light
- media_player
reorder: false
scroll_wheel_mode_ch3:
name: 'Kanaal 3: scroll mode'
default: 'lights: dim'
selector:
select:
options:
- 'lights: dim'
- 'lights: color temp and color hue'
- 'lights: color temp only'
- 'lights: color hue only'
- 'media: volume control'
custom_value: false
multiple: false
sort: false
global_section:
name: Globale instellingen
collapsed: true
input:
dim_step_pct:
name: Dimmer stapgrootte (%)
default: 5
selector:
number:
mode: box
step: 1.0
dim_min_pct:
name: Minimum brightness (%)
default: 5
selector:
number:
mode: box
step: 1.0
min_color_temp:
name: Minimum kleurtemperatuur (K)
default: 2200
selector:
number:
mode: box
step: 1.0
max_color_temp:
name: Maximum kleurtemperatuur (K)
default: 4000
selector:
number:
mode: box
step: 1.0
color_temp_step:
name: Kleurtemperatuur stap (K)
default: 100
selector:
number:
mode: box
step: 1.0
color_hue_step:
name: Hue stap (graden, 0..360)
default: 4
selector:
number:
mode: box
step: 1.0
color_saturation:
name: Saturation (%)
default: 100
selector:
number:
mode: box
step: 1.0
on_hold_delay:
name: On-hold delay (sec)
default: 0.1
selector:
number:
mode: box
step: 0.1
volume_step_pct:
name: Volume stap (%)
default: 2
selector:
number:
mode: box
step: 1.0
volume_max_pct:
name: Max volume (%)
default: 50
selector:
number:
mode: box
step: 1.0
source_url: https://raw.githubusercontent.com/BoGu5/ha-blueprints/a688916fc161affcb2577e1ed90bb4f7ecf778fb/kea%20BILRESA%20scroll%20wheel%20(sensors%20for%20scroll%2C%20events%20for%20press)
alias: Ikea_BILRESA_scroll_wheel
mode: queued
max: 50
max_exceeded: silent
trigger:
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ var_press_event_ch1 }}'
id: press_ch1
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ var_press_event_ch2 }}'
id: press_ch2
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ var_press_event_ch3 }}'
id: press_ch3
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ var_scroll_right_sensor_ch1 }}'
id: scroll_right_ch1
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ var_scroll_right_sensor_ch2 }}'
id: scroll_right_ch2
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ var_scroll_right_sensor_ch3 }}'
id: scroll_right_ch3
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ var_scroll_left_sensor_ch1 }}'
id: scroll_left_ch1
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ var_scroll_left_sensor_ch2 }}'
id: scroll_left_ch2
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ var_scroll_left_sensor_ch3 }}'
id: scroll_left_ch3
condition:
- condition: template
value_template: "{% set o = trigger.event.data.old_state %} {% set n = trigger.event.data.new_state
%} {{ o is not none and n is not none\n and o.state not in ['unavailable','unknown']\n
\ and n.state not in ['unavailable','unknown'] }}"
variables:
targets_entities_ch1: !input scroll_wheel_target_ch1
targets_entities_ch2: !input scroll_wheel_target_ch2
targets_entities_ch3: !input scroll_wheel_target_ch3
scroll_wheel_mode_ch1: !input scroll_wheel_mode_ch1
scroll_wheel_mode_ch2: !input scroll_wheel_mode_ch2
scroll_wheel_mode_ch3: !input scroll_wheel_mode_ch3
var_min_pct: !input dim_min_pct
var_dim_step_pct: !input dim_step_pct
var_color_hue_step: !input color_hue_step
var_color_saturation: !input color_saturation
var_min_color_temp: !input min_color_temp
var_max_color_temp: !input max_color_temp
var_color_temp_step: !input color_temp_step
var_on_hold_delay: !input on_hold_delay
var_volume_step_pct: !input volume_step_pct
var_volume_max_pct: !input volume_max_pct
action:
- variables:
channel: '{% if trigger.id in [''press_ch1'',''scroll_left_ch1'',''scroll_right_ch1'']
%}ch1 {% elif trigger.id in [''press_ch2'',''scroll_left_ch2'',''scroll_right_ch2'']
%}ch2 {% elif trigger.id in [''press_ch3'',''scroll_left_ch3'',''scroll_right_ch3'']
%}ch3 {% else %}unknown{% endif %}'
is_press: '{{ trigger.id is match(''^press_'') }}'
is_scroll: '{{ trigger.id is match(''^scroll_'') }}'
scroll_dir: '{% if trigger.id in [''scroll_left_ch1'',''scroll_left_ch2'',''scroll_left_ch3'']
%}left {% elif trigger.id in [''scroll_right_ch1'',''scroll_right_ch2'',''scroll_right_ch3'']
%}right {% else %}none{% endif %}'
pulse_rising: '{% set o = trigger.event.data.old_state.state %} {% set n = trigger.event.data.new_state.state
%} {{ (o in [''0'',''off''] and n in [''1'',''on'']) }}'
scroll_steps: '{{ 1 if (is_scroll and pulse_rising) else 0 }}'
press_event_type: "{% if is_press %}\n {{ trigger.event.data.new_state.attributes.event_type
| default('') }}\n{% else %}\n {{ '' }}\n{% endif %}"
- choose:
- conditions:
- condition: template
value_template: '{{ is_press and press_event_type == ''multi_press_1'' }}'
sequence:
- choose:
- conditions: '{{ channel == ''ch1'' }}'
sequence: !input click_action_ch1
- conditions: '{{ channel == ''ch2'' }}'
sequence: !input click_action_ch2
- conditions: '{{ channel == ''ch3'' }}'
sequence: !input click_action_ch3
- conditions:
- condition: template
value_template: '{{ is_press and press_event_type == ''multi_press_2'' }}'
sequence:
- choose:
- conditions: '{{ channel == ''ch1'' }}'
sequence: !input double_click_action_ch1
- conditions: '{{ channel == ''ch2'' }}'
sequence: !input double_click_action_ch2
- conditions: '{{ channel == ''ch3'' }}'
sequence: !input double_click_action_ch3
- conditions:
- condition: template
value_template: '{{ is_press and press_event_type == ''long_press'' }}'
sequence:
- choose:
- conditions: '{{ channel == ''ch1'' }}'
sequence:
- sequence: !input long_click_action_ch1
- repeat:
while: "{{ repeat.index < 500\n and state_attr(var_press_event_ch1,
'event_type') == 'long_press' }}"
sequence:
- sequence: !input on_hold_action_ch1
- delay: '{{ var_on_hold_delay }}'
- conditions: '{{ channel == ''ch2'' }}'
sequence:
- sequence: !input long_click_action_ch2
- repeat:
while: "{{ repeat.index < 500\n and state_attr(var_press_event_ch2,
'event_type') == 'long_press' }}"
sequence:
- sequence: !input on_hold_action_ch2
- delay: '{{ var_on_hold_delay }}'
- conditions: '{{ channel == ''ch3'' }}'
sequence:
- sequence: !input long_click_action_ch3
- repeat:
while: "{{ repeat.index < 500\n and state_attr(var_press_event_ch3,
'event_type') == 'long_press' }}"
sequence:
- sequence: !input on_hold_action_ch3
- delay: '{{ var_on_hold_delay }}'
- conditions:
- condition: template
value_template: '{{ is_scroll and scroll_steps > 0 and scroll_dir in [''left'',''right'']
}}'
sequence:
- repeat:
for_each: '{{ (targets_entities_ch1 if channel == ''ch1'' else (targets_entities_ch2
if channel == ''ch2'' else targets_entities_ch3)) }}'
sequence:
- choose:
- conditions:
- condition: template
value_template: '{{ (scroll_wheel_mode_ch1 if channel == ''ch1'' else
(scroll_wheel_mode_ch2 if channel == ''ch2'' else scroll_wheel_mode_ch3))
== ''lights: dim'' }}'
sequence:
- if:
- condition: template
value_template: '{{ is_state(repeat.item, ''on'') }}'
then:
- action: light.turn_on
target:
entity_id: '{{ repeat.item }}'
data:
brightness_step_pct: "{% if scroll_dir == 'right' %}\n {{ scroll_steps
* var_dim_step_pct }}\n{% else %}\n {{ -1 * ([scroll_steps *
var_dim_step_pct,\n state_attr(repeat.item, 'brightness')
* 100 / 254 - var_min_pct] | min) }}\n{% endif %}"
transition: 0.1
- conditions:
- condition: template
value_template: "{% set mode = (scroll_wheel_mode_ch1 if channel ==
'ch1' else (scroll_wheel_mode_ch2 if channel == 'ch2' else scroll_wheel_mode_ch3))
%} {{ mode == 'lights: color temp only'\n or (mode == 'lights: color
temp and color hue' and state_attr(repeat.item, 'color_temp_kelvin')
is not none) }}"
sequence:
- action: script.light_color_temp_helper
data:
mode: '{{ ''down'' if scroll_dir == ''left'' else ''up'' }}'
min_color_temp: '{{ var_min_color_temp }}'
max_color_temp: '{{ var_max_color_temp }}'
color_temp_step: '{{ var_color_temp_step * scroll_steps }}'
target_light: '{{ repeat.item }}'
- conditions:
- condition: template
value_template: "{% set mode = (scroll_wheel_mode_ch1 if channel ==
'ch1' else (scroll_wheel_mode_ch2 if channel == 'ch2' else scroll_wheel_mode_ch3))
%} {{ mode == 'lights: color hue only'\n or (mode == 'lights: color
temp and color hue' and state_attr(repeat.item, 'color_temp_kelvin')
is none) }}"
sequence:
- action: script.light_color_hs_helper
data:
target_light: '{{ repeat.item }}'
color_hue_step: '{{ var_color_hue_step * scroll_steps }}'
color_saturation: '{{ var_color_saturation }}'
mode: '{{ ''down'' if scroll_dir == ''left'' else ''up'' }}'
- conditions:
- condition: template
value_template: '{{ (scroll_wheel_mode_ch1 if channel == ''ch1'' else
(scroll_wheel_mode_ch2 if channel == ''ch2'' else scroll_wheel_mode_ch3))
== ''media: volume control'' }}'
sequence:
- action: media_player.volume_set
target:
entity_id: '{{ repeat.item }}'
data:
volume_level: "{{ [[\n (state_attr(repeat.item, 'volume_level')
| float(0)) * 100\n + (1 if scroll_dir == 'right' else -1) *
scroll_steps * var_volume_step_pct,\n var_volume_max_pct\n ]
| min, 0] | max / 100 }}"
trigger_variables:
var_remote: !input remote
var_press_event_ch1: "{% for item in device_entities(var_remote) %}\n {% set m
= item | regex_findall(find='^event\\\\..+_3$') %}\n {% if m | length > 0 %}{{
m[0] }}{% endif %}\n{% endfor %}"
var_press_event_ch2: "{% for item in device_entities(var_remote) %}\n {% set m
= item | regex_findall(find='^event\\\\..+_6$') %}\n {% if m | length > 0 %}{{
m[0] }}{% endif %}\n{% endfor %}"
var_press_event_ch3: "{% for item in device_entities(var_remote) %}\n {% set m
= item | regex_findall(find='^event\\\\..+_9$') %}\n {% if m | length > 0 %}{{
m[0] }}{% endif %}\n{% endfor %}"
var_scroll_right_sensor_ch1: "{% for item in device_entities(var_remote) %}\n {%
set m = item | regex_findall(find='^sensor\\\\..+current_switch_position_1$')
%}\n {% if m | length > 0 %}{{ m[0] }}{% endif %}\n{% endfor %}"
var_scroll_left_sensor_ch1: "{% for item in device_entities(var_remote) %}\n {%
set m = item | regex_findall(find='^sensor\\\\..+current_switch_position_2$')
%}\n {% if m | length > 0 %}{{ m[0] }}{% endif %}\n{% endfor %}"
var_scroll_right_sensor_ch2: "{% for item in device_entities(var_remote) %}\n {%
set m = item | regex_findall(find='^sensor\\\\..+current_switch_position_4$')
%}\n {% if m | length > 0 %}{{ m[0] }}{% endif %}\n{% endfor %}"
var_scroll_left_sensor_ch2: "{% for item in device_entities(var_remote) %}\n {%
set m = item | regex_findall(find='^sensor\\\\..+current_switch_position_5$')
%}\n {% if m | length > 0 %}{{ m[0] }}{% endif %}\n{% endfor %}"
var_scroll_right_sensor_ch3: "{% for item in device_entities(var_remote) %}\n {%
set m = item | regex_findall(find='^sensor\\\\..+current_switch_position_7$')
%}\n {% if m | length > 0 %}{{ m[0] }}{% endif %}\n{% endfor %}"
var_scroll_left_sensor_ch3: "{% for item in device_entities(var_remote) %}\n {%
set m = item | regex_findall(find='^sensor\\\\..+current_switch_position_8$')
%}\n {% if m | length > 0 %}{{ m[0] }}{% endif %}\n{% endfor %}"
@@ -0,0 +1,624 @@
blueprint:
name: IKEA BILRESA - Light Controller (v1.3)
description: 'Light control for IKEA BILRESA (Matter). Supports dynamic scrolling,
scene cycling, and multi-click actions for up to three channels.
'
domain: automation
input:
remote_device:
name: Remote Control
description: The IKEA BILRESA scroll wheel (Matter) used as the trigger.
selector:
device:
filter:
- manufacturer: IKEA of Sweden
model: BILRESA scroll wheel
multiple: false
header_1:
name: Channel 1
default: ''
selector:
constant:
value: ''
group_scroll_1:
name: Scroll Wheel Settings
icon: mdi:rotate-right
collapsed: true
input:
input_light_1:
name: Light / Light Group
description: Select the light or group to be controlled.
default: {}
selector:
target:
entity:
- domain:
- light
input_mode_1:
name: Scroll Function
description: Define which value is changed when turning the wheel.
default: brightness
selector:
select:
mode: dropdown
options:
- label: Brightness
value: brightness
- label: Color
value: hs_color
- label: Color temperature
value: temp_kelvin
custom_value: false
multiple: false
sort: false
input_sens_1:
name: Multiplier
description: Define how much the value should change with each scroll step.
default: 5
selector:
number:
min: 1.0
max: 50.0
unit_of_measurement: x
mode: slider
step: 1.0
input_transition_1:
name: Transition duration
description: Define how long the transition between changes should take.
default: 200
selector:
number:
min: 0.0
max: 5000.0
unit_of_measurement: ms
mode: slider
step: 1.0
group_single_click_1:
name: Single Click
icon: mdi:gesture-tap
collapsed: true
input:
input_click_mode_1:
name: Click Mode
description: Choose between a single action or cycling through scenes.
default: action
selector:
select:
mode: dropdown
options:
- label: Perform single action
value: action
- label: Scene cycler
value: scene_cycle
custom_value: false
multiple: false
sort: false
input_scenes_1:
name: Scene Selection
description: Select the scenes to cycle through sequentially.
default: []
selector:
entity:
domain:
- scene
multiple: true
reorder: false
input_click_1:
name: Action on Click
description: Action to perform when in 'Single Action' mode.
default: []
selector:
action: {}
group_multi_click_1:
name: Additional Button Functions
icon: mdi:plus-circle-outline
collapsed: true
input:
input_double_1:
name: Double Click
description: Action to perform on double press.
default: []
selector:
action: {}
input_triple_1:
name: Triple Click
description: Action to perform on triple press.
default: []
selector:
action: {}
input_long_1:
name: Hold
description: Action to perform when holding the button.
default: []
selector:
action: {}
input_release_1:
name: Release
description: Action to perform when releasing the button after a long press.
default: []
selector:
action: {}
header_2:
name: Channel 2
default: ''
selector:
constant:
value: ''
group_scroll_2:
name: Scroll Wheel Settings
icon: mdi:rotate-right
collapsed: true
input:
input_light_2:
name: Light / Light Group
description: Select the light or group to be controlled.
default: {}
selector:
target:
entity:
- domain:
- light
input_mode_2:
name: Scroll Function
description: Define which value is changed when turning the wheel.
default: brightness
selector:
select:
mode: dropdown
options:
- label: Brightness
value: brightness
- label: Color
value: hs_color
- label: Color temperature
value: temp_kelvin
custom_value: false
multiple: false
sort: false
input_sens_2:
name: Multiplier
description: Define how much the value should change with each scroll step.
default: 5
selector:
number:
min: 1.0
max: 50.0
unit_of_measurement: x
mode: slider
step: 1.0
input_transition_2:
name: Transition duration
description: Define how long the transition between changes should take.
default: 200
selector:
number:
min: 0.0
max: 5000.0
unit_of_measurement: ms
mode: slider
step: 1.0
group_single_click_2:
name: Single Click
icon: mdi:gesture-tap
collapsed: true
input:
input_click_mode_2:
name: Click Mode
description: Choose between a single action or cycling through scenes.
default: action
selector:
select:
mode: dropdown
options:
- label: Perform single action
value: action
- label: Scene cycler
value: scene_cycle
custom_value: false
multiple: false
sort: false
input_scenes_2:
name: Scene Selection
description: Select the scenes to cycle through sequentially.
default: []
selector:
entity:
domain:
- scene
multiple: true
reorder: false
input_click_2:
name: Action on Click
description: Action to perform when in 'Single Action' mode.
default: []
selector:
action: {}
group_multi_click_2:
name: Additional Button Functions
icon: mdi:plus-circle-outline
collapsed: true
input:
input_double_2:
name: Double Click
description: Action to perform on double press.
default: []
selector:
action: {}
input_triple_2:
name: Triple Click
description: Action to perform on triple press.
default: []
selector:
action: {}
input_long_2:
name: Hold
description: Action to perform when holding the button.
default: []
selector:
action: {}
input_release_2:
name: Release
description: Action to perform when releasing the button after a long press.
default: []
selector:
action: {}
header_3:
name: Channel 3
default: ''
selector:
constant:
value: ''
group_scroll_3:
name: Scroll Wheel Settings
icon: mdi:rotate-right
collapsed: true
input:
input_light_3:
name: Light / Light Group
description: Select the light or group to be controlled.
default: {}
selector:
target:
entity:
- domain:
- light
input_mode_3:
name: Scroll Function
description: Define which value is changed when turning the wheel.
default: brightness
selector:
select:
mode: dropdown
options:
- label: Brightness
value: brightness
- label: Color
value: hs_color
- label: Color temperature
value: temp_kelvin
custom_value: false
multiple: false
sort: false
input_sens_3:
name: Multiplier
description: Define how much the value should change with each scroll step.
default: 5
selector:
number:
min: 1.0
max: 50.0
unit_of_measurement: x
mode: slider
step: 1.0
input_transition_3:
name: Transition duration
description: Define how long the transition between changes should take.
default: 200
selector:
number:
min: 0.0
max: 5000.0
unit_of_measurement: ms
mode: slider
step: 1.0
group_single_click_3:
name: Single Click
icon: mdi:gesture-tap
collapsed: true
input:
input_click_mode_3:
name: Click Mode
description: Choose between a single action or cycling through scenes.
default: action
selector:
select:
mode: dropdown
options:
- label: Perform single action
value: action
- label: Scene cycler
value: scene_cycle
custom_value: false
multiple: false
sort: false
input_scenes_3:
name: Scene Selection
description: Select the scenes to cycle through sequentially.
default: []
selector:
entity:
domain:
- scene
multiple: true
reorder: false
input_click_3:
name: Action on Click
description: Action to perform when in 'Single Action' mode.
default: []
selector:
action: {}
group_multi_click_3:
name: Additional Button Functions
icon: mdi:plus-circle-outline
collapsed: true
input:
input_double_3:
name: Double Click
description: Action to perform on double press.
default: []
selector:
action: {}
input_triple_3:
name: Triple Click
description: Action to perform on triple press.
default: []
selector:
action: {}
input_long_3:
name: Hold
description: Action to perform when holding the button.
default: []
selector:
action: {}
input_release_3:
name: Release
description: Action to perform when releasing the button after a long press.
default: []
selector:
action: {}
source_url: https://gist.github.com/SvKRO/52f246362a19f0297fd18969b383181b
mode: single
max_exceeded: silent
trigger_variables:
var_remote: !input remote_device
var_events: '{{ device_entities(var_remote) | select(''match'', ''event\.'') | sort
| list }}
'
temp_kanal1_rechts: '{{ var_events[0] if var_events | length > 0 else ''none'' }}'
temp_kanal1_links: '{{ var_events[1] if var_events | length > 1 else ''none'' }}'
temp_kanal1_klick: '{{ var_events[2] if var_events | length > 2 else ''none'' }}'
temp_kanal2_rechts: '{{ var_events[3] if var_events | length > 3 else ''none'' }}'
temp_kanal2_links: '{{ var_events[4] if var_events | length > 4 else ''none'' }}'
temp_kanal2_klick: '{{ var_events[5] if var_events | length > 5 else ''none'' }}'
temp_kanal3_rechts: '{{ var_events[6] if var_events | length > 6 else ''none'' }}'
temp_kanal3_links: '{{ var_events[7] if var_events | length > 7 else ''none'' }}'
temp_kanal3_klick: '{{ var_events[8] if var_events | length > 8 else ''none'' }}'
trigger:
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ temp_kanal1_links }}'
id: trigger_kanal1_links
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ temp_kanal1_rechts }}'
id: trigger_kanal1_rechts
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ temp_kanal1_klick }}'
id: trigger_kanal1_klick
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ temp_kanal2_links }}'
id: trigger_kanal2_links
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ temp_kanal2_rechts }}'
id: trigger_kanal2_rechts
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ temp_kanal2_klick }}'
id: trigger_kanal2_klick
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ temp_kanal3_links }}'
id: trigger_kanal3_links
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ temp_kanal3_rechts }}'
id: trigger_kanal3_rechts
- trigger: event
event_type: state_changed
event_data:
entity_id: '{{ temp_kanal3_klick }}'
id: trigger_kanal3_klick
action:
- condition: template
value_template: "{% if trigger.event.data.new_state is none %}\n false\n{% else
%}\n {% set old_attr = trigger.event.data.old_state.attributes if trigger.event.data.old_state
is not none else {} %}\n {% set new_attr = trigger.event.data.new_state.attributes
%}\n {% set old_ts = old_attr.get('last_reported', 'none') %}\n {% set new_ts
= new_attr.get('last_reported', 'new') %}\n {{ old_ts != new_ts }}\n{% endif
%}\n"
- variables:
m_1: !input input_mode_1
m_2: !input input_mode_2
m_3: !input input_mode_3
s_1: !input input_sens_1
s_2: !input input_sens_2
s_3: !input input_sens_3
l_1: !input input_light_1
l_2: !input input_light_2
l_3: !input input_light_3
tr_1: !input input_transition_1
tr_2: !input input_transition_2
tr_3: !input input_transition_3
action_kanal_id: '{{ trigger.id.split(''_'')[1] }}'
event_attr: '{{ trigger.event.data.new_state.attributes | to_json | from_json
}}'
ticks: '{{ event_attr.get(''totalNumberOfPressesCounted'') | default(1) | int
}}'
action_event_type: '{{ event_attr.get(''event_type'') | default(''none'') }}'
curr_mode: '{% if action_kanal_id == ''kanal1'' %}{{ m_1 }}{% elif action_kanal_id
== ''kanal2'' %}{{ m_2 }}{% else %}{{ m_3 }}{% endif %}'
base_sens: '{% if action_kanal_id == ''kanal1'' %}{{ s_1 }}{% elif action_kanal_id
== ''kanal2'' %}{{ s_2 }}{% else %}{{ s_3 }}{% endif %}'
step_val: '{% set val = base_sens * ticks %}{{ val if trigger.id.endswith(''_rechts'')
else val * -1 }}'
transition_ms: '{% if action_kanal_id == ''kanal1'' %}{{ tr_1 }}{% elif action_kanal_id
== ''kanal2'' %}{{ tr_2 }}{% else %}{{ tr_3 }}{% endif %}'
transition_s: '{{ transition_ms / 1000 }}'
curr_target_obj: '{% if action_kanal_id == ''kanal1'' %}{{ l_1 }} {% elif action_kanal_id
== ''kanal2'' %}{{ l_2 }} {% else %}{{ l_3 }}{% endif %}
'
curr_target_list: "{% set t = curr_target_obj %} {% set ns = namespace(entities=[])
%}\n{% set e_ids = t.entity_id if t.entity_id is defined else [] %} {% set ns.entities
= [e_ids] if e_ids is string else e_ids %}\n{% set d_ids = t.device_id if t.device_id
is defined else [] %} {% set d_ids = [d_ids] if d_ids is string else d_ids %}
{% for d in d_ids %}\n {% set ns.entities = ns.entities + (device_entities(d)
| select('match', 'light\\.') | list) %}\n{% endfor %}\n{% set a_ids = t.area_id
if t.area_id is defined else [] %} {% set a_ids = [a_ids] if a_ids is string
else a_ids %} {% for a in a_ids %}\n {% set ns.entities = ns.entities + (area_entities(a)
| select('match', 'light\\.') | list) %}\n{% endfor %}\n{% set lb_ids = t.label_id
if t.label_id is defined else [] %} {% set lb_ids = [lb_ids] if lb_ids is string
else lb_ids %} {% for l in lb_ids %}\n {% set ns.entities = ns.entities + (label_entities(l)
| select('match', 'light\\.') | list) %}\n{% endfor %}\n{{ ns.entities | unique
| reject('none') | list }}\n"
curr_target: '{{ curr_target_list | first | default(''none'') }}'
- choose:
- conditions: '{{ trigger.id.endswith(''_rechts'') or trigger.id.endswith(''_links'')
}}'
sequence:
- choose:
- conditions: '{{ curr_mode == ''brightness'' }}'
sequence:
- variables:
target_value: '{% set current = state_attr(curr_target, ''brightness'')
| int(128) %} {{ [([current + (step_val | int * 2.55 | int), 0] | max),
255] | min }}
'
- service: light.turn_on
target:
entity_id: '{{ curr_target_list }}'
data:
transition: '{{ transition_s }}'
brightness: '{{ target_value }}'
- conditions: '{{ curr_mode == ''temp_kelvin'' }}'
sequence:
- variables:
target_value: '{% set curr = state_attr(curr_target, ''color_temp_kelvin'')
| int(3000) %} {% set min_v = state_attr(curr_target, ''min_color_temp_kelvin'')
| int(2000) %} {% set max_v = state_attr(curr_target, ''max_color_temp_kelvin'')
| int(6535) %} {{ [([curr + (step_val * 10) | int, min_v] | max), max_v]
| min }}
'
- service: light.turn_on
target:
entity_id: '{{ curr_target_list }}'
data:
transition: '{{ transition_s }}'
color_temp_kelvin: '{{ target_value }}'
- conditions: '{{ curr_mode == ''hs_color'' }}'
sequence:
- variables:
target_value: '{% set hs = state_attr(curr_target, ''hs_color'') | default([0,
100], true) %} {{ [((hs[0] | float(0) + step_val) % 360) | round(2),
hs[1] | float(100)] }}
'
- service: light.turn_on
target:
entity_id: '{{ curr_target_list }}'
data:
transition: '{{ transition_s }}'
hs_color: '{{ target_value }}'
- conditions: '{{ trigger.id.endswith(''_klick'') }}'
sequence:
- variables:
cm_1: !input input_click_mode_1
cm_2: !input input_click_mode_2
cm_3: !input input_click_mode_3
cs_1: !input input_scenes_1
cs_2: !input input_scenes_2
cs_3: !input input_scenes_3
c_mode: '{% if action_kanal_id == ''kanal1'' %}{{ cm_1 }}{% elif action_kanal_id
== ''kanal2'' %}{{ cm_2 }}{% else %}{{ cm_3 }}{% endif %}'
c_scenes: '{% if action_kanal_id == ''kanal1'' %}{{ cs_1 }}{% elif action_kanal_id
== ''kanal2'' %}{{ cs_2 }}{% else %}{{ cs_3 }}{% endif %}'
- choose:
- conditions: '{{ action_event_type == ''multi_press_1'' }}'
sequence:
- choose:
- conditions: '{{ c_mode == ''scene_cycle'' and c_scenes | count > 0 }}'
sequence:
- service: scene.turn_on
target:
entity_id: '{% set count = c_scenes | count %}{% set idx = (as_timestamp(now())
| int // 2) % count %}{{ c_scenes[idx] }}'
- conditions: '{{ c_mode == ''action'' }}'
sequence:
- choose:
- conditions: '{{ action_kanal_id == ''kanal1'' }}'
sequence: !input input_click_1
- conditions: '{{ action_kanal_id == ''kanal2'' }}'
sequence: !input input_click_2
- conditions: '{{ action_kanal_id == ''kanal3'' }}'
sequence: !input input_click_3
- conditions: '{{ action_event_type == ''multi_press_2'' }}'
sequence:
- choose:
- conditions: '{{ action_kanal_id == ''kanal1'' }}'
sequence: !input input_double_1
- conditions: '{{ action_kanal_id == ''kanal2'' }}'
sequence: !input input_double_2
- conditions: '{{ action_kanal_id == ''kanal3'' }}'
sequence: !input input_double_3
- conditions: '{{ action_event_type == ''multi_press_3'' }}'
sequence:
- choose:
- conditions: '{{ action_kanal_id == ''kanal1'' }}'
sequence: !input input_triple_1
- conditions: '{{ action_kanal_id == ''kanal2'' }}'
sequence: !input input_triple_2
- conditions: '{{ action_kanal_id == ''kanal3'' }}'
sequence: !input input_triple_3
- conditions: '{{ action_event_type == ''long_press'' }}'
sequence:
- choose:
- conditions: '{{ action_kanal_id == ''kanal1'' }}'
sequence: !input input_long_1
- conditions: '{{ action_kanal_id == ''kanal2'' }}'
sequence: !input input_long_2
- conditions: '{{ action_kanal_id == ''kanal3'' }}'
sequence: !input input_long_3
- conditions: '{{ action_event_type == ''long_release'' }}'
sequence:
- choose:
- conditions: '{{ action_kanal_id == ''kanal1'' }}'
sequence: !input input_release_1
- conditions: '{{ action_kanal_id == ''kanal2'' }}'
sequence: !input input_release_2
- conditions: '{{ action_kanal_id == ''kanal3'' }}'
sequence: !input input_release_3
@@ -0,0 +1,171 @@
blueprint:
name: IKEA BILRESA E2489 Dual Button (Matter)
author: censay
description: 'Full-featured automation for the IKEA BILRESA E2489 Matter dual-button
remote. Supports single press, double press, and long press (on press and on release)
for both buttons. Uses event entities exposed by Home Assistant for Matter devices.
Single-press actions are prioritized for reliability.
'
domain: automation
source_url: https://raw.githubusercontent.com/censay/haos-blueprints/refs/heads/master/ikea-bilresa-e2489-matter-smart-button/ikea-bilresa-e2489-matter-smart-button.yaml
homeassistant:
min_version: 2025.12.1
input:
target_device:
name: BILRESA Button Device
description: 'Select the IKEA BILRESA E2489 matter device.
'
selector:
device:
filter:
- manufacturer: IKEA of Sweden
multiple: false
button1_event:
name: Button 1 Event Entity
description: 'Select the event entity for Button 1 (for example: event.bilresa_dual_button_button_1_2).
'
selector:
entity:
domain:
- event
multiple: false
reorder: false
button2_event:
name: Button 2 Event Entity
description: 'Select the event entity for Button 2 (for example: event.bilresa_dual_button_button_2_2).
'
selector:
entity:
domain:
- event
multiple: false
reorder: false
button1_single:
name: Button 1 Single Press
description: Action for Button 1 single press (multi_press_1).
default: []
selector:
action: {}
button1_double:
name: Button 1 Double Press
description: Action for Button 1 double press (multi_press_2).
default: []
selector:
action: {}
button1_long_press:
name: Button 1 Long Press (on press)
description: Action for Button 1 long press start (long_press).
default: []
selector:
action: {}
button1_long:
name: Button 1 Long Press (on release)
description: Action for Button 1 long press completion (long_release).
default: []
selector:
action: {}
button2_single:
name: Button 2 Single Press
description: Action for Button 2 single press (multi_press_1).
default: []
selector:
action: {}
button2_double:
name: Button 2 Double Press
description: Action for Button 2 double press (multi_press_2).
default: []
selector:
action: {}
button2_long_press:
name: Button 2 Long Press (on press)
description: Action for Button 2 long press start (long_press).
default: []
selector:
action: {}
button2_long:
name: Button 2 Long Press (on release)
description: Action for Button 2 long press completion (long_release).
default: []
selector:
action: {}
variables:
button1_entity: !input button1_event
button2_entity: !input button2_event
trigger:
- platform: state
id: button1
entity_id: !input button1_event
not_from: unavailable
not_to: unavailable
- platform: state
id: button2
entity_id: !input button2_event
not_from: unavailable
not_to: unavailable
condition: []
action:
- variables:
press_type: '{{ trigger.to_state.attributes.event_type }}'
trigger_id: '{{ trigger.id }}'
- choose:
- conditions:
- condition: template
value_template: '{{ trigger_id == ''button1'' and press_type == ''multi_press_1''
}}
'
sequence: !input button1_single
- conditions:
- condition: template
value_template: '{{ trigger_id == ''button1'' and press_type == ''multi_press_2''
}}
'
sequence: !input button1_double
- conditions:
- condition: template
value_template: '{{ trigger_id == ''button1'' and press_type == ''long_press''
}}
'
sequence: !input button1_long_press
- conditions:
- condition: template
value_template: '{{ trigger_id == ''button1'' and press_type == ''long_release''
}}
'
sequence: !input button1_long
- conditions:
- condition: template
value_template: '{{ trigger_id == ''button2'' and press_type == ''multi_press_1''
}}
'
sequence: !input button2_single
- conditions:
- condition: template
value_template: '{{ trigger_id == ''button2'' and press_type == ''multi_press_2''
}}
'
sequence: !input button2_double
- conditions:
- condition: template
value_template: '{{ trigger_id == ''button2'' and press_type == ''long_press''
}}
'
sequence: !input button2_long_press
- conditions:
- condition: template
value_template: '{{ trigger_id == ''button2'' and press_type == ''long_release''
}}
'
sequence: !input button2_long
mode: restart
@@ -0,0 +1,99 @@
blueprint:
name: Zigbee2MQTT - IKEA Bilresa Dual Button
description: Control actions using an IKEA Bilresa remote via Zigbee2MQTT based on MQTT topic payloads.
domain: automation
input:
remote_name:
name: Zigbee2MQTT Friendly Name
description: The friendly name of the Bilresa remote in Zigbee2MQTT (used for the MQTT topic).
selector:
text: {}
on_click:
name: Top Button - Single Click
default: []
selector:
action: {}
on_double_click:
name: Top Button - Double Click
default: []
selector:
action: {}
on_hold:
name: Top Button - Long Press (Hold)
default: []
selector:
action: {}
off_click:
name: Bottom Button - Single Click
default: []
selector:
action: {}
off_double_click:
name: Bottom Button - Double Click
default: []
selector:
action: {}
off_hold:
name: Bottom Button - Long Press (Hold)
default: []
selector:
action: {}
stop_hold:
name: Button Released (Stop)
description: Action to run when a long press is released.
default: []
selector:
action: {}
trigger:
- platform: mqtt
topic: "{{ 'zigbee2mqtt/' ~ remote_id if '/' not in remote_id else remote_id }}"
# We dynamically evaluate the topic in the trigger instead of matching in conditions
# to avoid waking up the automation for unrelated MQTT traffic.
variables:
remote_id: !input remote_name
expected_topic: "{{ remote_id if '/' in remote_id else 'zigbee2mqtt/' ~ remote_id }}"
# Fallback trigger if the dynamic template topic has constraints in older versions
# Using wildcard MQTT trigger and filtering by topic in conditions for maximum compatibility:
trigger:
- platform: mqtt
topic: "zigbee2mqtt/#"
condition:
- condition: template
value_template: >-
{{ trigger.topic == expected_topic and trigger.payload_json.action is defined }}
action:
- choose:
- conditions:
- condition: template
value_template: "{{ trigger.payload_json.action == 'on' }}"
sequence: !input on_click
- conditions:
- condition: template
value_template: "{{ trigger.payload_json.action == 'on_double' }}"
sequence: !input on_double_click
- conditions:
- condition: template
value_template: "{{ trigger.payload_json.action == 'brightness_move_up' }}"
sequence: !input on_hold
- conditions:
- condition: template
value_template: "{{ trigger.payload_json.action == 'off' }}"
sequence: !input off_click
- conditions:
- condition: template
value_template: "{{ trigger.payload_json.action == 'off_double' }}"
sequence: !input off_double_click
- conditions:
- condition: template
value_template: "{{ trigger.payload_json.action == 'brightness_move_down' }}"
sequence: !input off_hold
- conditions:
- condition: template
value_template: "{{ trigger.payload_json.action == 'brightness_stop' }}"
sequence: !input stop_hold
mode: restart
@@ -0,0 +1,67 @@
blueprint:
name: IKEA Bilresa - High-Performance Wheel Control
description: Optimized control for the IKEA Bilresa wheel focusing on speed and
MG24/Ember stability.
domain: automation
input:
controller_name:
name: Remote MQTT Name
description: The name of the device in Zigbee2MQTT (e.g., Bilresa_Wheel)
default: Bilresa Wheel
action_on:
name: 'On'
description: Action to perform when the top button is pressed.
default: []
selector:
action: {}
action_off:
name: 'Off'
description: Action to perform when the bottom button is pressed.
default: []
selector:
action: {}
action_right:
name: Turn Right
description: 'Action for clockwise rotation. Recommended: light.turn_on with
brightness_step_pct: 10 and transition: 0.1'
default: []
selector:
action: {}
action_left:
name: Turn Left
description: 'Action for counter-clockwise rotation. Recommended: light.turn_on
with brightness_step_pct: -10 and transition: 0.1'
default: []
selector:
action: {}
source_url: https://gist.githubusercontent.com/patt110/4eda20da78422b8989b3b6686264e2ac/raw/a80245848aea86d29668015fc014b13d032569f0/Bilresa_Scrollwheel_Z2M_en.yaml
mode: restart
trigger:
- platform: mqtt
topic: zigbee2mqtt/{{ (controller_name if controller_name else 'Bilresa Wheel')
}}
action:
- variables:
payload: '{{ trigger.payload_json }}'
action_val: '{{ payload.action if ''action'' in payload else '''' }}'
- choose:
- conditions: '{{ action_val == ''on'' }}'
sequence: !input action_on
- conditions: '{{ action_val == ''off'' }}'
sequence: !input action_off
- conditions: '{{ action_val == ''brightness_move_to_level'' }}'
sequence:
- variables:
is_max: '{{ payload.action_brightness_delta is none and payload.action_level
is none }}'
is_min: '{{ payload.action_brightness_delta == 0 and payload.action_level
== 1 }}'
delta: '{{ payload.action_brightness_delta | int(0) if payload.action_brightness_delta
is not none else 0 }}'
- choose:
- conditions: '{{ delta > 0 or is_max }}'
sequence: !input action_right
- conditions: '{{ delta < 0 or is_min }}'
sequence: !input action_left
- delay:
milliseconds: 30
@@ -0,0 +1,908 @@
blueprint:
name: Ikea_bilresa_scroll_wheel
description: 'Blueprint for Ikea Bilresa Matter (button and scroll wheel for 3 different
channels)
Version: 2026-04-06
Info: https://community.home-assistant.io/t/ikea-bilresa-scroll-wheel-blueprint-matter/965365
'
domain: automation
input:
remote:
name: Remote
description: Ikea Bilresa scroll wheel remote control
selector:
device:
filter:
- manufacturer: IKEA of Sweden
model: BILRESA scroll wheel
integration: matter
multiple: false
channel1_section:
name: Channel 1
collapsed: true
input:
click_action_ch1:
name: 'Channel 1: <click> action'
default: []
selector:
action: {}
double_click_action_ch1:
name: 'Channel 1: <double-click> action'
default: []
selector:
action: {}
triple_click_action_ch1:
name: 'Channel 1: <triple-click> action'
default: []
selector:
action: {}
long_click_action_ch1:
name: 'Channel 1: <long-click> action'
description: ''
default: []
selector:
action: {}
on_hold_action_ch1:
name: 'Channel 1: <on-hold> action'
description: repeat action until button is released, see also on_hold_delay
default: []
selector:
action: {}
scroll_wheel_target_ch1:
name: 'Channel 1: Scroll wheel target entity'
description: 'Light or media player to be controlled by scroll wheel for
channel 1
- empty for <user defined> scroll wheel mode
'
selector:
entity:
multiple: true
filter:
- domain:
- light
- media_player
reorder: false
default: []
scroll_wheel_mode_ch1:
selector:
select:
options:
- 'lights: dim'
- 'lights: color temp and color hue'
- 'lights: color temp only'
- 'lights: color hue only'
- 'media: volume control'
- 'dynamic: choose from input_select'
- user defined
custom_value: false
multiple: false
sort: false
default: 'lights: dim'
name: 'Channel 1: Scroll wheel mode'
description: Select attribute of target entity controlled by scroll wheel
scroll_wheel_mode_dynamic_ch1:
selector:
entity:
filter:
- domain:
- input_select
multiple: false
reorder: false
default:
name: 'Channel 1: Scroll wheel mode input_select'
description: 'For scroll wheel mode <dynamic> only, leave empty otherwise.
Create and choose an ''input_select'' containing the scroll wheel modes
you want to choose from dynamically, e.g.
- "lights: dim"
- "lights: color temp only"'
scroll_wheel_mode_ext_ch1:
selector:
select:
options:
- relaxed
- instant
custom_value: false
multiple: false
sort: false
default: relaxed
name: 'Channel 1: Scroll wheel evaluation mode'
description: '<relaxed>: one action call after scrolling
<instant>: many action calls while scrolling
Beware:
for <instant> mode the 9 hidden sensor entities of Bilresa device have
to be enabled
'
scroll_wheel_user_action_ch1:
name: 'Channel 1: User defined scroll wheel action'
description: 'For scroll wheel mode <user defined> only
Exposed variables:
- <scroll_clicks>: for <instant> mode always 1, for <relaxed> mode number
between 1 to 8
- <scroll_direction>: ''left'' or ''right''
'
default: []
selector:
action: {}
channel2_section:
name: Channel 2
collapsed: true
input:
click_action_ch2:
name: 'Channel 2: <click> action'
default: []
selector:
action: {}
double_click_action_ch2:
name: 'Channel 2: <double-click> action'
default: []
selector:
action: {}
triple_click_action_ch2:
name: 'Channel 2: <triple-click> action'
default: []
selector:
action: {}
long_click_action_ch2:
name: 'Channel 2: <long-click> action'
description: ''
default: []
selector:
action: {}
on_hold_action_ch2:
name: 'Channel 2: <on-hold> action'
description: repeat action until button is released, see also on_hold_delay
default: []
selector:
action: {}
scroll_wheel_target_ch2:
name: 'Channel 2: Scroll wheel target entity'
description: 'Light or media player to be controlled by scroll wheel for
channel 2
- empty for <user defined> scroll wheel mode
'
selector:
entity:
multiple: true
filter:
- domain:
- light
- media_player
reorder: false
default: []
scroll_wheel_mode_ch2:
selector:
select:
options:
- 'lights: dim'
- 'lights: color temp and color hue'
- 'lights: color temp only'
- 'lights: color hue only'
- 'media: volume control'
- 'dynamic: choose from input_select'
- user defined
custom_value: false
multiple: false
sort: false
default: 'lights: dim'
name: 'Channel 2: Scroll wheel mode'
description: Select attribute of target entity controlled by scroll wheel
scroll_wheel_mode_dynamic_ch2:
selector:
entity:
filter:
- domain:
- input_select
multiple: false
reorder: false
default:
name: 'Channel 2: Scroll wheel mode input_select'
description: 'For scroll wheel mode <dynamic> only, leave empty otherwise.
Create and choose an ''input_select'' containing the scroll wheel modes
you want to choose from dynamically, e.g.
- "lights: dim"
- "lights: color temp only"'
scroll_wheel_mode_ext_ch2:
selector:
select:
options:
- relaxed
- instant
custom_value: false
multiple: false
sort: false
default: relaxed
name: 'Channel 2: Scroll wheel evaluation mode'
description: '- <relaxed>: one action call after scrolling
- <instant>: many action calls while scrolling
Beware:
- for <instant> mode the 9 hidden sensor entities of Bilresa device have
to be enabled
'
scroll_wheel_user_action_ch2:
name: 'Channel 2: User defined scroll wheel action'
description: 'For scroll wheel mode <user defined> only
Exposed variables:
- <scroll_clicks>: for <instant> mode always 1, for <relaxed> mode number
between 1 to 8
- <scroll_direction>: ''left'' or ''right''
'
default: []
selector:
action: {}
channel3_section:
name: Channel 3
collapsed: true
input:
click_action_ch3:
name: 'Channel 3: <click> action'
default: []
selector:
action: {}
double_click_action_ch3:
name: 'Channel 3: <double-click> action'
default: []
selector:
action: {}
triple_click_action_ch3:
name: 'Channel 3: <triple-click> action'
default: []
selector:
action: {}
long_click_action_ch3:
name: 'Channel 3: <long-click> action'
description: ''
default: []
selector:
action: {}
on_hold_action_ch3:
name: 'Channel 3: <on-hold> action'
description: repeat action until button is released, see also on_hold_delay
default: []
selector:
action: {}
scroll_wheel_target_ch3:
name: 'Channel 3: Scroll wheel target entity'
description: 'Light or media player to be controlled by scroll wheel for
channel 3
- empty for <user defined> scroll wheel mode
'
selector:
entity:
multiple: true
filter:
- domain:
- light
- media_player
reorder: false
default: []
scroll_wheel_mode_ch3:
selector:
select:
options:
- 'lights: dim'
- 'lights: color temp and color hue'
- 'lights: color temp only'
- 'lights: color hue only'
- 'media: volume control'
- 'dynamic: choose from input_select'
- user defined
custom_value: false
multiple: false
sort: false
default: 'lights: dim'
name: 'Channel 3: Scroll wheel mode'
description: Select attribute of target entity controlled by scroll wheel
scroll_wheel_mode_dynamic_ch3:
selector:
entity:
filter:
- domain:
- input_select
multiple: false
reorder: false
default:
name: 'Channel 3: Scroll wheel mode input_select'
description: 'For scroll wheel mode <dynamic> only, leave empty otherwise.
Create and choose an ''input_select'' containing the scroll wheel modes
you want to choose from dynamically, e.g.
- "lights: dim"
- "lights: color temp only"'
scroll_wheel_mode_ext_ch3:
selector:
select:
options:
- relaxed
- instant
custom_value: false
multiple: false
sort: false
default: relaxed
name: 'Channel 3: Scroll wheel evaluation mode'
description: '- <relaxed>: one action call after scrolling
- <instant>: many action calls while scrolling
Beware:
- for <instant> mode the 9 hidden sensor entities of Bilresa device have
to be enabled
'
scroll_wheel_user_action_ch3:
name: 'Channel 3: User defined scroll wheel action'
description: 'For scroll wheel mode <user defined> only
Exposed variables:
- <scroll_clicks>: for <instant> mode always 1, for <relaxed> mode number
between 1 to 8
- <scroll_direction>: ''left'' or ''right''
'
default: []
selector:
action: {}
global_light_setting:
name: Global light settings
collapsed: true
input:
dim_step_pct:
name: Dimmer step percentage
description: change in brightness in %
default: 10
selector:
number:
mode: box
step: 1.0
dim_min_pct:
name: Minimum brightness
description: minimum brightness in %
default: 5
selector:
number:
mode: box
step: 1.0
dim_turns_lights_on:
name: Dim turns lights on
description: 'allow scroll wheel (mode: dim) to turn lights on
'
default: false
selector:
boolean: {}
min_color_temp:
name: Minimum color temperature
description: minimum color temperature in K (for color_mode = color_temp)
default: 2200
selector:
number:
mode: box
step: 1.0
max_color_temp:
name: Maximum color temperature
description: maximum color temperature in K (for color_mode = color_temp)
default: 4000
selector:
number:
mode: box
step: 1.0
color_temp_step:
name: Color temperature step
description: change in color temperature in K (for color_mode = color_temp)
default: 100
selector:
number:
mode: box
step: 1.0
color_hue_step:
name: Color hue step
description: change in color hue in degree (0..360) (for color_mode = color_hue)
default: 6
selector:
number:
mode: box
step: 1.0
color_saturation:
name: Color saturation
description: color saturation in % (for color_mode = color_hue)
default: 100
selector:
number:
mode: box
step: 1.0
light_transition_duration:
name: Transition duration
description: Controls the duration of transitions
default: 0.5
selector:
number:
mode: box
step: 1.0
global_media_setting:
name: Global media settings
collapsed: true
input:
volume_step_pct:
name: Media player volume step percentage
description: change in volume in %
default: 3.5
selector:
number:
mode: box
step: 1.0
volume_use_up_down_for_instant:
name: 'Media player: use actions volume_up/volume_down for <instant> mode'
description: "<instant> mode uses standard actions volume_up/volume_down
instead of <volume step percentage> by default \n\nif this is to coarse
disable this option and adjust the <volume step percentage> value to the
lowest value which still changes the volume while scrolling in both directions\n"
default: true
selector:
boolean: {}
volume_max_pct:
name: Media player max volume percentage
description: max volume in %
default: 70
selector:
number:
mode: box
step: 1.0
global_misc_setting:
name: Miscellaneous settings
collapsed: true
input:
auto_reset_time_input_select_helpers:
name: Auto reset time for input_select helpers
description: 'auto reset time for input_select helpers if > 0
input_selects are reset to first selection
'
default: 0
selector:
number:
mode: box
step: 60.0
on_hold_delay:
name: On hold delay
description: delay between separate action calls while on hold button
default: 0.1
selector:
number:
mode: box
step: 0.1
max_queued_automation_calls:
name: Max queued automation calls
description: 'Increase if you want to queue more automation calls for special
cases
'
default: 10
selector:
number:
mode: box
step: 1.0
source_url: https://gist.github.com/jhol-byte/b2731a4d2476f530d76b9ff409f7f3a4
alias: Ikea_bilresa_scroll_wheel
trigger:
- event_type: state_changed
event_data:
entity_id: '{{ var_button_ch1 }}'
trigger: event
id: button_ch1
- event_type: state_changed
event_data:
entity_id: '{{ var_scroll_left_ch1 }}'
trigger: event
id: scroll_left_ch1
- event_type: state_changed
event_data:
entity_id: '{{ var_scroll_right_ch1 }}'
trigger: event
id: scroll_right_ch1
- event_type: state_changed
event_data:
entity_id: '{{ var_scroll_left_ext_ch1 }}'
trigger: event
id: scroll_left_ext_ch1
- event_type: state_changed
event_data:
entity_id: '{{ var_scroll_right_ext_ch1 }}'
trigger: event
id: scroll_right_ext_ch1
- event_type: state_changed
event_data:
entity_id: '{{ var_button_ch2 }}'
trigger: event
id: button_ch2
- event_type: state_changed
event_data:
entity_id: '{{ var_scroll_left_ch2 }}'
trigger: event
id: scroll_left_ch2
- event_type: state_changed
event_data:
entity_id: '{{ var_scroll_right_ch2 }}'
trigger: event
id: scroll_right_ch2
- event_type: state_changed
event_data:
entity_id: '{{ var_scroll_left_ext_ch2 }}'
trigger: event
id: scroll_left_ext_ch2
- event_type: state_changed
event_data:
entity_id: '{{ var_scroll_right_ext_ch2 }}'
trigger: event
id: scroll_right_ext_ch2
- event_type: state_changed
event_data:
entity_id: '{{ var_button_ch3 }}'
trigger: event
id: button_ch3
- event_type: state_changed
event_data:
entity_id: '{{ var_scroll_left_ch3 }}'
trigger: event
id: scroll_left_ch3
- event_type: state_changed
event_data:
entity_id: '{{ var_scroll_right_ch3 }}'
trigger: event
id: scroll_right_ch3
- event_type: state_changed
event_data:
entity_id: '{{ var_scroll_left_ext_ch3 }}'
trigger: event
id: scroll_left_ext_ch3
- event_type: state_changed
event_data:
entity_id: '{{ var_scroll_right_ext_ch3 }}'
trigger: event
id: scroll_right_ext_ch3
condition:
- condition: template
value_template: "{{ trigger.event.data.old_state.state != 'unavailable' \n and
trigger.event.data.new_state.state != 'unavailable'\n}}"
action:
- variables:
channel: "{% if trigger.id in ['button_ch1', 'scroll_left_ch1', 'scroll_right_ch1',
'scroll_left_ext_ch1', 'scroll_right_ext_ch1'] %}\n ch1\n{% elif trigger.id
in ['button_ch2', 'scroll_left_ch2', 'scroll_right_ch2', 'scroll_left_ext_ch2',
'scroll_right_ext_ch2'] %}\n ch2\n{% elif trigger.id in ['button_ch3', 'scroll_left_ch3',
'scroll_right_ch3', 'scroll_left_ext_ch3', 'scroll_right_ext_ch3'] %}\n ch3\n{%
endif %}"
- if: '{{ var_reset_time_input_select_helpers > 0 and last_triggered_seconds > var_reset_time_input_select_helpers
}}'
then:
repeat:
for_each:
- ch1
- ch2
- ch3
sequence:
- action: input_select.select_first
target:
entity_id: '{{ vars[''scroll_wheel_mode_dynamic_'' ~ repeat.item ] }}'
- choose:
- conditions:
- condition: template
value_template: '{{ trigger.id == (''button_'' ~ channel) and trigger.event.data.new_state.attributes.event_type
== ''multi_press_1'' }}
'
sequence:
- if: '{{ channel == ''ch1''}}'
then: !input click_action_ch1
- if: '{{ channel == ''ch2''}}'
then: !input click_action_ch2
- if: '{{ channel == ''ch3''}}'
then: !input click_action_ch3
- conditions:
- condition: template
value_template: '{{ trigger.id == (''button_'' ~ channel) and trigger.event.data.new_state.attributes.event_type
== ''multi_press_2'' }}
'
sequence:
- if: '{{ channel == ''ch1''}}'
then: !input double_click_action_ch1
- if: '{{ channel == ''ch2''}}'
then: !input double_click_action_ch2
- if: '{{ channel == ''ch3''}}'
then: !input double_click_action_ch3
- conditions:
- condition: template
value_template: '{{ trigger.id == (''button_'' ~ channel) and trigger.event.data.new_state.attributes.event_type
== ''multi_press_3'' }}
'
sequence:
- if: '{{ channel == ''ch1''}}'
then: !input triple_click_action_ch1
- if: '{{ channel == ''ch2''}}'
then: !input triple_click_action_ch2
- if: '{{ channel == ''ch3''}}'
then: !input triple_click_action_ch3
- conditions:
- condition: template
value_template: '{{ trigger.id == (''button_'' ~ channel) and trigger.event.data.new_state.attributes.event_type
== ''long_press'' }}
'
sequence:
- if: '{{ channel == ''ch1''}}'
then:
sequence:
- sequence: !input long_click_action_ch1
- repeat:
while: '{{ repeat.index < 100 and state_attr(var_button_ch1, ''event_type'')
== ''long_press'' }}'
sequence:
- sequence: !input on_hold_action_ch1
- delay: '{{ var_on_hold_delay }}'
- if: '{{ channel == ''ch2''}}'
then:
sequence:
- sequence: !input long_click_action_ch2
- repeat:
while: '{{ repeat.index < 100 and state_attr(var_button_ch2, ''event_type'')
== ''long_press'' }}'
sequence:
- sequence: !input on_hold_action_ch2
- delay: '{{ var_on_hold_delay }}'
- if: '{{ channel == ''ch3''}}'
then:
sequence:
- sequence: !input long_click_action_ch3
- repeat:
while: '{{ repeat.index < 100 and state_attr(var_button_ch3, ''event_type'')
== ''long_press'' }}'
sequence:
- sequence: !input on_hold_action_ch3
- delay: '{{ var_on_hold_delay }}'
- conditions:
- condition: template
value_template: "{{ ( (vars['scroll_wheel_mode_ext_' ~ channel] == 'relaxed')
and (trigger.id == ('scroll_left_' ~ channel) or trigger.id == ('scroll_right_'
~ channel))) or\n ( (vars['scroll_wheel_mode_ext_' ~ channel] == 'instant')
and (trigger.id == ('scroll_left_ext_' ~ channel) or trigger.id == ('scroll_right_ext_'
~ channel)) and trigger.event.data.new_state.state == '1' ) \n}}\n"
sequence:
- variables:
scroll_clicks: "{% if (vars['scroll_wheel_mode_ext_' ~ channel] == 'relaxed')
%}\n {{ trigger.event.data.new_state.attributes.totalNumberOfPressesCounted
}}\n{% else %}\n 1\n{% endif %}\n"
scroll_direction: "{% if (trigger.id == 'scroll_left_' ~ channel) or (trigger.id
== 'scroll_left_ext_' ~ channel)%}\n left\n{% else %}\n right\n{% endif
%}\n"
scroll_wheel_mode: '{{ vars[''scroll_wheel_mode_'' ~ channel] if vars[''scroll_wheel_mode_''
~ channel] != ''dynamic: choose from input_select'' else states(vars[''scroll_wheel_mode_dynamic_''
~ channel]) }}
'
- if:
- condition: template
value_template: '{{ scroll_wheel_mode == ''user defined'' }}'
then:
sequence:
- if: '{{ channel == ''ch1''}}'
then: !input scroll_wheel_user_action_ch1
- if: '{{ channel == ''ch2''}}'
then: !input scroll_wheel_user_action_ch2
- if: '{{ channel == ''ch3''}}'
then: !input scroll_wheel_user_action_ch3
else:
- repeat:
for_each: '{{ vars[''targets_entities_'' ~ channel] }}'
sequence:
- choose:
- conditions:
- condition: template
value_template: '{{ scroll_wheel_mode == ''lights: dim''}}'
sequence:
- if:
- condition: template
value_template: '{{ is_state(repeat.item, ''on'') or (var_dim_turns_lights_on
and scroll_direction == ''right'') }}'
then:
- if:
- condition: template
value_template: '{{ is_state(repeat.item, ''on'') }}'
then:
- action: light.turn_on
target:
entity_id: '{{ repeat.item }}'
data:
brightness_step_pct: "{{ iif(scroll_direction == 'right', scroll_clicks
* var_dim_step_pct, -1*([scroll_clicks * var_dim_step_pct,
state_attr(repeat.item, 'brightness')*100/254\n - var_min_pct]|min))
\n}}"
transition: '{{ var_light_transition_duration }}'
else:
- action: light.turn_on
target:
entity_id: '{{ repeat.item }}'
data:
transition: '{{ var_light_transition_duration }}'
- conditions:
- condition: template
value_template: '{{ scroll_wheel_mode == ''lights: color temp only''
or (scroll_wheel_mode == ''lights: color temp and color hue'' and
state_attr(repeat.item, ''color_temp_kelvin'') is not none ) }}'
sequence:
- action: script.light_color_temp_helper
data:
mode: '{{ iif(scroll_direction == ''left'', ''down'', ''up'') }}'
min_color_temp: '{{ var_min_color_temp }}'
max_color_temp: '{{ var_max_color_temp }}'
color_temp_step: '{{ var_color_temp_step * scroll_clicks }}'
target_light: '{{ repeat.item }}'
light_transition_duration: '{{ var_light_transition_duration }}'
- conditions:
- condition: template
value_template: '{{ scroll_wheel_mode == ''lights: color hue only''
or (scroll_wheel_mode == ''lights: color temp and color hue'' and
state_attr(repeat.item, ''color_temp_kelvin'') is none ) }}'
sequence:
- action: script.light_color_hs_helper
data:
target_light: '{{ repeat.item }}'
light_transition_duration: '{{ var_light_transition_duration }}'
color_hue_step: '{{ var_color_hue_step * scroll_clicks }}'
color_saturation: '{{ var_color_saturation }}'
mode: '{{ iif(scroll_direction == ''left'', ''down'', ''up'') }}'
- conditions:
- condition: template
value_template: '{{ scroll_wheel_mode == ''media: volume control''
and (is_state(repeat.item, ''playing'') or is_state(repeat.item,
''on'') )}}'
sequence:
- if:
- condition: template
value_template: '{{ vars[''scroll_wheel_mode_ext_'' ~ channel] ==
''relaxed'' or not var_volume_use_up_down_for_instant}}'
then:
- action: media_player.volume_set
target:
entity_id: '{{ repeat.item }}'
data:
volume_level: "{{ [[state_attr(repeat.item, 'volume_level')*100
+ \n iif(scroll_direction == 'right', 1, -1)* scroll_clicks
* var_volume_step_pct, var_volume_max_pct] | min, 0] | max /
100\n}}"
else:
- if:
- condition: template
value_template: '{{ scroll_direction == ''left'' }}'
then:
- action: media_player.volume_down
target:
entity_id: '{{ repeat.item }}'
else:
- if:
- condition: template
value_template: '{{ state_attr(repeat.item, ''volume_level'')*100
< var_volume_max_pct }}'
then:
- action: media_player.volume_up
target:
entity_id: '{{ repeat.item }}'
mode: queued
max: !input max_queued_automation_calls
max_exceeded: silent
variables:
vars:
targets_entities_ch1: !input scroll_wheel_target_ch1
targets_entities_ch2: !input scroll_wheel_target_ch2
targets_entities_ch3: !input scroll_wheel_target_ch3
scroll_wheel_mode_ch1: !input scroll_wheel_mode_ch1
scroll_wheel_mode_ch2: !input scroll_wheel_mode_ch2
scroll_wheel_mode_ch3: !input scroll_wheel_mode_ch3
scroll_wheel_mode_ext_ch1: !input scroll_wheel_mode_ext_ch1
scroll_wheel_mode_ext_ch2: !input scroll_wheel_mode_ext_ch2
scroll_wheel_mode_ext_ch3: !input scroll_wheel_mode_ext_ch3
scroll_wheel_mode_dynamic_ch1: !input scroll_wheel_mode_dynamic_ch1
scroll_wheel_mode_dynamic_ch2: !input scroll_wheel_mode_dynamic_ch2
scroll_wheel_mode_dynamic_ch3: !input scroll_wheel_mode_dynamic_ch3
var_min_pct: !input dim_min_pct
var_dim_step_pct: !input dim_step_pct
var_dim_turns_lights_on: !input dim_turns_lights_on
var_color_hue_step: !input color_hue_step
var_color_saturation: !input color_saturation
var_min_color_temp: !input min_color_temp
var_max_color_temp: !input max_color_temp
var_color_temp_step: !input color_temp_step
var_light_transition_duration: !input light_transition_duration
var_on_hold_delay: !input on_hold_delay
var_volume_step_pct: !input volume_step_pct
var_volume_max_pct: !input volume_max_pct
var_volume_use_up_down_for_instant: !input volume_use_up_down_for_instant
var_reset_time_input_select_helpers: !input auto_reset_time_input_select_helpers
last_triggered_seconds: "{{ as_timestamp(now()) - as_timestamp(this.attributes.last_triggered,
0) }} \n"
trigger_variables:
var_remote: !input remote
var_button_ch1: "{% for item in device_entities(var_remote) %} {% set match = item
| regex_findall(find='^event\\..+[a-zA-Z]_3.*$') %} {% if (match | length) > 0
%}\n {{ match[0] }}\n{% endif %} {% endfor %}"
var_scroll_left_ch1: "{% for item in device_entities(var_remote) %} {% set match
= item | regex_findall(find='^event\\..+[a-zA-Z]_2.*$') %} {% if (match | length)
> 0 %}\n {{ match[0] }}\n{% endif %} {% endfor %}"
var_scroll_right_ch1: "{% for item in device_entities(var_remote) %} {% set match
= item | regex_findall(find='^event\\..+[a-zA-Z]_1.*$') %} {% if (match | length)
> 0 %}\n {{ match[0] }}\n{% endif %} {% endfor %}"
var_scroll_left_ext_ch1: "{% for item in device_entities(var_remote) %} {% set match
= item | regex_findall(find='^sensor\\..+[a-zA-Z]_2.*$') %} {% if (match | length)
> 0 %}\n {{ match[0] }}\n{% endif %} {% endfor %}"
var_scroll_right_ext_ch1: "{% for item in device_entities(var_remote) %} {% set
match = item | regex_findall(find='^sensor\\..+[a-zA-Z]_1.*$') %} {% if (match
| length) > 0 %}\n {{ match[0] }}\n{% endif %} {% endfor %}"
var_button_ch2: "{% for item in device_entities(var_remote) %} {% set match = item
| regex_findall(find='^event\\..+[a-zA-Z]_6.*$') %} {% if (match | length) > 0
%}\n {{ match[0] }}\n{% endif %} {% endfor %}"
var_scroll_left_ch2: "{% for item in device_entities(var_remote) %} {% set match
= item | regex_findall(find='^event\\..+[a-zA-Z]_5.*$') %} {% if (match | length)
> 0 %}\n {{ match[0] }}\n{% endif %} {% endfor %}"
var_scroll_right_ch2: "{% for item in device_entities(var_remote) %} {% set match
= item | regex_findall(find='^event\\..+[a-zA-Z]_4.*$') %} {% if (match | length)
> 0 %}\n {{ match[0] }}\n{% endif %} {% endfor %}"
var_scroll_left_ext_ch2: "{% for item in device_entities(var_remote) %} {% set match
= item | regex_findall(find='^sensor\\..+[a-zA-Z]_5.*$') %} {% if (match | length)
> 0 %}\n {{ match[0] }}\n{% endif %} {% endfor %}"
var_scroll_right_ext_ch2: "{% for item in device_entities(var_remote) %} {% set
match = item | regex_findall(find='^sensor\\..+[a-zA-Z]_4.*$') %} {% if (match
| length) > 0 %}\n {{ match[0] }}\n{% endif %} {% endfor %}"
var_button_ch3: "{% for item in device_entities(var_remote) %} {% set match = item
| regex_findall(find='^event\\..+[a-zA-Z]_9.*$') %} {% if (match | length) > 0
%}\n {{ match[0] }}\n{% endif %} {% endfor %}"
var_scroll_left_ch3: "{% for item in device_entities(var_remote) %} {% set match
= item | regex_findall(find='^event\\..+[a-zA-Z]_8.*$') %} {% if (match | length)
> 0 %}\n {{ match[0] }}\n{% endif %} {% endfor %}"
var_scroll_right_ch3: "{% for item in device_entities(var_remote) %} {% set match
= item | regex_findall(find='^event\\..+[a-zA-Z]_7.*$') %} {% if (match | length)
> 0 %}\n {{ match[0] }}\n{% endif %} {% endfor %}"
var_scroll_left_ext_ch3: "{% for item in device_entities(var_remote) %} {% set match
= item | regex_findall(find='^sensor\\..+[a-zA-Z]_8.*$') %} {% if (match | length)
> 0 %}\n {{ match[0] }}\n{% endif %} {% endfor %}"
var_scroll_right_ext_ch3: "{% for item in device_entities(var_remote) %} {% set
match = item | regex_findall(find='^sensor\\..+[a-zA-Z]_7.*$') %} {% if (match
| length) > 0 %}\n {{ match[0] }}\n{% endif %} {% endfor %}"
@@ -0,0 +1,500 @@
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
@@ -0,0 +1,539 @@
blueprint:
name: IKEA Bilresa Scrollwheel - Light Control V1.0.5
description: 'The ultimate LIGHT controller for the IKEA Bilresa Smart Scroll Wheel
via Matter. (NOT for Media/Volume! NOT for the 2-Button Remote!)
VERSION 1.0.5: Included fix to check both the from_state and the to_state. VERSION
1.0.4: Code cleanup. Removed ''short_release'' placebo for multi_press events.
VERSION 1.0.3: Bugfix: Fixed "ghost switching" after Home Assistant restarts.
VERSION 1.0.2: Safety update. Added template fallbacks for RGB/Kelvin shifts.
VERSION 1.0.1: Fixed "Bouncing/Jumping" issues (added debounce logic).
YOU DECIDE EVERYTHING: - 3 Scroll Zones: Choose the function (Brightness, Kelvin,
RGB, Presets). - 3 Buttons x 2 Gestures: Choose a specific action for EACH gesture
(Double/Hold).
REQUIREMENTS: - ENABLE Sensors: 1,2, 4,5, 7,8 - ENABLE Events: 3,6,9 - DISABLE
Sensors: 3,6,9 (Not needed, keep disabled!)
HELPER GROUP: If controlling multiple lights, please always use a HELPER GROUP!
Directly controlling multiple entities can cause sync issues.
'
domain: automation
input:
light_entity:
name: Light (or Group)
selector:
entity:
domain:
- light
multiple: false
reorder: false
scroll_logic_1_2:
name: Scroll Zone 1 (Sensors 1 & 2)
default: brightness
selector:
select:
options:
- label: Brightness (Bright / Dark)
value: brightness
- label: Kelvin (Cool / Warm)
value: kelvin
- label: RGB Color (Rainbow)
value: rgb
- label: Presets (Work / Cozy)
value: preset
custom_value: false
multiple: false
sort: false
scroll_logic_4_5:
name: Scroll Zone 2 (Sensors 4 & 5)
default: kelvin
selector:
select:
options:
- label: Brightness (Bright / Dark)
value: brightness
- label: Kelvin (Cool / Warm)
value: kelvin
- label: RGB Color (Rainbow)
value: rgb
- label: Presets (Work / Cozy)
value: preset
custom_value: false
multiple: false
sort: false
scroll_logic_7_8:
name: Scroll Zone 3 (Sensors 7 & 8)
default: preset
selector:
select:
options:
- label: Brightness (Bright / Dark)
value: brightness
- label: Kelvin (Cool / Warm)
value: kelvin
- label: RGB Color (Rainbow)
value: rgb
- label: Presets (Work / Cozy)
value: preset
custom_value: false
multiple: false
sort: false
btn1_double:
name: 'Button 1: Double Click Action'
default: set_bright
selector:
select:
options:
- label: -> Bright (80%)
value: set_bright
- label: -> Cool White (4000K)
value: set_cold
- label: -> White (RGB Reset)
value: set_white
- label: -> Red (Signal Color)
value: set_red
- label: -> No Action
value: none
custom_value: false
multiple: false
sort: false
btn1_hold:
name: 'Button 1: Hold Action'
default: set_dim
selector:
select:
options:
- label: -> Dimmed (20%)
value: set_dim
- label: -> Warm White (2200K)
value: set_warm
- label: -> Enable Color (Saturation On)
value: set_color_on
- label: -> No Action
value: none
custom_value: false
multiple: false
sort: false
btn2_double:
name: 'Button 2: Double Click Action'
default: set_cold
selector:
select:
options:
- label: -> Bright (80%)
value: set_bright
- label: -> Cool White (4000K)
value: set_cold
- label: -> White (RGB Reset)
value: set_white
- label: -> Red (Signal Color)
value: set_red
- label: -> No Action
value: none
custom_value: false
multiple: false
sort: false
btn2_hold:
name: 'Button 2: Hold Action'
default: set_warm
selector:
select:
options:
- label: -> Dimmed (20%)
value: set_dim
- label: -> Warm White (2200K)
value: set_warm
- label: -> Enable Color (Saturation On)
value: set_color_on
- label: -> No Action
value: none
custom_value: false
multiple: false
sort: false
btn3_double:
name: 'Button 3: Double Click Action'
default: set_bright
selector:
select:
options:
- label: -> Bright (80%)
value: set_bright
- label: -> Cool White (4000K)
value: set_cold
- label: -> White (RGB Reset)
value: set_white
- label: -> Red (Signal Color)
value: set_red
- label: -> No Action
value: none
custom_value: false
multiple: false
sort: false
btn3_hold:
name: 'Button 3: Hold Action'
default: set_dim
selector:
select:
options:
- label: -> Dimmed (20%)
value: set_dim
- label: -> Warm White (2200K)
value: set_warm
- label: -> Enable Color (Saturation On)
value: set_color_on
- label: -> No Action
value: none
custom_value: false
multiple: false
sort: false
step_pct:
name: 'Step Size: Brightness (%)'
default: 10
selector:
number:
min: 1.0
max: 50.0
unit_of_measurement: '%'
step: 1.0
mode: slider
step_kelvin:
name: 'Step Size: Kelvin (K)'
default: 400
selector:
number:
min: 100.0
max: 1000.0
unit_of_measurement: K
step: 1.0
mode: slider
step_hue:
name: 'Step Size: RGB Hue (°)'
default: 15
selector:
number:
min: 5.0
max: 90.0
unit_of_measurement: °
step: 1.0
mode: slider
val_bright:
name: 'Value: Bright (%)'
default: 80
selector:
number:
min: 1.0
max: 100.0
step: 1.0
mode: slider
val_dim:
name: 'Value: Dim (%)'
default: 20
selector:
number:
min: 1.0
max: 100.0
step: 1.0
mode: slider
val_cold:
name: 'Value: Cool (K)'
default: 4000
selector:
number:
min: 2000.0
max: 6500.0
step: 1.0
mode: slider
val_warm:
name: 'Value: Warm (K)'
default: 2200
selector:
number:
min: 2000.0
max: 6500.0
step: 1.0
mode: slider
sensor_1:
name: Sensor 1 (Look for '(1)')
selector:
entity:
domain:
- sensor
multiple: false
reorder: false
sensor_2:
name: Sensor 2 (Look for '(2)')
selector:
entity:
domain:
- sensor
multiple: false
reorder: false
sensor_4:
name: Sensor 4 (Look for '(4)')
selector:
entity:
domain:
- sensor
multiple: false
reorder: false
sensor_5:
name: Sensor 5 (Look for '(5)')
selector:
entity:
domain:
- sensor
multiple: false
reorder: false
sensor_7:
name: Sensor 7 (Look for '(7)')
selector:
entity:
domain:
- sensor
multiple: false
reorder: false
sensor_8:
name: Sensor 8 (Look for '(8)')
selector:
entity:
domain:
- sensor
multiple: false
reorder: false
event_taste_1:
name: Event Button 1 (Look for '(3)')
selector:
entity:
domain:
- event
multiple: false
reorder: false
event_taste_2:
name: Event Button 2 (Look for '(6)')
selector:
entity:
domain:
- event
multiple: false
reorder: false
event_taste_3:
name: Event Button 3 (Look for '(9)')
selector:
entity:
domain:
- event
multiple: false
reorder: false
source_url: https://gist.github.com/stone13/63a778bdbf55efbd712ade1d074e9dbb
mode: single
max_exceeded: silent
variables:
target_light: !input light_entity
m_sc_1: !input scroll_logic_1_2
m_sc_2: !input scroll_logic_4_5
m_sc_3: !input scroll_logic_7_8
b1_d: !input btn1_double
b1_h: !input btn1_hold
b2_d: !input btn2_double
b2_h: !input btn2_hold
b3_d: !input btn3_double
b3_h: !input btn3_hold
s_p: !input step_pct
s_k: !input step_kelvin
s_h: !input step_hue
v_bri: !input val_bright
v_dim: !input val_dim
v_cold: !input val_cold
v_warm: !input val_warm
id_e1: !input event_taste_1
id_e2: !input event_taste_2
id_e3: !input event_taste_3
triggers:
- trigger: state
entity_id: !input sensor_1
id: sc_1_up
- trigger: state
entity_id: !input sensor_2
id: sc_1_down
- trigger: state
entity_id: !input sensor_4
id: sc_2_down
- trigger: state
entity_id: !input sensor_5
id: sc_2_up
- trigger: state
entity_id: !input sensor_7
id: sc_3_up
- trigger: state
entity_id: !input sensor_8
id: sc_3_down
- trigger: state
entity_id:
- !input event_taste_1
- !input event_taste_2
- !input event_taste_3
id: btn_event
conditions:
- condition: template
value_template: "{{ trigger.from_state is defined and trigger.to_state is defined
and\n trigger.from_state.state not in ['unknown', 'unavailable'] and\n trigger.to_state.state
not in ['unknown', 'unavailable'] }}"
actions:
- choose:
- conditions: '{{ ''sc_'' in trigger.id }}'
sequence:
- variables:
direction: '{% if ''_up'' in trigger.id %} 1 {% else %} -1 {% endif %}
'
active_func: '{% if ''sc_1'' in trigger.id %} {{ m_sc_1 }} {% elif ''sc_2''
in trigger.id %} {{ m_sc_2 }} {% elif ''sc_3'' in trigger.id %} {{ m_sc_3
}} {% endif %}
'
- choose:
- conditions: '{{ active_func == ''brightness'' }}'
sequence:
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
brightness_step_pct: '{{ (s_p | float(default=10)) * direction }}'
transition: 0
- conditions: '{{ active_func == ''kelvin'' }}'
sequence:
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
color_temp_kelvin: '{% set current = state_attr(target_light, ''color_temp_kelvin'')
| int(default=3000) %} {% set step = s_k | int(default=400) %} {% if
direction == 1 %} {{ ([current + step, 6500] | min) | int }} {% else
%} {{ ([current - step, 2000] | max) | int }} {% endif %}
'
transition: 0
- conditions: '{{ active_func == ''rgb'' }}'
sequence:
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
hs_color: '{% set current_hs = state_attr(target_light, ''hs_color'')
%} {% set h = current_hs[0] | float(default=0) if current_hs is not
none else 0 %} {% set s = current_hs[1] | float(default=100) if current_hs
is not none else 100 %} {% set step = s_h | float(default=15) %} {{
[(h + (step * direction)) % 360, s] }}
'
transition: 0
- conditions: '{{ active_func == ''preset'' }}'
sequence:
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
brightness_pct: '{{ v_bri if direction == 1 else v_dim }}'
color_temp_kelvin: '{{ v_cold if direction == 1 else v_warm }}'
transition: 1
- choose:
- conditions: '{{ trigger.id == ''btn_event'' }}'
sequence:
- choose:
- conditions: "{{ trigger.to_state.attributes.event_type is defined and \n trigger.to_state.attributes.event_type
== 'multi_press_1' }}\n"
sequence:
- action: light.toggle
target:
entity_id: '{{ target_light }}'
data:
transition: 0.5
- choose:
- conditions: "{{ trigger.to_state.attributes.event_type is defined and \n trigger.to_state.attributes.event_type
== 'multi_press_2' }}\n"
sequence:
- variables:
action_code: '{% if trigger.entity_id == id_e1 %} {{ b1_d }} {% elif trigger.entity_id
== id_e2 %} {{ b2_d }} {% elif trigger.entity_id == id_e3 %} {{ b3_d
}} {% endif %}
'
- choose:
- conditions: '{{ action_code == ''set_bright'' }}'
sequence:
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
brightness_pct: '{{ v_bri }}'
transition: 0.5
- conditions: '{{ action_code == ''set_cold'' }}'
sequence:
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
color_temp_kelvin: '{{ v_cold }}'
transition: 0.5
- conditions: '{{ action_code == ''set_white'' }}'
sequence:
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
color_temp_kelvin: 4000
transition: 0.5
- conditions: '{{ action_code == ''set_red'' }}'
sequence:
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
hs_color:
- 0
- 100
brightness_pct: 80
transition: 0.5
- choose:
- conditions: "{{ trigger.to_state.attributes.event_type is defined and \n trigger.to_state.attributes.event_type
== 'long_press' }}\n"
sequence:
- variables:
action_code: '{% if trigger.entity_id == id_e1 %} {{ b1_h }} {% elif trigger.entity_id
== id_e2 %} {{ b2_h }} {% elif trigger.entity_id == id_e3 %} {{ b3_h
}} {% endif %}
'
- choose:
- conditions: '{{ action_code == ''set_dim'' }}'
sequence:
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
brightness_pct: '{{ v_dim }}'
transition: 0.5
- conditions: '{{ action_code == ''set_warm'' }}'
sequence:
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
color_temp_kelvin: '{{ v_warm }}'
transition: 0.5
- conditions: '{{ action_code == ''set_color_on'' }}'
sequence:
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
hs_color:
- 0
- 100
brightness_pct: 80
transition: 0.5
- delay:
milliseconds: 150
@@ -0,0 +1,920 @@
blueprint:
name: IKEA Bilresa Scrollwheel - Light Control
author: thetestspecimen
source_url: https://github.com/thetestspecimen/homeassistant/blob/main/blueprints/ikea-bilresa-scroll-wheel.yaml
description: "\n# \U0001F4A1 Light controller \U0001F4A1\n## IKEA BILRESA - Smart
Scroll Wheel utilising Matter over Thread\n\n\n## VARIABLES:\n- 3 Scroll Zones:
Choose the function (Brightness, Kelvin, RGB, Presets).\n- 3 Buttons x 3 Gestures:
Choose a specific action for EACH gesture (double click / triple click / hold).\n\n***Note:***
single click will always toggle the light on or off.\n\n## REQUIREMENTS:\n\nFor
optimal behavior, the following settings must be applied:\n- ENABLE Events: 3,6,9
(in section \"Events\")\n- ENABLE Sensors: 1,2,4,5,7,8 (in section \"Diagnostic\")\n-
DISABLE Events: 1,2,4,5,7,8 (in section \"Events\")\n- DISABLE Sensors: 3,6,9
(in section \"Diagnostic\")\n\n***Note:*** this blueprint cannot be used with
the BILRESA 2-Button Remote.\n"
homeassistant:
min_version: 2024.6.0
domain: automation
input:
header_entity_mapping:
name: Entity Mapping [REQUIRED]
description: Select the entities for the scroll wheel and buttons
default: ''
selector:
constant:
value: ''
group_buttons:
name: Buttons
icon: mdi:gesture-tap
collapsed: true
input:
event_button_1:
name: 'Channel 1: Button'
description: 'Event: Look for ''Button (3)'''
selector:
entity:
filter:
- domain:
- event
integration: matter
device_class:
- button
multiple: false
reorder: false
event_button_2:
name: 'Channel 2: Button'
description: 'Event: Look for ''Button (6)'''
selector:
entity:
filter:
- domain:
- event
integration: matter
device_class:
- button
multiple: false
reorder: false
event_button_3:
name: 'Channel 3: Button'
description: 'Event: Look for ''Button (9)'''
selector:
entity:
filter:
- domain:
- event
integration: matter
device_class:
- button
multiple: false
reorder: false
group_scroll_wheel:
name: Scroll Wheel
icon: mdi:rotate-right
collapsed: true
input:
sensor_1:
name: 'Channel 1: Scroll forwards'
description: 'Sensor: Look for ''Current switch position (1)'''
selector:
entity:
filter:
- domain:
- sensor
integration: matter
multiple: false
reorder: false
sensor_2:
name: 'Channel 1: Scroll backwards'
description: 'Sensor: Look for ''Current switch position (2)'''
selector:
entity:
filter:
- domain:
- sensor
integration: matter
multiple: false
reorder: false
sensor_4:
name: 'Channel 2: Scroll forwards'
description: 'Sensor: Look for ''Current switch position (4)'''
selector:
entity:
filter:
- domain:
- sensor
integration: matter
multiple: false
reorder: false
sensor_5:
name: 'Channel 2: Scroll backwards'
description: 'Sensor: Look for ''Current switch position (5)'''
selector:
entity:
filter:
- domain:
- sensor
integration: matter
multiple: false
reorder: false
sensor_7:
name: 'Channel 3: Scroll forwards'
description: 'Sensor: Look for ''Current switch position (7)'''
selector:
entity:
filter:
- domain:
- sensor
integration: matter
multiple: false
reorder: false
sensor_8:
name: 'Channel 3: Scroll backwards'
description: 'Sensor: Look for ''Current switch position (8)'''
selector:
entity:
filter:
- domain:
- sensor
integration: matter
multiple: false
reorder: false
header_default_light:
name: Default Light
default: ''
selector:
constant:
value: ''
global_light:
name: Global Light - Default [REQUIRED]
description: This light will be used for any zone or button that does not have
a specific override light set below.
selector:
entity:
domain:
- light
multiple: true
reorder: false
header_1:
name: Channel 1
default: ''
selector:
constant:
value: ''
group_scroll_1:
name: Scroll Wheel Settings
icon: mdi:rotate-right
collapsed: true
input:
light_z1:
name: Light (Scroll - Channel 1) - Optional Override
default: ''
selector:
entity:
domain:
- light
multiple: true
reorder: false
scroll_logic_1_2:
name: Scroll - Channel 1
description: Sensors 1 & 2
default: brightness
selector:
select:
options:
- label: Brightness (Bright / Dark)
value: brightness
- label: Kelvin (Cool / Warm)
value: kelvin
- label: RGB Colour (Rainbow)
value: rgb
- label: Presets (Work / Cozy)
value: preset
sort: false
custom_value: false
multiple: false
group_button_1_single:
name: 'Button 1: Single Click (Toggle On/Off)'
icon: mdi:gesture-tap
collapsed: true
input:
light_b1_single:
name: Light (Button 1 Single Click) - Optional Override
default: ''
selector:
entity:
domain:
- light
multiple: true
reorder: false
group_button_1_double:
name: 'Button 1: Double Click'
icon: mdi:gesture-double-tap
collapsed: true
input:
light_b1_double:
name: Light (Button 1 Double Click) - Optional Override
default: ''
selector:
entity:
domain:
- light
multiple: true
reorder: false
btn1_double_bright:
name: 'Button 1: Double Click Brightness (%)'
default: 80
selector:
number:
min: 1.0
max: 100.0
step: 1.0
mode: slider
btn1_double_temp:
name: 'Button 1: Double Click Colour Temp (K)'
default: 4000
selector:
color_temp:
unit: kelvin
min: 2000
max: 6500
group_button_1_triple:
name: 'Button 1: Triple Click'
icon: mdi:gesture-tap
collapsed: true
input:
light_b1_triple:
name: Light (Button 1 Triple Click) - Optional Override
default: ''
selector:
entity:
domain:
- light
multiple: true
reorder: false
btn1_triple_bright:
name: 'Button 1: Triple Click Brightness (%)'
default: 100
selector:
number:
min: 1.0
max: 100.0
step: 1.0
mode: slider
btn1_triple_temp:
name: 'Button 1: Triple Click Colour Temp (K)'
default: 6000
selector:
color_temp:
unit: kelvin
min: 2000
max: 6500
group_button_1_hold:
name: 'Button 1: Hold'
icon: mdi:gesture-tap-hold
collapsed: true
input:
light_b1_hold:
name: Light (Button 1 Hold) - Optional Override
default: ''
selector:
entity:
domain:
- light
multiple: true
reorder: false
btn1_hold_bright:
name: 'Button 1: Hold Brightness (%)'
default: 20
selector:
number:
min: 1.0
max: 100.0
step: 1.0
mode: slider
btn1_hold_temp:
name: 'Button 1: Hold Colour Temp'
default: 2200
selector:
color_temp:
unit: kelvin
min: 2000
max: 6500
header_2:
name: Channel 2
default: ''
selector:
constant:
value: ''
group_scroll_2:
name: Scroll Wheel Settings
icon: mdi:rotate-right
collapsed: true
input:
light_z2:
name: Light (Scroll - Channel 2) - Optional Override
default: ''
selector:
entity:
domain:
- light
multiple: true
reorder: false
scroll_logic_4_5:
name: Scroll - Channel 2
description: Sensors 4 & 5
default: kelvin
selector:
select:
options:
- label: Brightness (Bright / Dark)
value: brightness
- label: Kelvin (Cool / Warm)
value: kelvin
- label: RGB Colour (Rainbow)
value: rgb
- label: Presets (Work / Cozy)
value: preset
sort: false
custom_value: false
multiple: false
group_button_2_single:
name: 'Button 2: Single Click (Toggle On/Off)'
icon: mdi:gesture-tap
collapsed: true
input:
light_b2_single:
name: Light (Button 2 Single Click) - Optional Override
default: ''
selector:
entity:
domain:
- light
multiple: true
reorder: false
group_button_2_double:
name: 'Button 2: Double Click'
icon: mdi:gesture-double-tap
collapsed: true
input:
light_b2_double:
name: Light (Button 2 Double Click) - Optional Override
default: ''
selector:
entity:
domain:
- light
multiple: true
reorder: false
btn2_double_bright:
name: 'Button 2: Double Click Brightness (%)'
default: 80
selector:
number:
min: 1.0
max: 100.0
step: 1.0
mode: slider
btn2_double_temp:
name: 'Button 2: Double Click Colour Temp (K)'
default: 4000
selector:
color_temp:
unit: kelvin
min: 2000
max: 6500
group_button_2_triple:
name: 'Button 2: Triple Click'
icon: mdi:gesture-tap
collapsed: true
input:
light_b2_triple:
name: Light (Button 2 Triple Click) - Optional Override
default: ''
selector:
entity:
domain:
- light
multiple: true
reorder: false
btn2_triple_bright:
name: 'Button 2: Triple Click Brightness (%)'
default: 100
selector:
number:
min: 1.0
max: 100.0
step: 1.0
mode: slider
btn2_triple_temp:
name: 'Button 2: Triple Click Colour Temp (K)'
default: 6000
selector:
color_temp:
unit: kelvin
min: 2000
max: 6500
group_button_2_hold:
name: 'Button 2: Hold'
icon: mdi:gesture-tap-hold
collapsed: true
input:
light_b2_hold:
name: Light (Button 2 Hold) - Optional Override
default: ''
selector:
entity:
domain:
- light
multiple: true
reorder: false
btn2_hold_bright:
name: 'Button 2: Hold Brightness (%)'
default: 20
selector:
number:
min: 1.0
max: 100.0
step: 1.0
mode: slider
btn2_hold_temp:
name: 'Button 2: Hold Colour Temp (K)'
default: 2200
selector:
color_temp:
unit: kelvin
min: 2000
max: 6500
header_3:
name: Channel 3
default: ''
selector:
constant:
value: ''
group_scroll_3:
name: Scroll Wheel Settings
icon: mdi:rotate-right
collapsed: true
input:
light_z3:
name: Light (Scroll - Channel 3) - Optional Override
default: ''
selector:
entity:
domain:
- light
multiple: true
reorder: false
scroll_logic_7_8:
name: Scroll - Channel 3
description: Sensors 7 & 8
default: preset
selector:
select:
options:
- label: Brightness (Bright / Dark)
value: brightness
- label: Kelvin (Cool / Warm)
value: kelvin
- label: RGB Colour (Rainbow)
value: rgb
- label: Presets (Work / Cozy)
value: preset
sort: false
custom_value: false
multiple: false
group_button_3_single:
name: 'Button 3: Single Click (Toggle On/Off)'
icon: mdi:gesture-tap
collapsed: true
input:
light_b3_single:
name: Light (Button 3 Single Click) - Optional Override
default: ''
selector:
entity:
domain:
- light
multiple: true
reorder: false
group_button_3_double:
name: 'Button 3: Double Click'
icon: mdi:gesture-double-tap
collapsed: true
input:
light_b3_double:
name: Light (Button 3 Double Click) - Optional Override
default: ''
selector:
entity:
domain:
- light
multiple: true
reorder: false
btn3_double_bright:
name: 'Button 3: Double Click Brightness (%)'
default: 80
selector:
number:
min: 1.0
max: 100.0
step: 1.0
mode: slider
btn3_double_temp:
name: 'Button 3: Double Click Colour Temp (K)'
default: 4000
selector:
color_temp:
unit: kelvin
min: 2000
max: 6500
group_button_3_triple:
name: 'Button 3: Triple Click'
icon: mdi:gesture-tap
collapsed: true
input:
light_b3_triple:
name: Light (Button 3 Triple Click) - Optional Override
default: ''
selector:
entity:
domain:
- light
multiple: true
reorder: false
btn3_triple_bright:
name: 'Button 3: Triple Click Brightness (%)'
default: 100
selector:
number:
min: 1.0
max: 100.0
step: 1.0
mode: slider
btn3_triple_temp:
name: 'Button 3: Triple Click Colour Temp (K)'
default: 6000
selector:
color_temp:
unit: kelvin
min: 2000
max: 6500
group_button_3_hold:
name: 'Button 3: Hold'
icon: mdi:gesture-tap-hold
collapsed: true
input:
light_b3_hold:
name: Light (Button 3 Hold) - Optional Override
default: ''
selector:
entity:
domain:
- light
multiple: true
reorder: false
btn3_hold_bright:
name: 'Button 3: Hold Brightness (%)'
default: 20
selector:
number:
min: 1.0
max: 100.0
step: 1.0
mode: slider
btn3_hold_temp:
name: 'Button 3: Hold Colour Temp (K)'
default: 2200
selector:
color_temp:
unit: kelvin
min: 2000
max: 6500
header_tuning:
name: Fine Tuning
default: ''
selector:
constant:
value: ''
group_step_size:
name: Step Sizes
icon: mdi:tune
collapsed: true
input:
step_pct:
name: 'Step Size: Brightness (%)'
default: 10
selector:
number:
min: 1.0
max: 50.0
unit_of_measurement: '%'
step: 1.0
mode: slider
step_kelvin:
name: 'Step Size: Kelvin (K)'
default: 400
selector:
number:
min: 100.0
max: 1000.0
unit_of_measurement: K
step: 1.0
mode: slider
step_hue:
name: 'Step Size: RGB Hue (°)'
default: 15
selector:
number:
min: 5.0
max: 90.0
unit_of_measurement: °
step: 1.0
mode: slider
group_values:
name: Preset Values
icon: mdi:brightness-6
collapsed: true
input:
val_bright:
name: 'Work: Brightness (%)'
description: The brightness value to use for the 'Work' preset
default: 80
selector:
number:
min: 1.0
max: 100.0
step: 1.0
mode: slider
val_cold:
name: 'Work: Colour Temperature (K)'
description: The colour temperature value to use for the 'Work' preset
default: 4000
selector:
color_temp:
unit: kelvin
min: 2000
max: 6500
val_dim:
name: 'Cozy: Brightness (%)'
description: The brightness value to use for the 'Cozy' preset
default: 20
selector:
number:
min: 1.0
max: 100.0
step: 1.0
mode: slider
val_warm:
name: 'Cozy: Colour Temperature (K)'
description: The colour temperature value to use for the 'Cozy' preset
default: 2200
selector:
color_temp:
unit: kelvin
min: 2000
max: 6500
group_timings:
name: Timings
icon: mdi:clock
collapsed: true
input:
transition_time:
name: Button Transition Time (Seconds)
default: 0.5
selector:
number:
min: 0.0
max: 5.0
step: 0.1
mode: slider
delay_time:
name: Delay (Milliseconds)
description: Delay between command actions. This is effectively button debounce.
default: 150
selector:
number:
min: 0.0
max: 1000.0
step: 10.0
mode: slider
mode: single
max_exceeded: silent
variables:
global_l: !input global_light
l_z1: !input light_z1
l_z2: !input light_z2
l_z3: !input light_z3
l_b1_s: !input light_b1_single
l_b1_d: !input light_b1_double
l_b1_t: !input light_b1_triple
l_b1_h: !input light_b1_hold
l_b2_s: !input light_b2_single
l_b2_d: !input light_b2_double
l_b2_t: !input light_b2_triple
l_b2_h: !input light_b2_hold
l_b3_s: !input light_b3_single
l_b3_d: !input light_b3_double
l_b3_t: !input light_b3_triple
l_b3_h: !input light_b3_hold
m_sc_1: !input scroll_logic_1_2
m_sc_2: !input scroll_logic_4_5
m_sc_3: !input scroll_logic_7_8
b1_d_b: !input btn1_double_bright
b1_d_t: !input btn1_double_temp
b1_t_b: !input btn1_triple_bright
b1_t_t: !input btn1_triple_temp
b1_h_b: !input btn1_hold_bright
b1_h_t: !input btn1_hold_temp
b2_d_b: !input btn2_double_bright
b2_d_t: !input btn2_double_temp
b2_t_b: !input btn2_triple_bright
b2_t_t: !input btn2_triple_temp
b2_h_b: !input btn2_hold_bright
b2_h_t: !input btn2_hold_temp
b3_d_b: !input btn3_double_bright
b3_d_t: !input btn3_double_temp
b3_t_b: !input btn3_triple_bright
b3_t_t: !input btn3_triple_temp
b3_h_b: !input btn3_hold_bright
b3_h_t: !input btn3_hold_temp
s_p: !input step_pct
s_k: !input step_kelvin
s_h: !input step_hue
v_bri: !input val_bright
v_dim: !input val_dim
v_cold: !input val_cold
v_warm: !input val_warm
t_time: !input transition_time
d_time: !input delay_time
id_e1: !input event_button_1
id_e2: !input event_button_2
id_e3: !input event_button_3
triggers:
- trigger: state
entity_id: !input sensor_1
id: sc_1_up
- trigger: state
entity_id: !input sensor_2
id: sc_1_down
- trigger: state
entity_id: !input sensor_4
id: sc_2_down
- trigger: state
entity_id: !input sensor_5
id: sc_2_up
- trigger: state
entity_id: !input sensor_7
id: sc_3_up
- trigger: state
entity_id: !input sensor_8
id: sc_3_down
- trigger: state
entity_id:
- !input event_button_1
- !input event_button_2
- !input event_button_3
id: btn_event
conditions:
- condition: template
value_template: "{{ trigger.from_state is defined and trigger.to_state is defined
and\n trigger.from_state.state not in ['unknown', 'unavailable'] and\n trigger.to_state.state
not in ['unknown', 'unavailable'] and\n trigger.from_state.state != trigger.to_state.state
}}"
actions:
- choose:
- conditions: '{{ ''sc_'' in trigger.id }}'
sequence:
- variables:
target_light: '{{ l_z1 if ''sc_1'' in trigger.id and l_z1 else l_z2 if ''sc_2''
in trigger.id and l_z2 else l_z3 if ''sc_3'' in trigger.id and l_z3 else
global_l }}'
direction: '{{ 1 if ''_up'' in trigger.id else -1 }}'
active_func: '{{ m_sc_1 if ''sc_1'' in trigger.id else m_sc_2 if ''sc_2''
in trigger.id else m_sc_3 }}'
- choose:
- conditions: '{{ active_func == ''brightness'' }}'
sequence:
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
brightness_step_pct: '{{ (s_p | float(default=10)) * direction }}'
transition: 0
- conditions: '{{ active_func == ''kelvin'' }}'
sequence:
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
color_temp_kelvin: '{% set entities = expand(target_light) | map(attribute=''entity_id'')
| list %} {% set first_light = entities[0] if entities else ''light.none''
%} {% set current = state_attr(first_light, ''color_temp_kelvin'') |
int(default=3000) %} {% set step = s_k | int(default=400) %} {% set
min_k = state_attr(first_light, ''min_color_temp_kelvin'') | int(default=2000)
%} {% set max_k = state_attr(first_light, ''max_color_temp_kelvin'')
| int(default=6500) %} {% if direction == 1 %} {{ ([current + step,
max_k] | min) | int }} {% else %} {{ ([current - step, min_k] | max)
| int }} {% endif %}
'
transition: 0
- conditions: '{{ active_func == ''rgb'' }}'
sequence:
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
hs_color: '{% set entities = expand(target_light) | map(attribute=''entity_id'')
| list %} {% set first_light = entities[0] if entities else ''light.none''
%} {% set current_hs = state_attr(first_light, ''hs_color'') %} {% set
h = current_hs[0] | float(default=0) if current_hs is not none else
0 %} {% set s = current_hs[1] | float(default=100) if current_hs is
not none else 100 %} {% set step = s_h | float(default=15) %} {{ [(h
+ (step * direction)) % 360, s] }}
'
transition: 0
- conditions: '{{ active_func == ''preset'' }}'
sequence:
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
brightness_pct: '{{ v_bri if direction == 1 else v_dim }}'
color_temp_kelvin: '{{ v_cold if direction == 1 else v_warm }}'
transition: 1
- choose:
- conditions: '{{ trigger.id == ''btn_event'' }}'
sequence:
- choose:
- conditions: "{{ trigger.to_state.attributes.event_type is defined and \n trigger.to_state.attributes.event_type
== 'multi_press_1' }}\n"
sequence:
- variables:
target_light: '{{ l_b1_s if trigger.entity_id == id_e1 and l_b1_s else
l_b2_s if trigger.entity_id == id_e2 and l_b2_s else l_b3_s if trigger.entity_id
== id_e3 and l_b3_s else global_l }}'
- action: light.toggle
target:
entity_id: '{{ target_light }}'
data:
transition: '{{ t_time }}'
- choose:
- conditions: "{{ trigger.to_state.attributes.event_type is defined and \n trigger.to_state.attributes.event_type
== 'multi_press_2' }}\n"
sequence:
- variables:
target_light: '{{ l_b1_d if trigger.entity_id == id_e1 and l_b1_d else
l_b2_d if trigger.entity_id == id_e2 and l_b2_d else l_b3_d if trigger.entity_id
== id_e3 and l_b3_d else global_l }}'
set_bri: '{{ b1_d_b if trigger.entity_id == id_e1 else b2_d_b if trigger.entity_id
== id_e2 else b3_d_b }}'
set_temp: '{{ b1_d_t if trigger.entity_id == id_e1 else b2_d_t if trigger.entity_id
== id_e2 else b3_d_t }}'
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
brightness_pct: '{{ set_bri }}'
color_temp_kelvin: '{{ set_temp }}'
transition: '{{ t_time }}'
- choose:
- conditions: "{{ trigger.to_state.attributes.event_type is defined and \n trigger.to_state.attributes.event_type
== 'multi_press_3' }}\n"
sequence:
- variables:
target_light: '{{ l_b1_t if trigger.entity_id == id_e1 and l_b1_t else
l_b2_t if trigger.entity_id == id_e2 and l_b2_t else l_b3_t if trigger.entity_id
== id_e3 and l_b3_t else global_l }}'
set_bri: '{{ b1_t_b if trigger.entity_id == id_e1 else b2_t_b if trigger.entity_id
== id_e2 else b3_t_b }}'
set_temp: '{{ b1_t_t if trigger.entity_id == id_e1 else b2_t_t if trigger.entity_id
== id_e2 else b3_t_t }}'
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
brightness_pct: '{{ set_bri }}'
color_temp_kelvin: '{{ set_temp }}'
transition: '{{ t_time }}'
- choose:
- conditions: "{{ trigger.to_state.attributes.event_type is defined and \n trigger.to_state.attributes.event_type
== 'long_press' }}\n"
sequence:
- variables:
target_light: '{{ l_b1_h if trigger.entity_id == id_e1 and l_b1_h else
l_b2_h if trigger.entity_id == id_e2 and l_b2_h else l_b3_h if trigger.entity_id
== id_e3 and l_b3_h else global_l }}'
set_bri: '{{ b1_h_b if trigger.entity_id == id_e1 else b2_h_b if trigger.entity_id
== id_e2 else b3_h_b }}'
set_temp: '{{ b1_h_t if trigger.entity_id == id_e1 else b2_h_t if trigger.entity_id
== id_e2 else b3_h_t }}'
- action: light.turn_on
target:
entity_id: '{{ target_light }}'
data:
brightness_pct: '{{ set_bri }}'
color_temp_kelvin: '{{ set_temp }}'
transition: '{{ t_time }}'
- delay:
milliseconds: '{{ d_time }}'