Sensor Reaction Robustness by Means of While Loop

Hi yalls,

I am trying to streamline a script for a quite simple use case.

Since sensors/actuators sometimes do not respond to commands, I want to ensure that a script is fully and reliably executed on all sensors/actuators by means of a while-loop.

The following is an example for a single thermostat:

  - repeat:
      while:
        - condition: not
          conditions:
            - condition: state
              entity_id: climate.room_climate_badezimmer
              attribute: temperature
              state: 18

The sequence to be executed with this is the following:

- repeat:
    count: "{{ wohnung | count }}"
    sequence:
      - service: climate.set_temperature
        data:
          temperature: 18
          hvac_mode: heat
        target:
          area_id: "{{ wohnung[repeat.index -1] }}"

Now in the while loop, I need to replace a static variable value (entitiy_id: climate.room_climate_badezimmer) with a variable one like so:
climate.room_climate_[wohnung[repeat.index - 1]]

while:
  - condition: not
    conditions:
      - condition: state
        entity_id: climate.room_climate_&"{{ wohnung[repeat.index - 1] }}" # Syntax knowingly wrong
        attribute: temperature
        state: 18

So my first question is: How do I concatenate variable value strings correctly for this use case?
And my second question is: How do I deal with repeat count in case of nested repeats? Auxiliary variable?
And the 3rd question: Is the complete code below correct and can my low programming experience be streamlined out of it?

The whole code should look something like this:

alias: LĂĽften 5 Minuten
variables:
  wohnung:
    - bad
    - arbeitszimmer
    - wohnzimmer
    - schlafzimmer
sequence:
  - repeat: # loop 1
      count: "{{ wohnung | count }}"
      sequence:
        - repeat: # nested loop 2
            while:
              - condition: NOT
                conditions:
                  - condition: state
                    entity_id: climate.room_climate_[wohnung[repeat.index - 1]] # but the repeat.index must be from the superordinate repeat loop 1 !!!
                    attribute: temperature
                    state: 18
                  - condition: state
                    entity_id: climate.room_climate_[wohnung[repeat.index - 1]] # but the repeat.index must be from the superordinate repeat loop !!!
                    attribute: hvac_mode
                    state: heat
              - sequence:
                  - service: climate.set_temperature
                    data:
                      temperature: 18
                      hvac_mode: heat
                    target:
                      area_id: climate.room_climate_[wohnung[repeat.index - 1]] # but the repeat.index must be from the superordinate repeat loop !!!

I know that at least the last part of my complete code is wrong - as mentioned above, I need suggestions as to how to concatenate correctly the variable names and how to deal with nested loops and their respective repeat.counts.

Here is what I do to escape a while loop after roughly 60 seconds of not getting what I want out of the automation:

      - repeat:
          until:
            - condition: template
              value_template: |-
                {{
                (
                  is_state("switch.roxanne", "off")
                  and
                  is_state("media_player.marantz_av7702mkii", "off")
                )
                or
                repeat.index > 60
                }}
              alias: Roxanne is off and Marantz is off, or 1 minute has passed
          sequence:
            - delay:
                hours: 0
                minutes: 0
                seconds: 1
                milliseconds: 0
            - service: homeassistant.update_entity
              data: {}
              target:
                entity_id:
                  - switch.roxanne
              alias: Update Roxanne
1 Like

I suggest using a repeat for_each and a repeat until.

The following example is untested so let me know if you encounter any problems.

alias: LĂĽften 5 Minuten
sequence:
  - repeat:
      for_each:
        - bad
        - arbeitszimmer
        - wohnzimmer
        - schlafzimmer
      sequence:
        - variables:
            entity: 'climate.room_climate_{{ repeat.item }}'
        - repeat: 
            until:
              - "{{ state_attr(entity, 'temperature') == 18 }}"
              - "{{ state_attr(entity, 'hvac_mode') == 'heat' }}"
            sequence:
              - service: climate.set_temperature
                data:
                  temperature: 18
                  hvac_mode: heat
                target:
                  entity_id: '{{ entity }}'

For safety, the inner loop should probably include logic to limit the number of iterations.

OK this is great - this is even more towards what I want - thank you so much, Taras!

However, I want to stay with the variable array as per my original snippet (wohnung[] = ..., ...) because I intend to use it further down in the overall script - how can I do that? Again, with a repeat loop going through wohnung[], right?

Or could I use something like repeat: for_each: wohnung[]?

Please do not duplicate posts. Your other post was removed.

alias: LĂĽften 5 Minuten
variables:
  wohnung:
    - bad
    - arbeitszimmer
    - wohnzimmer
    - schlafzimmer
