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: remote: name: Remote description: Ikea Bilresa scroll wheel remote control selector: device: multiple: false channel1_section: name: Channel 1 collapsed: true input: click_action_ch1: name: 'Channel 1: action' default: [] selector: action: {} double_click_action_ch1: name: 'Channel 1: action' default: [] selector: action: {} long_click_action_ch1: name: 'Channel 1: action' description: '' default: [] selector: action: {} on_hold_action_ch1: name: 'Channel 1: 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 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' custom_value: false sort: false multiple: false default: dim_only name: 'Channel 1: Scroll wheel mode' description: Select attribute of target entity controlled by scroll wheel channel2_section: name: Channel 2 collapsed: true input: click_action_ch2: name: 'Channel 2: action' default: [] selector: action: {} double_click_action_ch2: name: 'Channel 2: action' default: [] selector: action: {} long_click_action_ch2: name: 'Channel 2: action' description: '' default: [] selector: action: {} on_hold_action_ch2: name: 'Channel 2: 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 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' custom_value: false sort: false multiple: false default: dim_only name: 'Channel 2: Scroll wheel mode' description: Select attribute of target entity controlled by scroll wheel channel3_section: name: Channel 3 collapsed: true input: click_action_ch3: name: 'Channel 3: action' default: [] selector: action: {} double_click_action_ch3: name: 'Channel 3: action' default: [] selector: action: {} long_click_action_ch3: name: 'Channel 3: action' description: '' default: [] selector: action: {} on_hold_action_ch3: name: 'Channel 3: 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 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' custom_value: false sort: false multiple: false default: dim_only name: 'Channel 3: Scroll wheel mode' description: Select attribute of target entity controlled by scroll wheel 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 step: 1.0 dim_min_pct: name: Minimum brightness description: minimum brightness in % default: 5 selector: number: mode: box step: 1.0 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: 4 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 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 step: 1.0 volume_max_pct: name: Media player max volume percentage description: max volume in % default: 50 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_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' \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'] %}\n ch1\n{% elif trigger.id in ['button_ch2', 'scroll_left_ch2', 'scroll_right_ch2'] %}\n ch2\n{% elif trigger.id in ['button_ch3', 'scroll_left_ch3', 'scroll_right_ch3'] %}\n ch3\n{% endif %}" - 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 == ''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: '{{ 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: - 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\n - var_min_pct]|min)) \n}}" transition: 0.1 - 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: - 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 }}' - 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'') }}' - 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 + \n 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\n}}" 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 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_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_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 %}"