Integer list in data template converts to stings?

Hi I have a script that sends my vacuum cleaner to the kitchen by including a list of numbers in the data section. The numbers represents the coordinates of two opposite corners in the room and how many repetitions the vacuum cleaner should clean it. I want to be able to replace the number of repetitions in my script with a variable.

So i went from:

clean_kitchen:
  alias: Clean the kitchen
  sequence:
  - data:
     command: app_zoned_clean
     entity_id: vacuum.xiaomi_vacuum_cleaner
     params:
      - - 25850
        - 26750
        - 28750
        - 30550
        - 1
     service: vacuum.send_command

to:

clean_kitchen:
  alias: Clean the kitchen
  sequence:
  - data_template:
      command: app_zoned_clean
      entity_id: vacuum.xiaomi_vacuum_cleaner
      params:
      - - 25850
        - 26750
        - 28750
        - 30550
        - '{{repetitions|d(1)}}'
    service: vacuum.send_command

This somehow broke the parameters. The cleaner still start, but does not clean the kitchen, instead it returns to the dock immediately.

The log output when from:
2018-08-11 10:52:35 INFO (MainThread) [homeassistant.core] Bus:Handling <Event call_service[L]: domain=vacuum, service=send_command, service_data=command=app_zoned_clean, entity_id=vacuum.xiaomi_vacuum_cleaner, params=[[25850, 26750, 28750, 30550, 1]]>

to:

2018-08-11 10:58:12 INFO (MainThread) [homeassistant.core] Bus:Handling <Event call_service[L]: domain=vacuum, service=send_command, service_data=command=app_zoned_clean, entity_id=vacuum.xiaomi_vacuum_cleaner, params=[[‘25850’, ‘26750’, ‘28750’, ‘30550’, ‘1’]]>

From the log it looks like the parameters got quotes and I have tried to remove them without success, does anyone here know what I’m doing wrong?

Have you tried casting it as an integer, | int

Hi, thanks for the suggestion. I tried to replace one of the entries with:
- '{{30550|int}}'
Unfortunately it gave the same result. Still quotes in the log.

I meant like this:

clean_kitchen:
  alias: Clean the kitchen
  sequence:
  - data_template:
      command: app_zoned_clean
      entity_id: vacuum.xiaomi_vacuum_cleaner
      params:
      - - 25850
        - 26750
        - 28750
        - 30550
        - '{{ repetitions | int }}'
    service: vacuum.send_command

Tried that now as well, still the same result

If I only replace data with data_template without touching the repetition part, it still break the script with quotes.

Jinja templates return strings. Not much you can do about it. Usually this isn’t an issue because the service schema will coerce certain parameters to int or float or whatever. However, I just looked at the vacuum service handler, and it doesn’t do this for send_command’s “params” parameter, probably because it can be various types.

Probably the only way to resolve this is to change the platform code. What vacuum platform are you using?

Based on the entity_id, I assume you’re using the xiaomi vacuum platform. I looked over that code, as well as the miio library it uses, and since you’re using a raw command, there’s no code specific to this app_zoned_clean command that can be enhanced to force the elements of the “params” list to be integers.

Having said that, you could modify the generic async_send_command method to do this if command == ‘app_zoned_clean’. This is the method:

    @asyncio.coroutine
    def async_send_command(self, command, params=None, **kwargs):
        """Send raw command."""
        yield from self._try_command(
            "Unable to send command to the vacuum: %s",
            self._vacuum.raw_command, command, params)

You could do something like this:

    @asyncio.coroutine
    def async_send_command(self, command, params=None, **kwargs):
        """Send raw command."""
        if command == 'app_zoned_clean':
            params = [int(param) for param in params]
        yield from self._try_command(
            "Unable to send command to the vacuum: %s",
            self._vacuum.raw_command, command, params)

One question, though. In your YAML code, you’re making params a list of list of numbers. Did you mean to do that? Or should it just be a list of numbers? If it does indeed need to be a list of list of numbers, then the code suggestion above would need a slight modification.

So, turns out, the miio library does indeed have a method specifically for app_zoned_clean. So, the best solution is to enhance the xiaomi vacuum platform code (ziaomi_miio.py) to add a corresponding method and service schema which would coerce the parameters into int’s. If this is really important to you, and you’re not comfortable doing that yourself, I could give it a shot, but it would probably have to wait until after the weekend.

