Problem in script with templates for room cleaning

I am very new to scripting and templating. I would like to program a script that calls a service to activate the vacuum cleaner and clean a selection of rooms. For example, for 2 rooms in particular the script is as follows (thanks this post):

alias: Room Cleaning
sequence:
  - service: xiaomi_miot.call_action
    data:
      entity_id: vacuum.dreame_mc1808_ef51_robot_cleaner
      siid: 18
      aiid: 1
      params:
        - piid: 1
          value: 18
        - piid: 21
          value: "{\"selects\": [[7,1,3,3,1],[8,1,3,3,1]]}"
      force_params: true
      throw: true
mode: single
icon: mdi:robot-vacuum

where the first number of each vector [[7,1,3,3,1],[8,1,3,3,3,1]] is the room to be cleaned (7 for kitchen, 8 for living room, e.g.). This script works without problems.

Then, using a series of input_boolean and a sensor template (thanks to this), I am able to autogenerate the complete string (e.g. [[7,1,3,3,3,1],[8,1,3,3,3,1]]) with the necessary data depending on the chosen rooms:

template:
  - sensor:
      - name: rooms_to_clean
        state: >
          {% set x = expand('input_boolean.clean_kitchen', 'input_boolean.clean_living', 'input_boolean.clean_bedroom') | map(attribute='state') | list %}
          {% set y = [[7,1,3,3,1], [8,1,3,3,1], [9,1,3,3,1]] %}
          {% set rooms = namespace (z=[]) %}
          {% for i in x %}
            {% if i == 'on' %}
              {% set rooms.z = rooms.z + [y[loop.index-1]] %}
            {% endif %}
          {% endfor %}
          {{ rooms.z }}

The final script (and the one I have problems with) that I use is the following:

alias: Room Cleaning (var template)
sequence:
  - service: xiaomi_miot.call_action
    data:
      entity_id: vacuum.dreame_mc1808_ef51_robot_cleaner
      siid: 18
      aiid: 1
      params:
        - piid: 1
          value: 18
        - piid: 21
          value: "{\"selects\": {{ states('sensor.rooms_to_clean') }}}"
      force_params: true
      throw: true
mode: single
icon: mdi:robot-vacuum

Well, although through Developer tools/Template I check that this code is exactly the same as the first one without templates, I don’t know the reason why the service call returns error, I’m going quite crazy, what can I be doing wrong?

Thanks in advance!

An entity’s state value, regardless of its appearance, is always treated as a string. So the list created by your Template Sensor is ultimately stored as a string. That’s what is causing a problem for your service call. It expects to receive a list value for selects but is getting a string.

Change the Template Sensor so that the rooms list is stored in an attribute as opposed to the state. The value of an attribute can be a list, number, string, boolean, etc.

template:
  - sensor:
      - name: rooms_to_clean
        state: '{{ now().timestamp() | timestamp_custom }}'
        attributes:
          rooms: >
          {% set x = expand('input_boolean.clean_kitchen', 'input_boolean.clean_living', 'input_boolean.clean_bedroom') | map(attribute='state') | list %}
            {% set y = [[7,1,3,3,1], [8,1,3,3,1], [9,1,3,3,1]] %}
            {% set rooms = namespace(z=[]) %}
            {% for i in x if i == 'on' %}
              {% set rooms.z = rooms.z + [y[loop.index-1]] %}
            {% endfor %}
            {{ rooms.z }}

Then reference the attribute in the service call.

alias: Room Cleaning (var template)
sequence:
  - service: xiaomi_miot.call_action
    data:
      entity_id: vacuum.dreame_mc1808_ef51_robot_cleaner
      siid: 18
      aiid: 1
      params:
        - piid: 1
          value: 18
        - piid: 21
          value: "{\"selects\": {{ state_attr('sensor.rooms_to_clean', 'rooms') }}}"
      force_params: true
      throw: true
mode: single
icon: mdi:robot-vacuum

Thanks for the explanation! It’s clear that I didn’t quite understand how the templates work. However, it seems that with the attributes in list format it still doesn’t work. I will try with another type (number?) to see if I get something.

For what it’s worth, I have recorded the log of the actions in case it gives any clues::

It works:

Call miot action {'did': '415717468', 'siid': 18, 'aiid': 1, 'in': [{'piid': 1, 'value': 18}, {'piid': 21, 'value': '{"selects": [[7,1,3,3,1],[8,1,3,3,1]]}'}]}, result: {'did': '415717468', 'siid': 18, 'aiid': 1, 'code': 0, 'out': []}

Does not work:


Call miot action {'did': '415717468', 'siid': 18, 'aiid': 1, 'in': [{'piid': 1, 'value': 18}, {'piid': 21, 'value': {'selects': [[7,1,3,3,1],[8,1,3,3,1]]}}]} failed: {'did': '415717468', 'siid': 18, 'aiid': 1, 'code': -1}

I don’t know if it will be important, but the double quotes in select appear in the call that works. I will continue to explore options, thanks!

Then something else is wrong because the value you are using is a list where each item in the list is also a list.

[[7,1,3,3,1], [8,1,3,3,1]]

The template builds a list of lists and the attribute stores it intact (i.e. it doesn’t convert it to a string like state does).

In your service call, I’m not sure why you chose to use a mixture of YAML and JSON:

      params:
        - piid: 1
          value: 18
        - piid: 21
          value: "{\"selects\": {{ state_attr('sensor.rooms_to_clean', 'rooms') }}}"

