Using variable replacement as for_each items

Hello,

I am trying to create the list of a for_each dynamically for a script. I want to iterate over list items containing an entity_id and a select_id. Unfortunately I end up with the following error:
Error: Repeat 'for_each' must be a list of items

The template code for my group_list is looking good to me, at least it creates the following as group_list, which should be OK for usage in a for_each block according to (Script Syntax - Home Assistant). What am I overseeing here?:

group_list: |-
  - entity_id: climate.thermostat_wohnzimmer_fenster
    select_id: input_select.thermostat_wohnzimmer_fenster_ui
  - entity_id: climate.thermostat_wohnzimmer_wand
    select_id: input_select.thermostat_wohnzimmer_wand_ui

Script input parameters:

climate_group_id: group.climate
input_select_group_id: group.input_select
action: auto

Script:

alias: Set Climate Group
sequence:
  - variables:
      climate_group_id: "{{ climate_group_id }}"
      input_select_group_id: "{{ input_select_group_id }}"
      action: "{{ action }}"
      group_list: |
        {% for entity_id in state_attr(climate_group_id, "entity_id")|list %}
        - entity_id: {{ entity_id }}
          select_id: {{ state_attr(input_select_group_id, "entity_id")[loop.index0] }}
        {%- endfor %}
  - repeat:
      sequence:
        - service: script.set_thermostat
          data:
            entity_id: "{{ repeat.item.entity_id }}"
            select_id: "{{ repeat.item.select_id }}"
            action: "{{ action }}"
      for_each: "{{ group_list }}"

What does script.set_thermostat do? In your example, it seems to set a thermostat to auto mode but I don’t know what it does with the input_select.

Post the script.


EDIT

The reason why I am asking is because it’s unclear why a repeat is used to set the mode for multiple thermostats when it can be done with a single service call. Therefore I assume it’s because the input_select serves some special purpose.

On it’s own a for loop doesn’t return an actual list, it returns a list-shaped string. To get a real list you need to use a namespace.

  - variables:
      group_list: |
        {% set ns = namespace( members = [])%}
        {% for entity_id in state_attr(climate_group_id, "entity_id") | list %}
        {% set ns.members = ns.members + [{"entity_id": entity_id, "select_id": state_attr(input_select_group_id, "entity_id")[loop.index0] }] %}
        {%- endfor %}
        {{ ns.members }}

Unless you plan to assign defaults or alter them in some way there is no point in having the other 3 variables you had. Instead you should set up fields for them.

You are absolutely correct. The script is updating the HVAC, but is also updating an input select. Anyways, the code already crashes at the for-loop and that code is never reached.

Currently I use the script for setting my HVAC mode and temperature and updating the UI element as well. This script is working fine.

In that case, you have confirmed my suspicion that the design of your application is needlessly complicated. You’re currently focused on fixing a cog in machine that has too many cogs.

First of all thanks for your help! I did not know about namespaces in jinja so far and did not know that it makes a difference whether I provide a list shaped string or an actual list. With a slightly modified version of your code everything is working as intended - thanks a lot ! (There was only a mistake in resolving the select_id.

  - variables:
      group_list: |
        {% set ns = namespace( members = []) %}
        {% for entity_id in state_attr(climate_group_id, "entity_id") | list %}
        {% set ns.members = ns.members + [{"entity_id": entity_id, "select_id": state_attr(input_select_group_id, "entity_id")[loop.index0] }] %}
        {% endfor %} {{ ns.members }}

I like to declare all input parameters as variables. This improves readability of the script and documents the parameters, which are expected right at the beginning, even if the variables are not needed at a later point.

Of course fields are the way to go…

Thanks that you are trying to help, but yes, I would gracefully just use the service call and provide it with a list of entity ids that should receive the update. Unfortunately it is not possible in my use case. Call it needlessly complicated or whatever you like - I know of no other disproportionally time consuming way to achieve what I want:

I do have slow reacting zigbee thermostat valves, which take quite some time to wake up from sleep mode and respond to the mqtt message. When I change the HVAC mode in a custom climate card I am using this leads to confusion and people complained that they accidentally turned the HVAC off again, because the corresponding UI element only updates after HA receives the mqtt answer (which can take up to 10 seconds).
Because of these slow thermostat valves I added the feature that my state icon is playing a pulsing animation if the current state of the HVAC is different to the state, which was sent. And this state is stored inside my input_select.

Yes, I could spend lots of hours and update the code for the underlying ui element and implement a seperation of concerns between business logic and ui, but I simply do not have the time to do this, when I already have a working solution, which took me only a few minutes to implement and which is working perfectly.

In case it wasn’t clear, the reason I had asked for the script’s code was because I was offering to help you redesign your application. However, given your reluctance to participate in that process, that offer is off the table now.