Automation "numerical_state, below: X" is not triggered until the threshold is crossed

Tags: #<Tag:0x00007f466d9ca170>

I have the following automation:

# turn off only if below AQI value and in AUTO mode, not in silent or manual (favouri$
- alias: 'Auto off air purifier living room'
  trigger:
    platform: numeric_state
    entity_id: 'fan.air_purifier_living_room'
    value_template: "{{ state_attr('fan.air_purifier_living_room', 'aqi') }}"
    below: 10
    for:
      minutes: 5
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: 'fan.air_purifier_living_room'
        state: 'on'
      - condition: template
        value_template: "{{ is_state_attr('fan.air_purifier_living_room', 'mode', 'auto') }}"
  action:
    - service: fan.turn_off
      entity_id: 'fan.air_purifier_living_room'

The automation does not get triggered properly.
For example, this is the graph of the attribute “aqi”:


As you can see, the trigger should fire, since the AQI is well lower than 10 and for a long time: the purifier went on at 20:00.

When I reload the automations or restart HA, nothing happens.

If I remove the

    for:
      minutes: 5

and reload the automations, the automation gets triggered as it should and the purifier goes off.

What could be the cause of the issue? the format I used was copied from the examples about triggering, it should be correct.

Thanks in advance.

You’re right, that’s weird.
My fans are controlled from switches, so I’ve never seen the fan domain previously. I’ve also not seen entity_id’s in quotes before either. It looks like you have used the automation editor BUT you seem to have cleaned up the code. Regardless, we’ll done for that.
Perhaps there is a spike of too short a duration for it to appear on the sensor history (I’m grasping at straws here)
All I can suggest is you reverse the duration.
In the automation, remove the for, and trigger a script with a delay in it of the 5 mins, before turning the fan off.
Write a new automation, with the value going above 10 ‘for:’ 10 secs then have the action cancel the script.
It may get round your problem, and/or it may provide you with more information to diagnose.

I’ll do as you say, but while removing the “for” and reloading the automations made it turn off, it didn’t work anymore afterwards: I have an automation starting it every two hours and 40 minutes later it’s still on even if the values were always low.

So it’s not the “for”.

I’m not sure what’s the problem anymore

try forcing the value_template to return a number. Right now I think it returns a string. And a number won’t be able to be compared to a string.

try this:

value_template: "{{ state_attr('fan.air_purifier_living_room', 'aqi') | int }}"
1 Like

The only thing I’d change is to use |float instead of |int as the sensor is capable of measuring fractions. Just depends if you want it to trigger at 9.9 instead of 9.

yeah, good point.

I was just going by the fact that they used an int in the trigger.

Since that is the only place that template is used if you don’t need a float then an int will work just as good. If you need a float for better precision then use that instead.

Yesterday night I removed all the “conditions” and the “for 5 minutes” and I let it run.

This is the result:

First of all: only motor1 means Auto mode and motor1 + motor2 means “Silent” mode, which I trigger every two hours to move the air around, only if the fan is OFF and left in mode Auto:

- alias: 'Silent on air purifier living room'
  trigger:
    platform: time_pattern
    hours: "/2"
    minutes: 0
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: 'fan.air_purifier_living_room'
        state: 'off'
      - condition: template
        value_template: "{{ is_state_attr('fan.air_purifier_living_room', 'mode', 'auto') }}"
  action:
# turns on automatically when speed is changed
#    - service: fan.turn_on
#      entity_id: 'fan.air_purifier_living_room'
    - service: fan.set_speed
      entity_id: 'fan.air_purifier_living_room'
      data:
        speed: 'Silent'
    - delay: 0:10
    - service: fan.set_speed
      entity_id: 'fan.air_purifier_living_room'
      data:
        speed: 'Auto'

At about 1:30 I started it manually. It should have been turned off by HA immediately (it was already below 10), but it took an hour! Actually, it took until the AQI crossed the threshold “at/above 10 -> below 10”

Later at 4:00 Silent mode was triggered. AQI was above 10 so it stayed on. After 10 minutes it switched back to Auto mode, and when it fell below 10 it went off as expected. Crossing the defined threshold is detected quickly.

At 6:00 again Silent mode, and at the end it switched to Auto, and then off because briefly below 10.

At 8:00 it was turned on in Silent mode, but my first automation turned it off as soon as AQI dropped below 10. Few minutes later the automation than handles the Silent mode switched the fan back to Auto, and this turned on the fan (changing speed always turns the fan on). The fan continued to stay on for a while, more specifically until AQI reached 10 and fell again below it.

A similar behaviour takes places at 10:00: Silent mode, back to Auto mode, and fan gets turned off only after AQI reaches AQI=10 and falls again below it.

I think the issue is clear: it looks like that the automation, with “below: 10”, doesn’t monitor the AQI by itself, but it monitors only the crossing of the threshold 10.
So if I turn on the fan when it’s <10, the automation is not triggered just by looking at the values of AQI, but it is triggered when the AQI crosses “at/from above to below” the threshold I gave.

Am I correct?

If so, this is not what I want. I want the fan to go off even without waiting for a crossing to take place… how to do it?

I must have missed the description from https://www.home-assistant.io/docs/automation/trigger/ … this is the intended behaviour.

I’m trying templates now.

Well that is what your automation says should happen, ie trigger when goes below (distinct from if already below).
Set a hass start event to put the unit on if the value is in the range you want it to be triggered at

Trying templates to achieve what exactly ?

If in the last 5 minutes the value has always been below a threshold, turn off:

- alias: 'Auto off air purifier living room'
  trigger:
    platform: template
    value_template: "{{ state_attr('fan.air_purifier_living_room', 'aqi') | int < 10 }}"
    for:
      minutes: 5
...

AND
"do not execute the automation if the device is already off or is in Silent mode:

  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: 'fan.air_purifier_living_room'
        state: 'on'
      - condition: template
        value_template: "{{ is_state_attr('fan.air_purifier_living_room', 'mode', 'auto') }}"

Obviously it didn’t work either, since triggers work on “changes” and the template I used is equivalent to the numeric_state already offered by HA.

At this point I think that the only way to have HA turn off the fan when the AQI values in the last X minutes have been lower than a threshold is to trigger the automation every 5 minutes and add a condition which checks a filtered value of the AQI sensor.

The filter should calculate the max value within a defined time window, therefore I see only two options: “statistics” sensor, which publishes also “max”, which is the cleanest solution but does many other calculations, or use the “filter” sensor with a time filtered average, which actually may not save many CPU cycles.

The statistics or filter sensors can be used as trigger as well, but it could happen that after I turn on the fan the AQI stays on the same value (the minimum) for a long time, resulting in the automation never being triggered (it works on changes if value).
Or I can trigger the automation on change of state, change of speed, change of time-averaged value and use a condition to test the averaged value.

This should cover every practical case and avoid the time trigger.

As you have guessed, I’m a novice.
Would you recommend time triggering with a condition to check the time-averaged or statistics/max value, or triggering based on change of state/speed/filtered sensor, followed by the same condition?

I’m very lost on exactly what you are trying to accomplish. I don’t think you ever said what you want to have happen.

Maybe it would be best to just start over with a clear statement of the exact end result you want to do.

Fair enough, I wasn’t clear.

I would like the air purifier turn off automatically when the readings stay below a certain threshold for a defined amount of time.

Sometimes the readings decrease and cross the threshold from above because of the purifier itself, but sometimes I turn on the fan manually as precaution and the readings never get above the threshold.
In this case, I’d like the purifier to wait for a certain amount of time and then turn off because it’s unneeded.

For example, before I turn on the fire the readings are low. I turn on the purifier manually to avoid having to wait for the pm2.5 to increase, but most of the time the stove / chimney work well and the readings never increase. A delay of 10 minutes would avoid me to stay there and check.

Then try this:

- alias: 'Auto off air purifier living room'
  trigger:
    - platform: numeric_state
      entity_id: 'fan.air_purifier_living_room'
      value_template: "{{ state_attr('fan.air_purifier_living_room', 'aqi') }}"
      below: 10
      for:
        minutes: 5
    - platform: state
      entity_id: 'fan.air_purifier_living_room'
      to: 'on'
      for:
        minutes: 5
  condition:
    - condition: state
      entity_id: 'fan.air_purifier_living_room'
      state: 'on'
    - condition: template
      value_template: "{{ state_attr('fan.air_purifier_living_room', 'aqi') | int < 10 }}"
    - condition: template
        value_template: "{{ is_state_attr('fan.air_purifier_living_room', 'mode', 'auto') }}"
  action:
    - service: fan.turn_off
      entity_id: 'fan.air_purifier_living_room'

that way if the AQI falls below 10 for 5 minutes or the fan gets turned on (for any reason) for 5 minutes it will then turn off as long as the AQI is less than 10.

the only thing I don’t know is if the “air purifier in auto” condition will be a problem. If you need to switch the purifier out of auto to turn it on manually (I assume so…) then you should remove that condition.

If you still need that condition in the original automation for some reason then you will probably need to split these up into two different automations - one to turn it off in auto after 5 minutes below 10 and one to turn it off in manual after 5 minutes.

Thanks for the suggestion.

For info: the purifier can be turned on without touching the mode, the last one used is retained.

Regarding my question and idea, here an example of the PM2.5 (AQI).

Case 1): when the device is turned on (it doesn’t matter how: manual or automatic) the AQI>10, then it decreases.

Case 2): the AQI<10 when the device is turned on.

Since I made the graph wrong, let’s assume the “for” is set to 10 minutes, not 5 as in the above examples.

