Script template - Create a dynamic array with input_boolean values

Hi,

I have created the following card:
image

Witch I want to use as an input to call the follwing script:

aspira_personalizado:
  alias: Roborock S5 Max aspira as areas escolhidas
  sequence:
    - service: vacuum.send_command
      data:
        entity_id: vacuum.xiaomi_vacuum_cleaner
        command: app_segment_clean
        params: [17, 21, 22]

But I don’t know how to build by code the array using the input_boolean of the card.

Can anyone help me with this?

Thanks!

What do you want in the list (array)? The state of each input_boolean? Something like this:

['off', 'off', 'on', 'off', 'off', 'off', 'off']

If you have a group containing the seven input_booleans, you can use this template:

{{expand('group.your_group') | map(attribute='state') | list }}

If you don’t have them in a group, you will need to list them within the expand function:

{{expand('input_boolean.corredor, 'input_boolean.sala', input_boolean.etc'),  | map(attribute='state') | list }}

No, each zone of the house has a specific code, so I’ll need to check witch of the input_booleans is on and then add the specific numeric code of the house area to the array. So it will generate an array like [17,21,22]

So each input_boolean has a corresponding numeric value? You want to include the values of only the input_booleans that are on?

Copy-paste the following example into the Template Editor and then change the names of the input_booleans and modify the numeric codes accordingly.

{% set x = expand('input_boolean.corredor', 'input_boolean.sala', 'input_boolean.etc') | map(attribute='state') | list %}
{% set y = [17, 23, 19, 31, 29, 22, 34] %}
{% set ns = namespace (z=[]) %}
{% for i in x %}
  {% if i == 'on' %}
    {% set ns.z = ns.z + [y[loop.index-1]] %}
  {% endif %}
{% endfor%}
{{ ns.z }}

Turn on a few of the input_booleans and the template will produce a list containing their respective numeric codes.

2 Likes

Thanks a lot for your help! The solution is almost perfect but doesn’t work as is.
The problem on your solution is that the order of the list inside the expand() is not preserved. For some reason the compiler reorders the list alphabetically so to insure that the x list matches the y list, you have to put the items ordered alphabetically. I just did that and it works just fine!

{% set x = expand('input_boolean.aspira_corredor', 'input_boolean.aspira_cozinha', 'input_boolean.aspira_quarto', 'input_boolean.aspira_quarto_lara', 'input_boolean.aspira_quarto_miudos', 'input_boolean.aspira_sala', 'input_boolean.aspira_wc') | map(attribute='state') | list %}
{% set y = [19, 22, 17, 21, 20, 16, 18] %}
{% set ns = namespace (z=[]) %}
{% for i in x %}
  {% if i == 'on' %}
    {% set ns.z = ns.z + [y[loop.index-1]] %}
  {% endif %}
{% endfor%}
{{ ns.z }}
1 Like

I’m glad to hear that the solution I provided was 99% correct. An easy way to avoid the sorting performed by expand is to simply avoid it and create the list explicitly with the State objects in the desired order:

{% set x = [ states.input_boolean.aspira_corredor, states.input_boolean.sala, states.input_boolean.cozinha, states.input_boolean.etc ] %}

That makes it easier to maintain because you don’t have to compensate for expand's sorting.

You should know that it is the custom of this community to assign the Solution tag to the first post that supplies the concept that solves the problem. In other words, the answer was the technique I presented which builds a third list based on the data found in two other lists. However, you have chosen to mark your own post as the Solution after simply rearranging the items in one list.

You have all the credit for the solution obviously, and I’m grateful for your help. I only pointed the solution for my comment because it has the complete solution. But don’t worry, I will change it for your comment, I really don’t care about that.
By the way, I tested the code on your last comment and once again is almost perfect, but you missed the “.state” at the end of each entity. Like this:

{% set x = [ states.input_boolean.aspira_sala.state, states.input_boolean.aspira_quarto.state, states.input_boolean.aspira_wc.state, states.input_boolean.aspira_corredor.state, states.input_boolean.aspira_quarto_miudos.state, states.input_boolean.aspira_quarto_lara.state, states.input_boolean.aspira_cozinha.state ] %}
{% set y = [16, 17, 18, 19, 20, 21, 22] %}

Now it’s perfect and perfectly ordered.

Once again thanks for your help! :+1:

What I missed was changing this:

  {% if i == 'on' %}

to this:

  {% if i.state == 'on' %}

and that avoids having to append .state to each entity in the list.


If you’re interested, the template can be reduced to this:

{% set ns = namespace (z=[]) %}
{% for i in [states.input_boolean.aspira_corredor, states.input_boolean.aspira_cozinha,
             states.input_boolean.aspira_quarto, states.input_boolean.aspira_quarto_lara,
             states.input_boolean.aspira_quarto_miudos, states.input_boolean.aspira_sala,
             states.input_boolean.aspira_wc] %}
  {% if i.state == 'on' %} {% set ns.z = ns.z + [[19, 22, 17, 21, 20, 16, 18][loop.index-1]] %} {% endif %}
{% endfor%}
{{ ns.z }}

@123 @jfpcarreira
I was trying to do very similar thing. I wanted to be able to select rooms for the roborock s5 to clean and be able to start the cleaning by clicking a button. Additionally I wanted all this to be available from Google Home and voice assistant.

So the solution that I came up with is:
I have multiple switches to enable or disable rooms for cleaning.
I created a sensor that contains the data of selected rooms, friendly names of the rooms and its IDs. The value is updated when any switch change:

sensor:
  - platform: "template"
    sensors:
      roborock_cleaning_settings:
        value_template: "CHECK ATTRIBUTES"
        attribute_templates:
          selected_rooms: >-
            {% set result = namespace (selectedRooms=[]) %}
            {% set rooms = [
            { "roomId": 16, "friendlyName": "Gabinet", "inputEntityId": "input_boolean.odkurzanie_gabinet"},
            { "roomId": 18, "friendlyName": "Korytarz", "inputEntityId": "input_boolean.odkurzanie_korytarz"},
            { "roomId": 1, "friendlyName": "Kuchnia", "inputEntityId": "input_boolean.odkurzanie_kuchnia"},
            { "roomId": 19, "friendlyName": "Salon", "inputEntityId": "input_boolean.odkurzanie_salon"},
            { "roomId": 17, "friendlyName": "Sypialnia", "inputEntityId": "input_boolean.odkurzanie_sypialnia"},
            { "roomId": 2, "friendlyName": "Łazienka", "inputEntityId": "input_boolean.odkurzanie_lazienka"},
            ] %}
            {% for room in rooms %}
              {% if is_state(room.inputEntityId, 'on') %}
                {% set result.selectedRooms = result.selectedRooms + [room] %}
              {% endif %}
            {% endfor %}
            {{ result.selectedRooms }}

Next I created a custom switch that allows me to turn on cleaning or send robot to the dock. Switch also allows you to use the feature from google home and by voice commands :wink:

switch:
  - platform: "template"
    switches:
      roborock_clean:
        value_template: "{{ not is_state('vacuum.roborock_s5', 'docked') }}"
        turn_on:
          service: script.vacuum_selected_rooms
        turn_off:
          service: vacuum.return_to_base
          target:
            entity_id: vacuum.roborock_s5

The script to run vacuuming allows you to also repeat room cleaning and set the fan speed, it looks like this:

vacuum_selected_rooms:
  alias: "Vacuum selected rooms"
  sequence:
    - service: vacuum.set_fan_speed
      data:
        entity_id: vacuum.roborock_s5
        fan_speed: >
          {{ states('input_number.vacuum_power') | int }}
    - service: vacuum.send_command
      data:
        entity_id: vacuum.roborock_s5
        command: app_segment_clean
        params: >
          {% set result = namespace(roomIds=[]) %}
          {% for roomId in state_attr('sensor.roborock_cleaning_settings', 'selected_rooms') | map(attribute='roomId') %}
            {% for n in range(states('input_select.odkurzanie_powtorzenia') | int) %}
              {% set result.roomIds = result.roomIds + [roomId] %}
            {% endfor %}
          {% endfor %}
          {{ result.roomIds }}
    - service: notify.all_mobile
      data:
        title: Vacuuming started
        message: >
          Vacuuming started for rooms: {{ state_attr('sensor.roborock_cleaning_settings', 'selected_rooms') | map(attribute='friendlyName') | join(', ') }}

Hope that it will help somebody in the future :smiley:

Hello,
Awesome thread, I used it to create custom trigger for my Dreame L20 Ultra Complete.
I used Dreame HACS custom component dev branch from Tasshack.

here is my sensor in configuration.yaml:

sensor:
  - platform: "template"
    sensors:
      dreame_cleaning_settings:
        value_template: "CHECK ATTRIBUTES"
        attribute_templates:
          selected_rooms: >-
            {% set result = namespace (selectedRooms=[]) %}
            {% set rooms = [
            { "roomId": 6, "friendlyName": "SdB RdC", "inputEntityId": "input_boolean.sdb_rdc"},
            { "roomId": 1, "friendlyName": "Chambre", "inputEntityId": "input_boolean.chambre"},
            { "roomId": 2, "friendlyName": "Salon", "inputEntityId": "input_boolean.salon"},
            { "roomId": 4, "friendlyName": "Salle à Manger", "inputEntityId": "input_boolean.salle_a_manger"},
            { "roomId": 3, "friendlyName": "Entrée", "inputEntityId": "input_boolean.entree"},
            { "roomId": 5, "friendlyName": "Cuisine", "inputEntityId": "input_boolean.cuisine"},
            ] %}
            {% for room in rooms %}
              {% if is_state(room.inputEntityId, 'on') %}
                {% set result.selectedRooms = result.selectedRooms + [room] %}
              {% endif %}
            {% endfor %}
            {{ result.selectedRooms }}

display in the UI with this card:

type: entities
entities:
  - sensor.dreame_cleaning_settings

image

I used to add the corresponding input_boolean as well in configuration.yaml:

input_boolean:
  sdb_rdc:
    name: SdB RdC
  chambre:
    name: Chambre
  salon:
    name: Salon
  salle_a_manger:
    name: Salle a Manger
  entree:
    name: Entree
  cuisine:
    name: Cuisine

So I displayed in the UI the corresponding card.

type: entities
entities:
  - entity: input_boolean.chambre
  - entity: input_boolean.cuisine
  - entity: input_boolean.entree
  - entity: input_boolean.salle_a_manger
  - entity: input_boolean.salon
  - entity: input_boolean.sdb_rdc

image

Then I created the script with the following instruction:

alias: Vacuum Selected Room
sequence:
  - service: dreame_vacuum.vacuum_clean_segment
    data:
      segments: >
        {% set result = namespace(roomIds=[]) %} {% for roomId in
        state_attr('sensor.dreame_cleaning_settings', 'selected_rooms') |
        map(attribute='roomId') %}
          {% set result.roomIds = result.roomIds + [roomId] %}
        {% endfor %}  {{ result.roomIds }}
    target:
      entity_id: vacuum.dreamebot_l20_ultra_complete
mode: single
icon: mdi:robot-vacuum

I just removed the number of time cleaning a room, as I want to use the default controls coming with the Dreame HACS custom component.

Then I created a card to mock the default vacuum call to service button and have direct access in the UI instead of using the Entity button:

type: horizontal-stack
cards:
  - show_name: false
    show_icon: true
    type: button
    icon_height: 60px
    tap_action:
      action: toggle
    entity: script.vacuum_selected_room
    show_state: false
    icon: mdi:play
  - show_name: false
    show_icon: true
    type: button
    icon_height: 60px
    tap_action:
      action: call-service
      service: vacuum.start_pause
      target:
        entity_id: vacuum.dreamebot_l20_ultra_complete
    icon: mdi:play-pause
    entity: vacuum.dreamebot_l20_ultra_complete
  - show_name: false
    show_icon: true
    type: button
    icon_height: 60px
    tap_action:
      action: call-service
      service: vacuum.stop
      target:
        entity_id: vacuum.dreamebot_l20_ultra_complete
    icon: mdi:stop
    entity: vacuum.dreamebot_l20_ultra_complete
  - show_name: false
    show_icon: true
    type: button
    icon_height: 60px
    tap_action:
      action: call-service
      service: vacuum.return_to_base
      target:
        entity_id: vacuum.dreamebot_l20_ultra_complete
    icon: mdi:home-import-outline
    entity: vacuum.dreamebot_l20_ultra_complete
  - show_name: false
    show_icon: true
    type: button
    icon_height: 60px
    tap_action:
      action: call-service
      service: vacuum.locate
      data: {}
      target:
        entity_id: vacuum.dreamebot_l20_ultra_complete
    icon: mdi:map-marker-radius
    entity: vacuum.dreamebot_l20_ultra_complete

image

In the end I created 3 cards, can be optimized.
#1 is to set default params from Dreame HACS custom component like cleanning mode, suction level, path, mop humidity etc.
#2 is to select the room user wants to clean
#3 is direct control of the robot start, resume, stop, home and locate.

Everything works well thanks to this thread :slight_smile:

I am sure we can do better (like using custom cards) but it was fun to do and learn.

2 Likes