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