If I use your automation, the first condition (AQI<10 for 10 minutes) fires at 16 minutes (6+10) for curve 1) and never for case 2). For case 1) that’s the intended behaviour, for case 2) not.

The second trigger of your automation (state = on for 10 minutes) fires at 10 minutes for case 1) and since at 10 minutes AQI <10 already, at 10 minutes the purifier goes off. This is not my goal, since I want “at most 10 minutes of operation when AQI < threshold”.

For curve 2), the second trigger fires at 10 minutes and the purifier goes off. This is the expected behaviour.

If I add a new virtual sensor:

# configuration.yaml

sensor:
  - platform: template
    sensors:
      purifier_aqi:
        friendly_name: "Air quality"
        value_template: "{{ state_attr('fan.air_purifier_living_room', 'aqi') }}"
        unit_of_measurement: "μg/m³"
  - platform: statistics
    name: purifier_aqi_stats
    entity_id: sensor.purifier_aqi
    max_age:
      minutes: 10

and

- alias: 'Auto off air purifier living room'
  trigger:
# checks every time the AQI changes (but it could be every x minutes instead)
    - platform: numeric_state
      entity_id: 'fan.air_purifier_living_room'
      value_template: "{{ state_attr('fan.air_purifier_living_room', 'aqi') }}"
  condition:
    - condition: state
      entity_id: 'fan.air_purifier_living_room'
      state: 'on'
    - condition: template
      value_template: "{{ state_attr('sensor.purifier_aqi_stats', 'max_value') | int < 10 }}"
    - condition: template
        value_template: "{{ is_state_attr('fan.air_purifier_living_room', 'mode', 'auto') }}"
  action:
    - service: fan.turn_off
      entity_id: 'fan.air_purifier_living_room'

This way every time the AQI changes the automation checks whether the max value of the last 10 minutes was <10 and if so, turns off the purifier.

Of course, this solution looks back also before the fan was turned on. I didn’t think about this.

It could be solved by modifying the first condition:

    - condition: state
      entity_id: 'fan.air_purifier_living_room'
      state: 'on'
      for: 
        minutes: 10

Yes, what you are describing there is the exact reason I put two triggers in the automation - one to handle case 1 (which you said above is working correctly for case 1) and one to handle case 2 (which you said above was also working correctly).

I didn’t expect that trigger 2 would work for case 1. And it doesn’t need to work for case 1. As I said, that’s the entire point of creating trigger 2 in the first place.

Are you just misunderstanding how triggers work?

All triggers are always “or”. It only takes one trigger or the other trigger for the automation to start.

As you are describing it, I think the automation will do what you want.

Did you even try the suggestion?

As to the rest of your post I’m not really sure if you are saying that your latest changes ultimately solved your problem or not.

Curve 2) is no problem.

However, since the triggers are in OR as you said, in the case of curve 1) the automation will fire at 10 minutes due to the second trigger, not at 16 minutes (6+10) as I would prefer.

Still acceptable.

But that’s not what that says.

It says you want it to be on for no more than 10 minutes if AQI is less than 10.

in case 2 AQI is already less than 10 and has been for some unknown period of time in history. So when you turn the air purifier on the timer starts and 10 minutes later it shuts off which satifies the stated goal.

However… if you want it to be on for 16 minutes then just change the time in the second trigger to 16 minutes.

- platform: state
  entity_id: 'fan.air_purifier_living_room'
  to: 'on'
  for:
    minutes: 16

Personally, the way your purifier is measuring the air quality is going to be an issue, because it appears to be inexact and it appears to jump over and under your threshold quite often. Likely going to take some trial and error.

Conceptually, if it were me I’d create a single ON Automation and 2 OFF automations. I’d also make sure that the threshholds you’re setting for air quality are far enough apart that the automations don’t false-positive and turn on/off when not wanted.

ON:
Trigger. Air quality over 10 for 15 minutes.

OFF #1:
Trigger: Air quality under 7 for 10 minutes

OFF #2:
Trigger: Air purifier to on
Condition: Air quality < 9.0
Action: Delay 10 minutes, then turn off

ON: is going to trigger automatically when air quality is bad enough for long enough.
OFF #1: is going to trigger when air quality is good enough for long enough
OFF #2: is going to trigger when units goes TO on when air quality is not that bad, then waits 10 minutes, then turns off.

The trick is going to be testing to ensure that your thresholds are set far enough apart to not generate false positives… You’ll have to play with the quality thresholds and time delays and such to get it more in line with what you want.

The other option (for the MANUAL operation timer) is to find another way (besides the button on the unit) to trigger it to turn on, and then you can set a variable / input_boolean and have the OFF #2 automation to use that variable/boolean value as a condition.

IE ON #2: Trigger a script to turn ON the purifier and set a variable/boolean to TRUE
OFF #2: is then Trigger - Purifier to ON, Condition Boolean = TRUE, Action: Wait xx minutes, unset variable, turn unit off

Obviously more than one way to ‘skin a cat’ :wink: