A List generated by a template - is it possible? HELP!

In brief, yes on all counts.

Originally I was hopeful that '[1, 2]' might get a pass but had a sneaky feeling that ',' was a step too far. Fact it, it was doomed from the start but denial hope was strong. :slight_smile:

1 Like

I see a nice discussion in here. One thing that makes me wonder is this thing:
http://jinja.pocoo.org/docs/2.10/nativetypes/


So seems jinja can produce also somehting else than just a string. Perhaps the way HA integrates jinja with itself does nto allow it to handle nativ data types like a list out of the jinja template…

We are constrained by the abilities of Home Assistant’s implementation of the Jinja2 processor. The article states:

The default Environment renders templates to strings. With NativeEnvironment , rendering a template produces a native Python type. This is useful if you are using Jinja outside the context of creating text files.

However, if you try just the first line of the example code in the Template Editor … game over.

If you want to be technical… :wink:

While Jinja is processing the template, yes, it can use Python data types. I believe I commented on that somewhere. But HA converts the result of the template into a string.

Sorry for waking up this old thread, but I was trying to implement this with python_script and get a confusing error message.

My clean_room.py file looks like this:

room = data.get("room")
rooms = {"kitchen": [[25907,26406,29707,30806],[22658,26502,25958,27602]],"bath_room": [20889,24984,23689,27584],"bed_room": [21311,27688,24811,31138],"living_room": [[20824,21307,26892,25007],[23760,24975,26610,26425]],"flat": [20829,21384,29779,31234]}

service_data = {"entity_id": "vacuum.xiaomi_vacuum_cleaner", "repeats": 2, "zone": rooms[room]}

hass.services.call("xiaomi_miio", "vacuum_clean_zone", service_data, False)

And the error is the following: voluptuous.error.MultipleInvalid: None @ data['zone'][0]
But if I use the logger and say logger.error("CleanRoom: %s", rooms[room]) I get the correct result, for example “bath_room” prints out: CleanRoom: [20889, 24984, 23689, 27584]

Does anyone of you know what I am missing here?
Thanks in advance

I think each one needs to be a list of lists. Try:

room = data.get("room")
rooms = {
    "kitchen": [[25907,26406,29707,30806],[22658,26502,25958,27602]],
    "bath_room": [[20889,24984,23689,27584]],
    "bed_room": [[21311,27688,24811,31138]],
    "living_room": [[20824,21307,26892,25007],[23760,24975,26610,26425]],
    "flat": [[20829,21384,29779,31234]],
}

service_data = {"entity_id": "vacuum.xiaomi_vacuum_cleaner", "repeats": 2, "zone": rooms[room]}

hass.services.call("xiaomi_miio", "vacuum_clean_zone", service_data, False)
1 Like

@pnbruckner thanks, that did the trick! :sweat_smile:

1 Like

I also just wanted to add my findings to the whole topic of “passing a list to a script and from there pass it on to a service”:

tl;dr: I couldn’t get it working with a usual script, I had to do it with a python script.
Here’s my solution first, then I will post my findings. Hopefully they will help somebody or maybe knows the last missing puzzle piece:

My Python script:

rooms = data.get("rooms")
# logger.info(rooms)
rooms = [ int(x) for x in rooms.split(",") ]
# logger.info(rooms)

serviceData = {
  "entity_id": 'vacuum.living_room_wall_e',
  "command": "app_segment_clean",
  "params": rooms
}

# logger.info(serviceData)

hass.services.call('vacuum','send_command',serviceData)

And my automation where I call it:

- id: '1594586327354'
  alias: Vacuum the apartment if everybody left home
  description: ''
  trigger:
  - entity_id: group.everybody
    from: home
    platform: state
  condition:
  - after: '10:00:00'
    before: '20:00:00'
    condition: time
  action:
  - service: vacuum.set_fan_speed
    data:
      fan_speed: "Turbo"
    entity_id: vacuum.living_room_wall_e
  - service: python_script.wall_e_vacuum_room
    data_template:
      rooms: >
        {% if is_state("binary_sensor.living_room_door_on_off", "on") %}
          {{ "1,2,17" }}
        {% else %}
          {{ "17" }}
        {% endif %}

So as you can see I could not make it work to have the automation piece of code pass an actual list. No matter what I did, it was always a string, never a list. It always works in the Template Developer Tool, but never in the automation code.
I tried this: {{ [1,2,17] }}
I even tried weird conversions like that: {{ "1,2,17".split(',')|map('int')|list }}
Which again all work in the dev tools, but when receiving it in the script, it’s always a string, like this: '[1,2,17]'

But with my Python script, I can ezpz handle every data type. So I just decided to send a comma separated list, make that a real list and force each element to be int.

Maybe this is of any help for somebody or maybe knows how to properly pass a list to a script :slight_smile:

Greetings,

Andy!

2 Likes

Fantastic solution @ezcGman :bouquet:

I’ve been struggling with the issue of passing a templates array and your elegant implementation nailed it.

Thanks!

1 Like

Version 0.117 will permit templates to produce other types (list, int, float, dict) and not only strings.

I’d try to use my script without the python script in 0.117 beta now, but it seems to still create a String.

This is my template I send to the python script currently:


vacuum_segmented_cleanup:
      value_template: >-
        [{% for state in dict(states.input_boolean|groupby('state'))['on']|sort(attribute="last_changed")  if ("vacuum_ug" in state.entity_id)-%}
        {% if not loop.first and loop.length > 1 %}, {% endif %}
        {{- state.name | replace(" saugen","") | capitalize | trim(",") }}
        {%- endfor %}]

Sending this directly to the robot in 0.117 isn’t possible. Tried some changes, but non of them made it possible to send it as a list to the cleaner.

Maybe anybody has suggestions or isn’t this possible in 0.117, as I’m using the name of the Boolean and not the entity id?

The preliminary release notes indicate it should be possible. However there is an open Issue, in the Core GitHub repo, where beta-testers are reporting they’re having difficulties succeeding with the new capability.

I’m not beta-testing 0.117 but, as an experiment, try this (note: it may not even pass Configuration Check):

    vacuum_segmented_cleanup:
      value_template: [{% for state in dict(states.input_boolean|groupby('state'))['on']|sort(attribute="last_changed")  if ("vacuum_ug" in state.entity_id)-%}
        {% if not loop.first and loop.length > 1 %}, {% endif %}
        {{- state.name | replace(" saugen","") | capitalize | trim(",") }}
        {%- endfor %}]

Thanks for the reply, but as you expected it didn’t pass, as the „>-„ was missing to use the ‚on‘ filter in line.

Deleting the ‚>-‚ was the only thing you changed or did I look over other changes?

Really? Excited for this, thanks for the headsup!

Since you’re trying to send a list of strings, you’ll probably need single or double quotes around them.

Something like this would probably work

[{% for state in dict(states.input_boolean|groupby('state'))['on']|sort(attribute="last_changed")  if ("vacuum_ug" in state.entity_id)-%}
  {% if not loop.first and loop.length > 1 %}, {% endif %}
  "{{- state.name | replace(" saugen","") | capitalize | trim(",") }}"
{%- endfor %}]

That would work because, as I understand it, the new templating logic, determines the type from the string representation (e.g. “[…]” gives a list, “{…}” gives an object, “1.0” gives a float)

Alternatively, something like this will generate it as an actual list which may be less prone to issues:

{%- set ns = namespace(items=[]) -%}
{% for state in dict(states.input_boolean|groupby('state'))['on']|sort(attribute="last_changed")  if ("vacuum_ug" in state.entity_id)-%}
  {%- set ns.items = ns.items + [state.name | replace(" saugen","") | capitalize | trim(",")] -%}
{%- endfor %}
{{ ns.items }}
2 Likes

I have a potential solution that requires a custom_component and a working HACS installation.

I forked this Jinja2 custom filters repository on GitHub (originally authored by zvldz), and added some additional filters that should help you accomplish what you’re trying to achieve.

Installation:

  1. Add my GitHub repo url to your HACS custom repositories.
  2. Install the package called Custom Jinja2 Template Filters.

Available Filters:

strtolist             - Turn a string (e.g. "['blue', 'red']") into list
listify               - Convert a string or non-list into a list/dict
get_index             - Return the numeric index of a list or dict item
grab                  - Get list/dict item by key (w/ optional fallback)
ternary               - Return different values for true/false/null
shuffle               - Randomize list

Examples:

value_template: >-
  {% set colors = "[ red, blue, green ]" %}

  {# returns colors as a list #}
  {{ colors|listify }}

  {# returns 'blue' #}
  {{ (colors|listify)[1] }}

Hope it helps someone!

You may have noticed that this topic is over a year old now. Support for native types was introduced many versions ago; templates can easily return a list.

@blizzrdof77 since you’ve been doing what I wanted for some time now, but never knew how, please let me ask this:

legacy Custom-Header had support for jinja templates from within custom-header config, as they are defined/decribed here: custom-header/template-variables.js at 11537f553819077934034224afb61af1f21964fe · maykar/custom-header · GitHub

Ive always used these, and now CH is no longer supported, have lost the ability to simply state eg {{ viewTitle }} . Frontend themes, and other templating needs would benefit enormously of having these variables builtin again.

Would your custom resource be a place to integrate those? If so, consider this a FR :wink:
thanks for having a look!

Hi @Mariusthvdb – I’ll have to look into this when I have some spare time and get back to you.