This is the equivalent in YAML:

      params:
        - piid: 1
          value: 18
        - piid: 21
          value:
            selects: "{{ state_attr('sensor.rooms_to_clean', 'rooms') }}"

It effectively becomes this in YAML:

        - piid: 21
          value:
            selects:
              - [7,1,3,3,1]
              - [8,1,3,3,1]

Which is the same as this in YAML:

        - piid: 21
          value:
            selects:
              - - 7
                - 1
                - 3
                - 3
                - 1
              - - 8
                - 1
                - 3
                - 3
                - 1

I use the combination YAML and JSON as shown here and here.

In fact, the call to the service (without template) succeeds only if it is done like this:

        - piid: 21
          value: "{\"selects\": [[7,1,3,3,1],[8,1,3,3,1]]}"

but not according to the last 2 options you show me in YAML, i.e:

        - piid: 21
          value:
            selects:
              - [7,1,3,3,1]
              - [8,1,3,3,1]

The only thing that surprises me is that in the Home Assistants YAML script editor the script call transforms from this:

value: '{"selects": [[7,1,3,3,1],[8,1,3,3,1]]}"

to this:

value: "{\"selects\": [[7,1,3,3,1],[8,1,3,3,1]]}"

after “Save Script” and enter again, but I understand it’s a script editor issue?

I haven’t found any more information anywhere, although many people try to do something similar, thanks anyway 123!

Then it’s due to some sort of requirement by your vacuum’s integration.

value: '{“selects”: [[7,1,3,3,1],[8,1,3,3,1]]}"

I know why the script editor changes it but the answer has no bearing on the original problem.

Looks like I’m almost there. If I call the service using single quotes that encompass value, the service call works!

entity_id: vacuum.dreame_mc1808_ef51_robot_cleaner
siid: 18
aiid: 1
params:
  - piid: 1
    value: 18
  - piid: 21
    'value': '{"selects": {{ state_attr('sensor.rooms_to_clean', 'rooms') }}}'
force_params: true
throw: true

My problem however is that when I save the script and go back in, Home Assitant seems to automatically evaluate the value, undoing the template, and leaving the service configured like this:

entity_id: vacuum.dreame_mc1808_ef51_robot_cleaner
siid: 18
aiid: 1
params:
  - piid: 1
    value: 18
  - piid: 21
    value: "{\"selects\": [[7,1,3,3,1],[8,1,3,3,1]]}"
force_params: true
throw: true

This undoes the option to choose rooms. We are very close it seems, any idea how to make the first script stay as it is?

Thx

params:
  - piid: 1
    value: 18
  - piid: 21
    value: >
      {{ {'selects': state_attr('sensor.rooms_to_clean', 'rooms')} }}

EDIT
If it’s a list, you need | string

params:
  - piid: 1
    value: 18
  - piid: 21
    value: >
      {{ {'selects': state_attr('sensor.rooms_to_clean', 'rooms') | string | replace(' ', '')} }}

Even then, that may not work because you’d need to remove whitespace if value selects requires no spaces so you’d have to add the replace(' ', '').

EDIT2

Or just build the room crap into the script…

alias: Room Cleaning
variables:
  config:
    kitchen: [7,1,3,3,1]
    living: [8,1,3,3,1]
    bedroom: [9,1,3,3,1]
  value: >
    {% set ns = namespace(ret=[]) %}
    {% for k, v in config.items() if is_state('input_boolean.clean_' ~ k, 'on') %}
      {% set ns.ret = ns.ret + [ v ] %}
    {% endfor %}
    {{ {'selects': ns.ret | string | replace(' ', '')} }}
sequence:
  - service: xiaomi_miot.call_action
    data:
      entity_id: vacuum.dreame_mc1808_ef51_robot_cleaner
      siid: 18
      aiid: 1
      params:
        - piid: 1
          value: 18
        - piid: 21
          value: "{{ value }}"
      force_params: true
      throw: true
mode: single
icon: mdi:robot-vacuum

Unfortunately, none of the three options work. I’ve been checking it the last couple of days in case I wasn’t applying the code here properly, but I’ll give it a try again.
It all seems to be a simple inverted comma issue or similar (from my lack of knowledge), but thank you very much anyway, I’ll keep checking.
(About Edit 1: no need to delete the space between the numbers, the service can run without it)

Ok then, this will work.

alias: Room Cleaning
variables:
  config:
    kitchen: [7,1,3,3,1]
    living: [8,1,3,3,1]
    bedroom: [9,1,3,3,1]
  value: >
    {% set ns = namespace(ret=[]) %}
    {% for k, v in config.items() if is_state('input_boolean.clean_' ~ k, 'on') %}
      {% set ns.ret = ns.ret + [ v ] %}
    {% endfor %}
    {{ { 'piid': 21, 'value': {'selects': ns.ret} | to_json } }}
sequence:
  - service: xiaomi_miot.call_action
    data:
      entity_id: vacuum.dreame_mc1808_ef51_robot_cleaner
      siid: 18
      aiid: 1
      params:
        - piid: 1
          value: 18
        - "{{ value }}"
      force_params: true
      throw: true
mode: single
icon: mdi:robot-vacuum

Now! Thank you very much, :clap:
I’ll take a look at the code to understand it well.