Looping through all states in for_each loop

Hello,

I am new to home assistant and I am trying to use script for looping through specific entities. However, I am unable to get even a simplest example to work, which indicates that I am stuck in some basic error in the yaml syntax or something. Why does the first log call work, but the for-each loop doesn’t? The loop doesn’t seem to be working at all.

alias: Toggle Lights
mode: single
sequence:
  - service: logbook.log
    data_template:
      name: Script
      message: >
        Blabla: {% for state in states %}{{ state.entity_id }}{% endfor %} 
  - repeat:
      for_each: '{{ states }}'
      sequence:
        - service: logbook.log
          data_template:
            name: Script
            message: '{{ repeat.item.entity_id }}'

I believe for_each: '{{ states }}' is returning a string but you need a list to iterate around. This appears to work:

repeat:
  for_each: |
    {{ expand(states) | map(attribute="entity_id") | list }}
  sequence:
    - service: logbook.log
      data:
        name: testing
        message: |
          {{ repeat.item }}

Not expert on this stuff, but a bit of googling round the forums and trial and error got me to a working point. Hopefully you can take it forward from here.

3 Likes

Depending on what data you wish to extract, it may not even be necessary to use a for-loop or for_each in order to get it. The Jinja2 templating language contains filters that allow you to select data without ‘looping’.

Is your application truly to write every entity_id existing in your system to the log? Or is that just an experiment and you have another application in mind?

1 Like

Thanks, dudes - I got it working, but it turned out to be faster to concatenate entity id’s and pass them as strin in eg. turn_on service. It turned out to be a simple syntax issue, but I don’t recall what was wrong. This works:

- service: light.turn_on
    data:
      entity_id: >-
        {{ states | selectattr('entity_id', 'match', 'light.blabla*') |
        selectattr('state', 'eq', 'off') | map(attribute='entity_id') | join(',') }}

Fyi, I am trying to construct a script for toggling lights in multi-level house without using hard-coded device id lists in the script, I can post my solution when I get it working as intended. The logic I am trying to achieve is based on prefixing light etc. entity id’s with eg. upstairs/downstairs. Looks good so far.

Your template begins with a needlessly large dataset (all entities) when all it really wants are the light entities. Use states.light and select the ones whose object_id matches the desired pattern. Finally, report the result as a list.

  - service: light.turn_on
    target:
      entity_id: >-
        {{ states.light | selectattr('object_id', 'match', 'blabla*') |
        selectattr('state', 'eq', 'off') | map(attribute='entity_id') | list }}

FWIW, as suspected, your application is a fairly common one and can be achieved by filtering alone (no for-loop needed).

1 Like

Thanks Taras,

My code was just a simple example, I was trying to figure out the correct syntax. I am a programmer myself and familiar with jinja2, but it is surprisingly difficult to get the yaml syntax exactly right, as the editor does not give much feedback.

-h-

If you find it ‘surprisingly difficult to get the yaml syntax exactly right’ then I suggest you use the Automation Editor in visual mode to compose your actions. Switch its mode to YAML to see the result. After a few experiments like this, you’ll get a better understanding of YAML’s syntax (as used in Home Assistant).

Similarly, you can use the Template Editor to test Jinja2 templates (not YAML; the Template Editor doesn’t check YAML syntax).

I do programming for living, and as a result I find it easier to use text-based editor. However, sometimes I do find the yaml syntax difficult to understand. For example, I am now struggling with the following condition, which in my opinion should be exactly as described in the documentation:

- service: "{{ service_to_call }}"
    data:
      entity_id: "{{ light_entities }}"
    condition: 
      alias: "Run only if there is entities to be toggled"
      condition: template
      value_template: "{{ light_entities != '' }}"

In this case my condition looks exactly like the example condition in the documentation, still I get an error:

Message malformed: Unexpected value for condition: ‘[{‘condition’: ‘template’, ‘value_template’: “{{ light_entities != ‘’ }}”}]’. Expected and, device, not, numeric_state, or, state, sun, template, time, trigger, zone, a list of conditions or a valid template @ data[‘sequence’][2]

In my opinion even the json-formatted value in the error message looks right.

I find the home assistant very interesting platform and I am considering to replace my z-way.me setup with it (>30 z-wave devices), but I find it quite frustrating having to struggle for days with problems like this.

The reason why it doesn’t work is because you put it in the wrong place.

Regardless of your profession credentials, your limited knowledge of how Home Assistant employs YAML for its scripting language is causing you to make basic mistakes.

That’s why I suggested you start with the Automation Editor in visual mode because it generates properly formatted YAML.

If you insist on learning the hard way, and you’re using Visual Studio Code, then at least add the Config Helper plug-in.

Ok, I was mistaken that a condition can be defined per action.

I’m pretty sure it can - would the choose syntax meet your requirements?

If and choose statements work well as they should. I was just being misled by the error message, which made me think there is a syntax error parsing the condition, but you just can’t put a condition in a service call.

We have roughly similar customization in one of our commercial products and it has a condition in all blocks, which also made me think that this is obviously similar to ours.

The scripting documentation is quite clear regarding how actions can be configured.

FWIW, it’s possible to make one of the actions a condition. If the condition evaluates to false then no other actions are executed. In effect, it behaves like an ‘if-then’ with no ‘else’.