How to "cool down" an automation

Just something I’ve been thinking about for a while. I have an automation that is triggered by a line crossing alert in my driveway. Works great 95% of the time. Sometimes, there are kids playing in the driveway while I’m away (which is ok), but it ends up triggering a lot of alerts. I have other automation that I use a condition to not run if it has ran in the last minute, but in this case I’d like something more like a count, etc.

Something like if you’ve sent 5 alerts i the past 5 minutes, then turn off the automation for 5 minutes then turn it back on. Or something like that, I’ve having trouble thinking of the best way to do that.

I could see this being useful for other stuff as well. Anyone done anything similiar?

You could probably use an automation to control the automation, so have a 2nd automation that turns your driveway automation off if a certain number of alerts are sent.

Hmm, not a bad idea, Now how to do a rolling 5 minute count. :slight_smile:

Couldn’t you have a input switch that turns off after 5mins and then use It as a condition in your automation.

You can do it with a delay, or a timer, or any other way, probably.

    - alias: Driveway Motion Detected Alarm
      trigger:
        - *motion detected*
      action:
        - *send alert*
        - delay: '00:05:00' 

This should work because, AFAIK, an automation cant re-trigger while it’s running, so the delay action will keep the automation running for 5 minutes keeping it from re-triggering during that time.

With a timer:

    - alias: Driveway Motion Detected Alarm
      trigger:
        - *motion detected*
      condition: and
      conditions:
        - condition: state
          entity_id: timer.keep_driveway_alert_off
          state: 'idle'
      action:
        - *send alert*
        - service: timer.start
          data:
            entity_id: timer.keep_driveway_alert_off

#In Configuration.YAML:
timer:
  keep_driveway_alert_off:
    name: Time remaining
    duration: 300

Good idea, but not quite what I’m looking for. I want it to “pause” if it’s gone off a certain amount of times in a specific time period.

So if I got 3 alerts in 5 minutes, then nothing would happen, life as usual

But if I got 5 alerts in 5 minutes, then turn off or pause the automation for X minutes, then turn it back on.

So it needs to be a combination of a counter that resets 5 minutes after the 1st count, and then maybe use that as a condition?

Gotcha.

You probably want to take a look at the counter component then.

#In configuration.yaml
counter:
  driveway_trigger_counter:
    initial: 0
    step: 1

input_boolean:
  disable_driveway_notify:
    name: disable driveway notifications

timer:
  driveway_timer:
    name: Time remaining
    duration: 300

#Automation to alert and count
- alias: Driveway Motion Detected Alarm
      trigger:
        - *motion detected*
      condition: and
      conditions:
        - condition: state
          entity_id: input_boolean.disable_driveway_notify
          state: 'off'
      action:
        - *send alert*
        - service: counter.increment
          data:
            entity_id: counter.driveway_trigger_counter
        - service: timer.start
          data:
            entity_id: timer.driveway_timer
          
#Automation to disable alerting  
- alias: Driveway Motion disabler
      trigger:
        - *motion detected*
      condition:
        condition: numeric_state
        entity_id: counter.driveway_trigger_counter
        above: 5
      action:
        - service: input_boolean.turn_on
          entity_id: input_boolean.disable_driveway_notify
        - delay: '00:05:00' 
        - service: input_boolean.turn_off
          entity_id: input_boolean.disable_driveway_notify
        - service: counter.reset
          data:
            entity_id: counter.driveway_trigger_counter

#Reset counter after timeout
- alias: Driveway Motion timeout
  trigger:
    - platform: event
      event_type: timer.finished
      event_data:
        entity_id: timer.driveway_timer
  action:
    - service: counter.reset
          data:
            entity_id: counter.driveway_trigger_counter

I’m just writing this freehand, so it might not work right off the bat, but it’s the general idea.

What it should do is on every detection of motion, if the disabeler boolean is off, send an alert, increment the counter, and start a timer (or reset if already running).
If the counter exceeds 5 counts, it will turn on the disabeler boolean stopping further alerts. After 5 minutes it will turn the disabeler boolean off, and reset the counter.
If 5 minutes pass since the last motion trigger, the counter will be reset.

I think that meets your criteria.

2 Likes

Love it, makes perfect sense. I’ll post something back once I get it all set up.

No problem.

I edited my psudo code a bit, but I think you might be able to reduce it by 1 automation and get rid of the input bool by making the counter being under 5 a condition of the alert, and just have the timer reset it after 5 minutes after the last time it was triggered.

I can’t test any of it at the moment, but I’m sure you get the idea. Good luck!

The only thing that I need to tweak, is that with the timer approach as is, it could disable if I had one motion event every 4 minutes and 59 seconds over a period of 25 minutes. (I think so anyway), I’m looking to turn off if I get a lot of alerts in a short (5 minute) time.

This is super close, I think I can get it.

As I was typing this, I remembered a great example of what I’m trying to accomplish.

I have one camera that wasp’s seem to love. Sometimes a wasp will crawl over the lens, triggering alerts, and I end up with 20 alerts in a 10 minute period, etc. In that case I’d just like to turn it off for a few minutes and then back on, hopefully the wasp has moved on to somewhere else.

Here’s a simple one liner using the last time the automation was triggered. Doesn’t have as advanced a cooling off system but for most of my cases this does the trick.

  - condition: template # Throttle by not triggering push messages if triggered in the last 10 seconds
    value_template: >
      {{ not state_attr('automation.doorbell','last_triggered') or (as_timestamp(now()) - as_timestamp(state_attr('automation.doorbell','last_triggered'))) > 10 }}
8 Likes

Just an FYI, this was broken in 0.111. I don’t have a workaround for it yet.

What part of it broke?
If you run that template in the template tool, what error do you get?

I’ve tested the template with one of my automations and I’ve got the correct result and no error.

What error do you get?

So I use the condition further down in my automation and by that point it’s already changed the last_triggered as per the changes on this recent release.

Maybe if the condition is right below the trigger this won’t happen.

This worked for me. In the UI, after the action I actually need the automation to run, I added an “action” and chose “wait for time to pass”. This worked because the automation does not re-run while it is running, but it still runs immediately if it was not recently triggered.

I just had to do a cooldown so I’ll offer this as another option. With the introduction of timer helpers, we now have an easy way to do cooldowns that aren’t impacted by HA restarts.

I have an automation that sends me an image when there is someone detected at my front door. However if my kids are playing in the front yard, I don’t want to be barraged by notifications. So I used a cooldown timer so that I wouldn’t be notified unless 5 minutes have passed since a person was last detected.

This automation triggers when a person is detected. It only sends a notification if the timer is expired. And then it starts (or restarts) the timer.

alias: Front Door Image when Person is Detected
description: ""
trigger:
  - platform: state
    entity_id: sensor.front_door_detected_object
    to: person
action:
  - if:
      - condition: state
        entity_id: timer.front_door_notification_cooldown
        state: idle
    then:
      - service: camera.snapshot
        data:
          filename: /config/www/obscurefilename.jpg
        target:
          entity_id: camera.front_door
      - service: notify.mobile_app_my_phone
        data:
          title: Front Door Motion!
          message: Long-press to view image
          data:
            image: /local/obscurefilename.jpg
  - service: timer.start
    data: {}
    target:
      entity_id: timer.front_door_notification_cooldown
mode: single

2 Likes

Hey guys, I have a question. I’m not great with yaml, but I have a slightly more elaborate cooldown in a python script I use that I hope to use the same logic in yaml.

Basically, the cooldown is incremental from the last cooldown. So if an automation triggers and the PreviousCooldown + cooldown is more than the CurrentTime + cooldown, then add cooldown to the PreviousCooldown and don’t trigger. Else PreviousCooldown = CurrentTime + cooldown

So basically the more activity you have, the increasingly fewer triggers you’ll have, until time clears.

Here is the python logic using pickle (as it has to save the previous time, not sure how to achieve this with Home Assistant)

def coolDownTimer(secondsCool, picklename):
 currentTime = datetime.datetime.now()
 currentTimeCoolDown = datetime.datetime.now() + datetime.timedelta(seconds=secondsCool)
 try:
  with open(picklename, 'rb') as f: # Using Pickle to make sure to not send duplicate notifications
   previousTime = pickle.load(f)
   print("Previous Time: %s" % previousTime)
   print("Current Time: %s" % currentTime)
  with open(picklename, 'wb') as f:
   pickle.dump(currentTimeCoolDown, f, pickle.HIGHEST_PROTOCOL)
 except EOFError:
  with open(picklename, 'wb') as f:
   pickle.dump(currentTimeCoolDown, f, pickle.HIGHEST_PROTOCOL)
 except FileNotFoundError:
  os.open(picklename, os.O_CREAT | os.O_EXCL)
 if currentTime > previousTime:
  print(currentTime > previousTime) 
  return True;
 if currentTime < previousTime:
  print("COOL DOWN STILL ACTIVE")
  with open(picklename, 'wb') as f:
   extraCoolDown = previousTime + datetime.timedelta(seconds=secondsCool)
   pickle.dump(extraCoolDown, f, pickle.HIGHEST_PROTOCOL)
   return False;