Hi, thank you very much for digging in to this!
I gave it a shot, but it is my first time coding in home assistant and I’m pretty new to python as well. But it seems to work at least!
https://github.com/home-assistant/home-assistant/commit/7c155017bbdd8b9bb699d4b4c0a90e2f75bbad48

Should I make it a pull request?

Wow, pretty impressive, and fast! :slight_smile:

The only change I’d suggest (without reviewing it in detail) is to use the zoned_clean method instead of raw_command. See line 94 of https://github.com/rytilahti/python-miio/blob/76a2dd2d18d811a8f19932b0983356dff8d74c22/miio/vacuum.py.

And, yes, you should make a pull request so others can benefit.

Tried to call zoned_clean instead but it did not work. I think it needs to be a list of lists with numbers to work. The point of the second list is to be able to send the cleaner to multiple zones in one command.

Have you tried removing the quote marks like this:

clean_kitchen:
  alias: Clean the kitchen
  sequence:
  - data_template:
      command: app_zoned_clean
      entity_id: vacuum.xiaomi_vacuum_cleaner
      params:
      - - 25850
        - 26750
        - 28750
        - 30550
        - {{ repetitions|d(1) | int }}
    service: vacuum.send_command

I’m having the same issues.
When calling a service with data: is no problem, but when using data_template i’m getting errors the output is not an integer.

for example:

  set_level:
    service: dreamscreen.set_brightness
    data_template:
      entity_id: dreamscreen.dreamscreen_4k
      brightness: "{{ (brightness / 2.55) | int }}"

gives the following error:

ERROR (MainThread) [homeassistant.core] Invalid service data for dreamscreen.set_brightness: expected int for dictionary value @ data[‘brightness’]. Got ‘13’

Also tried this to get rid of the quotes:

     brightness: >
       {{ (brightness / 2.55) | int }}

but no change.

Calling the service directly goes without issues:

  set_level:
    service: dreamscreen.set_brightness
    data:
      entity_id: dreamscreen.dreamscreen_4k
      brightness: 13

I’m sure it’s the same issue - the service schema doesn’t coerce the value to an int. Jinja templates output strings, you can’t avoid it as far as I know. So the code has to handle that situation. If it doesn’t, then the code has to be fixed. That’s my understanding.

Thanks @pnbruckner. That’s also my understanding. I’ve analysed my logs for Xiaomi vacuum service call, where I also use a template and it turns out the whole value is passed as
['[1,2,3,4,5]']
rather than
[[1,2,3,4,5]]

The reason is that the whole expression is output as string with quotes. I’ve tried playing with the |tojson jinja filter, but it doesn’t help much :frowning:

Has anyone filed a bug in guthub for this?? I’m asking as I’m willing to do so, so some dev could check this out.

Today I managed to solve the problem. The bug I filed wasn’t even touched by anyone and this fact forced me to dig in HA’s code, the Xiaomi vacuum component and this thread again.
I missed it when I saw this thread first, but what TENFIRE implemented in Aug '18 solves the problem. Now this piece of code is changed a bit, but it basically solves it!

To parametrize zone cleaning it’s enough to switch from the send_command service (which sends a raw command to the vacuum) to the xiaomi_clean_zone service which supports templating correctly!! :slight_smile:

This is the key thing here. Most of the sources in the web are still referring to the “old” send_command service, which doesn’t support templating correctly.

So for all of you seeking the solution in the future - an example:

vacuum_living_room:
  alias: Vacuum the living room
  sequence:
  - data_template:
      entity_id: vacuum.xiaomi
      zone:
      - - "{{ 25200+states('input_number.vac_off_x')|int }}"
        - "{{ 24000+states('input_number.vac_off_y')|int }}"
        - "{{ 27500+states('input_number.vac_off_x')|int }}"
        - "{{ 26000+states('input_number.vac_off_y')|int }}"
      repeats: 1
    service: vacuum.xiaomi_clean_zone

This does the job! :slight_smile:

FWIW, the issue was not templating. Templating happens before the service is even called, and is the same for both services.

The issue was the service schema, or in this case, the lack of an appropriate one. The schema is a data structure (in the implementation) that describes what a service’s data should look like, and can force values to be converted to different types, etc. In this case, the fix was adding a new service, that included an appropriate service schema (that converts the string representations of numbers that come out of the template to ints), and which calls the appropriate library method.

But, basically, yes, @TENFIRE’s fix solved the problem. :slight_smile: