Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save jamesj2/65cf4760cbb3a7bd3af1b7906f8a3fe2 to your computer and use it in GitHub Desktop.

Select an option

Save jamesj2/65cf4760cbb3a7bd3af1b7906f8a3fe2 to your computer and use it in GitHub Desktop.
Patched IKEA BILRESA Matter scroll wheel blueprint with idle-event guard
blueprint:
name: IKEA BILRESA - Light Controller (Local Patched)
description: >
Local, patched copy of the SvKRO IKEA BILRESA Matter scroll wheel blueprint.
It preserves the original multi-channel light controller behavior while
filtering out non-user state changes from the BILRESA event entities.
domain: automation
source_url: https://gist.github.com/SvKRO/52f246362a19f0297fd18969b383181b
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
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 Temperature
value: temp_std
- label: Color Temperature (Philips Hue)
value: temp_hue
- label: Color (Hue)
value: hue_std
- label: Color (Philips Hue)
value: hue_hue
input_sens_1:
name: Step Size
description: Determine how much the value changes per scroll step.
default: 5
selector:
number:
min: 1
max: 20
unit_of_measurement: "%"
mode: slider
input_transition_1:
name: Scroll Transition
description: Transition duration in seconds for scroll-based changes.
default: 0
selector:
number:
min: 0
max: 120
step: 1
unit_of_measurement: s
mode: slider
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
input_scenes_1:
name: Scene Selection
description: Select the scenes to cycle through sequentially.
default: []
selector:
entity:
domain: scene
multiple: true
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 Temperature
value: temp_std
- label: Color Temperature (Philips Hue)
value: temp_hue
- label: Color (Hue)
value: hue_std
- label: Color (Philips Hue)
value: hue_hue
input_sens_2:
name: Step Size
description: Determine how much the value changes per scroll step.
default: 5
selector:
number:
min: 1
max: 20
unit_of_measurement: "%"
mode: slider
input_transition_2:
name: Scroll Transition
description: Transition duration in seconds for scroll-based changes.
default: 0
selector:
number:
min: 0
max: 120
step: 1
unit_of_measurement: s
mode: slider
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
input_scenes_2:
name: Scene Selection
description: Select the scenes to cycle through sequentially.
default: []
selector:
entity:
domain: scene
multiple: true
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 Temperature
value: temp_std
- label: Color Temperature (Philips Hue)
value: temp_hue
- label: Color (Hue)
value: hue_std
- label: Color (Philips Hue)
value: hue_hue
input_sens_3:
name: Step Size
description: Determine how much the value changes per scroll step.
default: 5
selector:
number:
min: 1
max: 20
unit_of_measurement: "%"
mode: slider
input_transition_3:
name: Scroll Transition
description: Transition duration in seconds for scroll-based changes.
default: 0
selector:
number:
min: 0
max: 120
step: 1
unit_of_measurement: s
mode: slider
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
input_scenes_3:
name: Scene Selection
description: Select the scenes to cycle through sequentially.
default: []
selector:
entity:
domain: scene
multiple: true
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: {}
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:
- platform: event
event_type: state_changed
event_data:
entity_id: "{{ temp_kanal1_links }}"
id: trigger_kanal1_links
- platform: event
event_type: state_changed
event_data:
entity_id: "{{ temp_kanal1_rechts }}"
id: trigger_kanal1_rechts
- platform: event
event_type: state_changed
event_data:
entity_id: "{{ temp_kanal1_klick }}"
id: trigger_kanal1_klick
- platform: event
event_type: state_changed
event_data:
entity_id: "{{ temp_kanal2_links }}"
id: trigger_kanal2_links
- platform: event
event_type: state_changed
event_data:
entity_id: "{{ temp_kanal2_rechts }}"
id: trigger_kanal2_rechts
- platform: event
event_type: state_changed
event_data:
entity_id: "{{ temp_kanal2_klick }}"
id: trigger_kanal2_klick
- platform: event
event_type: state_changed
event_data:
entity_id: "{{ temp_kanal3_links }}"
id: trigger_kanal3_links
- platform: event
event_type: state_changed
event_data:
entity_id: "{{ temp_kanal3_rechts }}"
id: trigger_kanal3_rechts
- platform: event
event_type: state_changed
event_data:
entity_id: "{{ temp_kanal3_klick }}"
id: trigger_kanal3_klick
action:
- 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
t_1: !input input_transition_1
t_2: !input input_transition_2
t_3: !input input_transition_3
l_1: !input input_light_1
l_2: !input input_light_2
l_3: !input input_light_3
action_kanal_id: "{{ trigger.id.split('_')[1] }}"
old_state_obj: "{{ trigger.event.data.old_state if trigger.event.data.old_state is defined else none }}"
new_state_obj: "{{ trigger.event.data.new_state if trigger.event.data.new_state is defined else none }}"
old_state_val: "{{ old_state_obj.state if old_state_obj is not none else none }}"
new_state_val: "{{ new_state_obj.state if new_state_obj is not none else none }}"
old_attr: "{{ old_state_obj.attributes if old_state_obj is not none else dict() }}"
new_attr: "{{ new_state_obj.attributes if new_state_obj is not none else dict() }}"
event_attr: "{{ new_attr }}"
ticks: "{{ new_attr.totalNumberOfPressesCounted | default(1) | int }}"
action_event_type: "{{ new_attr.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 %}
curr_transition_raw: >
{% if action_kanal_id == 'kanal1' %}
{{ t_1 }}
{% elif action_kanal_id == 'kanal2' %}
{{ t_2 }}
{% else %}
{{ t_3 }}
{% endif %}
curr_transition: "{{ curr_transition_raw | int(0) }}"
step_val: >
{% set val = base_sens * ticks %}
{{ val if trigger.id.endswith('_rechts') else val * -1 }}
curr_target_raw: >
{% if action_kanal_id == 'kanal1' %}
{{ l_1 }}
{% elif action_kanal_id == 'kanal2' %}
{{ l_2 }}
{% else %}
{{ l_3 }}
{% endif %}
curr_target: "{{ curr_target_raw.entity_id if curr_target_raw.entity_id is defined else curr_target_raw }}"
state_payload_valid: >
{{
old_state_obj is not none
and new_state_obj is not none
and old_state_val not in [none, 'unknown', 'unavailable']
and new_state_val not in [none, 'unknown', 'unavailable']
}}
event_change_detected: >
{{
old_state_val != new_state_val
or (old_attr.totalNumberOfPressesCounted | default(-1) | int)
!= (new_attr.totalNumberOfPressesCounted | default(-1) | int)
or (old_attr.event_type | default(''))
!= (new_attr.event_type | default(''))
}}
scroll_payload_present: >
{{
new_attr.totalNumberOfPressesCounted is defined
or (new_attr.event_type | default('')) not in ['', 'none', 'unknown', 'unavailable']
}}
button_payload_present: >
{{
action_event_type in [
'multi_press_1',
'multi_press_2',
'multi_press_3',
'long_press',
'long_release'
]
}}
- choose:
- conditions: >
{{
(trigger.id.endswith('_rechts') or trigger.id.endswith('_links'))
and state_payload_valid
and event_change_detected
and scroll_payload_present
}}
sequence:
- choose:
- conditions: "{{ curr_mode == 'brightness' }}"
sequence:
- service: light.turn_on
target:
entity_id: "{{ curr_target }}"
data:
brightness_step_pct: "{{ step_val }}"
transition: "{{ curr_transition }}"
- conditions: "{{ curr_mode == 'temp_std' }}"
sequence:
- service: light.turn_on
target:
entity_id: "{{ curr_target }}"
data:
color_temp_step_pct: "{{ step_val }}"
transition: "{{ curr_transition }}"
- conditions: "{{ curr_mode == 'temp_hue' }}"
sequence:
- service: light.turn_on
target:
entity_id: "{{ curr_target }}"
data:
transition: "{{ curr_transition }}"
color_temp: >
{% set curr = state_attr(curr_target, 'color_temp') | default(300) | int %}
{% set min = state_attr(curr_target, 'min_mireds') | default(153) | int %}
{% set max = state_attr(curr_target, 'max_mireds') | default(500) | int %}
{% set next = curr - (step_val * 5) %}
{{ ([min, next, max] | sort)[1] | int }}
- conditions: "{{ curr_mode == 'hue_std' }}"
sequence:
- service: light.turn_on
target:
entity_id: "{{ curr_target }}"
data:
hue_step: "{{ step_val }}"
transition: "{{ curr_transition }}"
- conditions: "{{ curr_mode == 'hue_hue' }}"
sequence:
- service: light.turn_on
target:
entity_id: "{{ curr_target }}"
data:
transition: "{{ curr_transition }}"
hs_color:
- >
{% set curr = state_attr(curr_target, 'hs_color')[0] | default(0) | float %}
{{ (curr + step_val) % 360 }}
- "{{ state_attr(curr_target, 'hs_color')[1] | default(100) }}"
- conditions: >
{{
trigger.id.endswith('_klick')
and state_payload_valid
and event_change_detected
and button_payload_present
}}
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment