Using the new for_each

@frenck recently released the for_each command in 2022.5 and mentioned that complex templates could be use to return lists of entities. I wanted to provide an example here for others to use.

This example takes a pre-defined group of lights, expands the members, checks if they’re already on, and then adjusts their brightness according to a lux sensor.

alias: Lux controlled lights
sequence:
  - repeat:
      for_each: >-
        {{ expand('light.lux_controlled_lights') | selectattr('state', 'eq',
        'on') | map(attribute='entity_id') | list }}
      sequence:
        - service: light.turn_on
          data:
            entity_id: '{{ repeat.item }}'
            brightness_pct: '{{ lux_capped|float }}'
variables:
  lux: '{{ states(''sensor.lux_mean'') }}'
  maxlux_minbrightness: '{{ states(''input_number.max_lux'') }}'
  lowlux_maxbrightness: '{{ states(''input_number.min_lux'') }}'
  slope: '{{ -1 / (maxlux_minbrightness - lowlux_maxbrightness) }}'
  yintersect: '{{ slope * (0 - lowlux_maxbrightness) + 1}}'
  lux_brightness: '{{ ((slope * lux + yintersect) * 100) | round(1) }}'
  lux_capped1: '{{ [1, lux_brightness] | max }}'
  lux_capped: '{{ [100, lux_capped1] | min }}'
mode: restart

4 Likes

Why control each light with a separate service call when they can all be controlled with a single call?

        - service: light.turn_on
          target:
            entity_id: >
              {{ expand('light.lux_controlled_lights') | selectattr('state', 'eq', 'on')
                | map(attribute='entity_id') | list }}
          data:
            brightness_pct: '{{ lux_capped|float }}'
1 Like

Also see this neat trick for simplifying your brightness cap:

2 Likes

Interesting thanks @123 taras, I didn’t know that turn_on could accept a list of entities like that. But it makes sense now that you show it.

In any case, I’m hoping the expand/map/list in the for_each is helpful for someone.

Isn’t it the case that if you go with @123’s suggestion, if there are no lights that are on, an error with be thrown for trying a service call on an empty list? I do agree that if you know there will be at least one light on (for example with a condition check) it is more efficient. However, the for each route avoids needing the condition check and would also allow you to do things like check the brightness level for each light as well.

Maybe I am wrong. Anyway, cool stuff.

The answer to your question is: no.

Try running the following service call in Developer Tools > Services. In my case (using version 2022.5.2), the template produces an empty list yet executing the service call doesn’t log any warning or error message. It simply does nothing without complaint.

Fair enough.