sequence:
  - repeat:
      for_each: '{{ wohnung }}'
      sequence:
        - variables:
            entity: 'climate.room_climate_{{ repeat.item }}'
        - repeat: 
            until:
              - "{{ state_attr(entity, 'temperature') == 18 }}"
              - "{{ state_attr(entity, 'hvac_mode') == 'heat' }}"
            sequence:
              - service: climate.set_temperature
                data:
                  temperature: 18
                  hvac_mode: heat
                target:
                  entity_id: '{{ entity }}'
              - wait_template: >
                  {{ state_attr(entity, 'temperature') == 18 and
                     state_attr(entity, 'hvac_mode') == 'heat' }}
                timeout: '00:00:03'

EDIT

Enhancement. Added wait_template to the repeat until loop in order for the service call to take effect before evaluating the until condition. It waits a maximum of 3 seconds; more efficient than a fixed delay.

!!! Perfect - I had a hard time finding info on how this whole {{}} business works (is it like js?). Also, hard time to find good examples about template_value, generally variables and arrays.

So thank you so much again!

1 Like

References:

Templating
Variables
Repeat for_each

1 Like

Hm - still doesn’t work, I’ve narrowed the problem down to 2 lines.
This works:

sequence:
  - repeat:
      count: 2
      sequence:
        - repeat:
            for_each: "{{ wohnung }}"
            sequence:
              - variables:
                  entity: climate.room_climate_{{ repeat.item }}
                  area: "{{ repeat.item }}"
              - repeat:
                  until:
                    - "{{ 1 == 1 }}"
                    # - "{{ state_attr(entity, 'temperature') == 18 }}"
                    # - "{{ state_attr(entity, 'hvac_mode') == 'heat' }}"
                  sequence:
                    - service: climate.set_temperature
                      data:
                        temperature: 18
                        hvac_mode: heat
                      target:
                        area_id: "{{ area }}"
        - delay:
            seconds: 2

The commented part results in only the first item in wohnung[] to be actuated. It seems there’s something wrong in the condition check of the while loop.

Do you want while or until?

If you use until, its sequence is executed at least once before it checks the condition (this is explained in its documentation)

1 Like

[quote=“123, post:6, topic:535472”]

Sorry, UNTIL like you proposed.

OK, so what exactly “doesn’t work”? Post the script’s trace.

1 Like

I’m starting to feel more and more stupid - how do I get the trace? Is that it?

The trace is also available for download as a file containing all execution steps (and more) in JSON format.

Even without the trace, what exactly did you mean when you said it “doesn’t work”? I can see from the screenshot that the script stopped execution at some point. Where did the script stop?

1 Like

FYI: I added an enhancement to my example posted above. The inner loop waits up to 3 seconds for the service call’s changes to take effect before attempting another iteration.

Reference: wait_template

1 Like

So this is my current script. It doesn’t seem to work if I - for test reasons - shorten the top level delay (roughly in the middle of the script) too much. It appears that maybe the Bosch Controller responds with a sensor state that in fact the sensor itself doesn’t take over. Could that be?

alias: Vent for 5 minutes already, geez!
variables:
  where_happens:
    - bathroom
    - bureau
    - putting_range
    - bonfire_lounge
    - bondage_room
sequence:
  - repeat:
      for_each: "{{ where_happens }}"
      sequence:
        - variables:
            entity_settings: climate.room_climate_{{ repeat.item }}
            entity: "{{ repeat.item }}"
        - repeat:
            sequence:
              - service: climate.set_temperature
                data:
                  hvac_mode: heat
                  temperature: 18
                target:
                  entity_id: "{{ entity_settings }}"
            until:
              - "{{ state_attr(entity_settings, 'temperature') == 18 }}"
              - "{{ state_attr(entity_settings, 'preset_mode') != 'eco' }}"
        - delay:
            seconds: 0.1
  - delay:
      hours: 0
      minutes: 5
      seconds: 0
      milliseconds: 0
  - repeat:
      count: 2
      sequence:
        - repeat:
            for_each: "{{ where_happens }}"
            sequence:
              - variables:
                  entity_settings: climate.room_climate_{{ repeat.item }}
                  entity: "{{ repeat.item }}"
              - repeat:
                  sequence:
                    - service: climate.set_hvac_mode
                      data:
                        hvac_mode: auto
                      target:
                        entity_id: "{{ entity_settings }}"
                  until:
                    - "{{ states(entity_settings) == 'auto' }}"
              - delay:
                  seconds: 0.1
  - service: notify.notify
    data:
      message: Close windows, duh!
      title: Vent
mode: single
icon: mdi:air-filter

For future reference, you don’t mark your own post with the Solution tag if it duplicates a solution given to you by someone else.

Your posted is based entirely on the proposal presented here which suggested to replace your outer repeat count with a repeat for_each and have it iterate a variable (wohnung) containing a list of names.

For more information about how to use the Solution tag, refer to guideline 21 in the FAQ.