Last active
January 1, 2026 17:26
-
-
Save jhol-byte/b2731a4d2476f530d76b9ff409f7f3a4 to your computer and use it in GitHub Desktop.
Home Assistent Blueprint "Ikea_bilresa_scroll_wheel"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| blueprint: | |
| name: Ikea_bilresa_scroll_wheel | |
| description: | | |
| Blueprint for Ikea Bilresa (button and scroll wheel for 3 different channels) | |
| Version: 2025-01-01 | |
| domain: automation | |
| # input selectors | |
| input: | |
| remote: | |
| name: Remote | |
| description: "Ikea Bilresa scroll wheel remote control" | |
| selector: | |
| device: | |
| # input selectors: channel 1 | |
| 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: {} | |
| 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 | |
| selector: | |
| entity: | |
| multiple: true | |
| filter: | |
| domain: | |
| - light | |
| - media_player | |
| 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" | |
| default: dim_only | |
| name: "Channel 1: Scroll wheel mode" | |
| description: "Select attribute of target entity controlled by scroll wheel" | |
| # input selectors: channel 1 | |
| 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: {} | |
| 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 | |
| selector: | |
| entity: | |
| multiple: true | |
| filter: | |
| domain: | |
| - light | |
| - media_player | |
| 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" | |
| default: dim_only | |
| name: "Channel 2: Scroll wheel mode" | |
| description: "Select attribute of target entity controlled by scroll wheel" | |
| # input selectors: channel 3 | |
| 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: {} | |
| 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 | |
| selector: | |
| entity: | |
| multiple: true | |
| filter: | |
| domain: | |
| - light | |
| - media_player | |
| 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" | |
| default: dim_only | |
| name: "Channel 3: Scroll wheel mode" | |
| description: "Select attribute of target entity controlled by scroll wheel" | |
| # input selectors: settings for all channels | |
| global_section: | |
| name: Global settings | |
| collapsed: true | |
| input: | |
| dim_step_pct: | |
| name: Dimmer step percentage | |
| description: "change in brightness in %" | |
| default: 5 | |
| selector: | |
| number: | |
| mode: box | |
| dim_min_pct: | |
| name: Minimum brightness | |
| description: "minimum brightness in %" | |
| default: 5 | |
| selector: | |
| number: | |
| mode: box | |
| min_color_temp: | |
| name: Minimum color temperature | |
| description: "minimum color temperature in K (for color_mode = color_temp)" | |
| default: 2200 | |
| selector: | |
| number: | |
| mode: box | |
| max_color_temp: | |
| name: Maximum color temperature | |
| description: "maximum color temperature in K (for color_mode = color_temp)" | |
| default: 4000 | |
| selector: | |
| number: | |
| mode: box | |
| 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 | |
| color_hue_step: | |
| name: Color hue step | |
| description: "change in color hue in degree (0..360) (for color_mode = color_hue)" | |
| default: 4 | |
| selector: | |
| number: | |
| mode: box | |
| color_saturation: | |
| name: Color saturation | |
| description: "color saturation in % (for color_mode = color_hue)" | |
| default: 100 | |
| selector: | |
| number: | |
| mode: box | |
| 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 | |
| volume_step_pct: | |
| name: Media player volume step percentage | |
| description: "change in volume in %" | |
| default: 2 | |
| selector: | |
| number: | |
| mode: box | |
| volume_max_pct: | |
| name: Media player max volume percentage | |
| description: "max volume in %" | |
| default: 50 | |
| selector: | |
| number: | |
| mode: box | |
| # automation logic | |
| 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_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_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 | |
| condition: | |
| - condition: template | |
| value_template: >- | |
| {{ trigger.event.data.old_state.state != 'unavailable' | |
| and trigger.event.data.new_state.state != 'unavailable' | |
| }} | |
| action: | |
| - variables: | |
| channel: >- | |
| {% if trigger.id in ['button_ch1', 'scroll_left_ch1', 'scroll_right_ch1'] %} | |
| ch1 | |
| {% elif trigger.id in ['button_ch2', 'scroll_left_ch2', 'scroll_right_ch2'] %} | |
| ch2 | |
| {% elif trigger.id in ['button_ch3', 'scroll_left_ch3', 'scroll_right_ch3'] %} | |
| ch3 | |
| {% endif %} | |
| - choose: | |
| # button click | |
| - 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 | |
| # button double-click | |
| - 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 | |
| # button long-press | |
| - conditions: | |
| - condition: template | |
| value_template: > | |
| {{ trigger.id == ('button_' ~ channel) and trigger.event.data.new_state.attributes.event_type == | |
| 'long_press' }} | |
| # button on-hold action | |
| 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 }}" | |
| # scroll wheel actions | |
| - conditions: | |
| - condition: template | |
| value_template: > | |
| {{ trigger.id == ('scroll_left_' ~ channel) or trigger.id == ('scroll_right_' ~ channel) }} | |
| sequence: | |
| - repeat: | |
| for_each: "{{ vars['targets_entities_' ~ channel] }}" | |
| sequence: | |
| - choose: | |
| - conditions: | |
| - condition: template | |
| value_template: "{{ vars['scroll_wheel_mode_' ~ channel] == 'lights: dim'}}" | |
| sequence: | |
| # scroll wheel mode: lights: dim | |
| - 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(trigger.id == 'scroll_right_' ~ channel, trigger.event.data.new_state.attributes.totalNumberOfPressesCounted * var_dim_step_pct, | |
| -1*([trigger.event.data.new_state.attributes.totalNumberOfPressesCounted * var_dim_step_pct, state_attr(repeat.item, 'brightness')*100/254 | |
| - var_min_pct]|min)) | |
| }} | |
| transition: 0.1 | |
| # scroll wheel mode: lights: color temp | |
| - conditions: | |
| - condition: template | |
| value_template: "{{ vars['scroll_wheel_mode_' ~ channel] == 'lights: color temp only' or (vars['scroll_wheel_mode_' ~ channel] == 'lights: color temp and color hue' and state_attr(repeat.item, 'color_temp_kelvin') is not none ) }}" | |
| sequence: | |
| # scroll wheel mode: lights: color temp | |
| - action: script.light_color_temp_helper | |
| data: | |
| mode: >- | |
| {{ iif(trigger.id == 'scroll_left_' ~ channel, 'down', 'up') | |
| }} | |
| min_color_temp: "{{ var_min_color_temp }}" | |
| max_color_temp: "{{ var_max_color_temp }}" | |
| color_temp_step: "{{ var_color_temp_step * trigger.event.data.new_state.attributes.totalNumberOfPressesCounted }}" | |
| target_light: "{{ repeat.item }}" | |
| # scroll wheel mode: lights: color hue | |
| - conditions: | |
| - condition: template | |
| value_template: "{{ vars['scroll_wheel_mode_' ~ channel] == 'lights: color hue only' or (vars['scroll_wheel_mode_' ~ channel] == '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 * trigger.event.data.new_state.attributes.totalNumberOfPressesCounted }}" | |
| color_saturation: "{{ var_color_saturation }}" | |
| mode: >- | |
| {{ iif(trigger.id == 'scroll_left_' ~ channel, 'down', 'up') | |
| }} | |
| # scroll wheel mode: media: volume | |
| - conditions: | |
| - condition: template | |
| value_template: "{{ vars['scroll_wheel_mode_' ~ channel] == 'media: volume control' and is_state(repeat.item, 'playing')}}" | |
| sequence: | |
| - action: media_player.volume_set | |
| target: | |
| entity_id: "{{ repeat.item }}" | |
| data: | |
| volume_level: >- | |
| {{ [[state_attr(repeat.item, 'volume_level')*100 + | |
| iif(trigger.id == 'scroll_right_' ~ channel, +1, -1)* trigger.event.data.new_state.attributes.totalNumberOfPressesCounted * var_volume_step_pct, var_volume_max_pct] | min, 0] | max / 100 | |
| }} | |
| mode: single | |
| 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 | |
| } | |
| 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 | |
| trigger_variables: | |
| var_remote: !input remote | |
| # get all event names from device_id only | |
| 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 %} | |
| {{ match[0] }} | |
| {% 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 %} | |
| {{ match[0] }} | |
| {% 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 %} | |
| {{ match[0] }} | |
| {% 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 %} | |
| {{ match[0] }} | |
| {% 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 %} | |
| {{ match[0] }} | |
| {% 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 %} | |
| {{ match[0] }} | |
| {% 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 %} | |
| {{ match[0] }} | |
| {% 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 %} | |
| {{ match[0] }} | |
| {% 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 %} | |
| {{ match[0] }} | |
| {% endif %} | |
| {% endfor %} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment