Script with Repeat...While Loop causes memory usage to sky rocket and crash Core

I have been having problems with my HA setup since last October. After applying the 2021.10 Core update my RPi4 started crashing within 24 hrs repeatedly. I had not changed any of my configuration. I also updated the system to 6.0 (IIRC) so thought I might be suffering from issue #1119 affecting Rpi 4 stability.
However, I have now moved to a generic x64 installation and the problem persists.
My investigations have shown that at some point in the day (variable) the memory usage starts to clock up from ~10% and within 30 minutes it gets to ~90% and swap file is 100% utilised. This is when the web UI becomes unresponsive and automations no longer trigger.
I have tried a complete fresh configuration (about 5 times) and always end up with the same crash using just add-ons from the Supervisor and official integrations. I have a couple of automations and I have now correlated the triggering of one of them with the memory blow up.
The automation triggers when the temperature sensor reading on my hot water cylinder falls below a set level. When triggered, it calls a script to turn on the hot water boost function on the boiler programmer. The boost cycle lasts for a hour and then turns itself off. I have observed that one cycle of the boost is not always sufficient to get the water back up to temperature. So, the script uses a repeatā€¦ while control loop with a 15 minute delay at the end of each loop to check the temperature and the state of the boost switch and turn it on again if necessary.
I have deduced that itā€™s the script causing the memory blow uo by running off some hot water from the cylinder until the automation triggers and watching the memory statistics from the systemmonitor sensors.

Since the code was working when I first implemented it, I think it may be a memory leak in the Coreā€™s handling of the repeatā€¦ while loop.

Can anyone corroborate this issue?
Iā€™m posting my script below in case it is poor and the level of ineptitude is now being caught by the Core code. Please comment if you see anything wrong with it.

hot_water_boost:
  sequence:
  - repeat:
      while:
      - condition: numeric_state
        entity_id: sensor.hot_water_temperature
        below: '50'
      sequence:
      - condition: state
        entity_id: switch.hot_water_boost
        state: 'off'
      - service: switch.turn_on
        target:
          entity_id: switch.hot_water_boost
      - delay:
          hours: 0
          minutes: 15
          seconds: 0
          milliseconds: 0
  - service: switch.turn_off
    target:
      entity_id: switch.hot_water_boost
  mode: single
  alias: Hot water boost

This is what I suspect is happening:

Your switch is on but the temperature is below 50.

This causes your while loop to start the sequence but then the switch condition in the loop is false so the loop resets to the start of the loop instantly (skipping the delay) this will repeat happening very, very fast, consuming a lot of resources.

Delete the switch condition in the loop to fix this (turning on a switch that is already on does no harm):

hot_water_boost:
  sequence:
  - repeat:
      while:
      - condition: numeric_state
        entity_id: sensor.hot_water_temperature
        below: '50'
      sequence:
      - service: switch.turn_on
        target:
          entity_id: switch.hot_water_boost
      - delay:
          hours: 0
          minutes: 15
          seconds: 0
          milliseconds: 0
  - service: switch.turn_off
    target:
      entity_id: switch.hot_water_boost
  mode: single
  alias: Hot water boost

This will slow the loop down to running only every 15 minutes and should solve your issue. The loop will still stop and the switch will turn off when the temperature rises above 50. Though this could take up to 15 minutes.

Thereā€™s probably a better way to do this. Let me think about itā€¦

EDIT: a better way would be to forget the loop and just add anther trigger to your automation.

Trigger 1) temperature below 50
Trigger 2) boost switch turns to: 'off`

Condition 1) temperature is below 50
Condition 2) boost switch is off

Action: turn switch on (no loop needed).

This way at the end of your boost cycle the automation is re-triggered and the temperature is checked to see if another boos cycle is needed.

alias: Hot water boost on
trigger:
  - platform: numeric_state
    entity_id: sensor.hot_water_temperature
    below: 50
  - platform: state
    entity_id: switch.hot_water_boost
    from: "on"
    to: "off"
condition:
  - condition: numeric_state
    entity_id: sensor.hot_water_temperature
    below: 50
  - condition: state
    entity_id: switch.hot_water_boost
    state: "off"
action:
  - service: switch.turn_on
    target:
      entity_id: switch.hot_water_boost
mode: single

alias: Hot water boost off
trigger:
  - platform: numeric_state
    entity_id: sensor.hot_water_temperature
    above: 50
    for: 60 # adjust as necessary
action:
  - service: switch.turn_off
    target:
      entity_id: switch.hot_water_boost
mode: single
1 Like

Besides what Tom said, probably a more efficient way, in an event oriented fashion, would be to have the automation handle both cases:

Something like:

- id: 'foobar'
  trigger:
  - entity_id: sensor.hot_water_temperature
    platform: state
  - entity_id: switch.hot_water_boost
    platform: state
  action:
  - choose:
    - conditions:
        - condition: numeric_state
          entity_id: sensor.hot_water_temperature
          above: '50'
      sequence:
      - service: switch.turn_off
        target:
          entity_id: switch.hot_water_boost
    default:
    - service: switch.turn_on
      target:
        entity_id: switch.hot_water_boost
1 Like

In addition, if you want to have it triggered every 15 minutes or so, you might prefer the use of a time pattern trigger.

Thereā€™s absolutely no need. State based triggers are more efficient than time pattern triggers. They should be a last resort.

the guys are right that time pattern trigger is better , i have the below running for time-laps project for the past 4 months on 24/7 bases with no issue



alias: Vin_BLUE_led
description: ''
trigger:
  - platform: time_pattern
    minutes: /20
condition: []
action:
  - service: switch.turn_on
    target:
      entity_id:
        - switch.sonoff_1000f550f0_1
  - delay:
      hours: 0
      minutes: 0
      seconds: 1
      milliseconds: 0
  - service: switch.turn_off
    target:
      entity_id:
        - switch.sonoff_1000f550f0_1
mode: single

I agree, but I it is an option that I didnā€™t want to leave out since I donā€™t know the considerations of @petitpiton in his initial automation.

I see a lot of delay/wait/state for stuff and in my experience they are very unreliable once the ā€œwaitā€ is too long. See my unresolved closed github issue: Trigger on Device state duration fails after save random automation Ā· Issue #48584 Ā· home-assistant/core Ā· GitHub

So the reason for adding the trigger is to help in case one feels the need to have a time based trigger.

This definitely not the best way in the original posterā€™s situation. Trigger on things happening (state changes) not some arbitrary time interval.

For your time lapse, it is appropriate and possibly one of the only times I have ever seen this trigger used appropriately.

You probably are right that it works for time-laps due to small delay and no condition , but isnā€™t possible with a small change to modify my example to include a conditions that works for original posterā€™s situation ? I am thinking now to add a condition to check if a file is moved prior to the next trigger

I only recently joined this forum and I think i did things my way which is for sure not optimal ( or even wrong ) and I want to see the best way to also modify all of my automatons . Thanks for the help

You could do it but it would not be as efficient as the state based automation that I and Chris (his version is better IMO) have proposed. The automation only fires when needed, based on state changes. Unnecessary periodic triggering is avoided.

1 Like

I like the example you both listed but wanted to know what is the frequency of state changes sensing in HA Is it immediate or fixed intervals or is it dependent on the sensor . I have seen situation where a temperature sensor state is changed only every 10 minutes and others every 5 minutes . Is it dependent of the type of sensor if Bluetooth or zigbee ā€¦etc

I hope i did not ask a lot or hijack the post but I assumed the original problem is solved with your examples

Thanks ,

That.
But in the matter at hand, it doesnā€™t matter. Checking via a time_pattern wonā€™t force a state refresh of the sensor.

Itā€™s basically polling vs. pushing: With a time_pattern, you are running the automation every x, not knowing whether a value has changed or not; with a state trigger, you know something has changed and HA is pushing the execution of the automation.

Iā€™d say that a time_pattern with a condition can almost certainly be rewritten with a trigger, and same goes for long-running while and repeat

Thanks. I like this, but the reason I split between automation and script was so that I can have a low temperature to trigger the automation (40) and a higher figure to turn off the boost (50) in the script.
So the heating boost should be: on if the temperature is below 40, off if above 50 and may be on or off in between depending on whether itā€™s heating up or cooling down.
Is this possible with your approach?

You could do it with the two automations I posted. Just change the on temperature.

1 Like

Unless I missed something.
But then, if the booster stops its cycle when the temperature is between 40 & 50, nothing will happen, which is not quite the same as the initial proposition.

- id: 'foobar'
  trigger:
  - entity_id: sensor.hot_water_temperature
    platform: state
  - entity_id: switch.hot_water_boost
    platform: state
  action:
  - choose:
    - conditions:
        - condition: numeric_state
          entity_id: sensor.hot_water_temperature
          below: '40'
      sequence:
      - service: switch.turn_on
        target:
          entity_id: switch.hot_water_boost
    - conditions:
        - condition: numeric_state
          entity_id: sensor.hot_water_temperature
          above: '50'
      sequence:
      - service: switch.turn_off
        target:
          entity_id: switch.hot_water_boost

Yeah, the boost needs to be kept on until it reaches 50 on the way up, but the temperature can fall to 40 before the heating sequence starts againā€¦

Thank you @tom_l Iā€™m going to implement this. As a ā€˜non-programmerā€™ the rapid looping didnā€™t occur to me.

Donā€™t forget to change the numeric condition value as well as the trigger in the on automation.

@tom_l Hereā€™s what I have put in the automation.

- id: 'hot_water_boost_on'
  alias: Hot water boost on
  trigger:
    - platform: numeric_state
      entity_id: sensor.hot_water_temperature
      below: 40
    - platform: state
      entity_id: switch.hot_water_boost
      from: "on"
      to: "off"
  condition:
    - condition: numeric_state
      entity_id: sensor.hot_water_temperature
      below: 50
    - condition: state
      entity_id: switch.hot_water_boost
      state: "off"
  action:
    - service: switch.turn_on
      target:
        entity_id: switch.hot_water_boost
  mode: single

- id: 'hot_water_boost_off'
  alias: Hot water boost off
  trigger:
    - platform: numeric_state
      entity_id: sensor.hot_water_temperature
      above: 50
      for: 
        minutes: 2 # adjust as necessary
  action:
    - service: switch.turn_off
      target:
        entity_id: switch.hot_water_boost
  mode: single

  condition:
    - condition: numeric_state
      entity_id: sensor.hot_water_temperature
      below: 40 ### <---