Fire automation on interval

I’m working on implementing my automation from this thread.

Here are the automations:

- id: garage_light_automation_1
  alias: Garage Lights Door Event Handler
  description: Update garage_timer_input on door events when it is night
  trigger: 
  - platform: state
    entity_id: binary_sensor.garage_door
    from: 'off'    
    to: 'on'
  condition:
  - condition: state
    entity_id: sun.sun
    state: below_horizon
  - condition: or
    conditions:
    - condition: numeric_state
      entity_id: input_number.garage_timer_input
      above: -1
    - condition: state
      entity_id: light.frst_garage_interior
      state: 'off'
  action:
    - service: input_number.set_value
      data_template:
        entity_id: input_number.garage_timer_input
        value: "{{ [states('input_number.garage_timer_input')|int, 5]|max }}"
- id: garage_light_automation_2
  alias: Garage Lights Counter State Handler
  description: Decrement garage_timer_input and take actions
  trigger: 
    - platform: state
      entity_id: input_number.garage_timer_input
  action:
    - delay: '00:00:01'
    - condition: numeric_state
      entity_id: input_number.garage_timer_input
      above: -1
    - service_template: "{% if (states('input_number.garage_timer_input')|int - 1) == 0 %}\n   switch.turn_off\n\
        {% else %}\n   switch.turn_on\n{% endif %}"
      entity_id: light.frst_garage_interior      
    - service: notify.mobile_app_devans_iphone
      data_template:
        title: Garage Timer
        message: Garage lights will turn off in {{ trigger.to_state.state|int - 1 }} minutes.
        data:
          apns_headers:
            apns-collapse-id: garge-interior-light
          push:
            category: gargetimer
    - wait_template: "{{ states('input_number.garage_timer_input') != trigger.to_state.state }}"
      timeout: '00:01:00'
    - service: input_number.decrement
      entity_id: input_number.garage_timer_input
- id: garage_light_automation_3
  alias: Garage Lights iOS App Event Handler
  description: Update garage_timer_input on app events
  trigger:
  - platform: event
    event_type: ios.notification_action_fired
    event_data:
      actionName: SLEEP_TIMER
  - platform: event
    event_type: ios.notification_action_fired
    event_data:
      actionName: DISABLE_TIMER
  action:
    service: input_number.set_value
    data_template:
      entity_id: input_number.garage_timer_input
      value: >
        {% if trigger.event.data.actionName == "SLEEP_TIMER" %}
          {{ states('input_number.garage_timer_input')|int + 5 }}
        {% else %}
          -1
        {% endif %}

I’m running in to a problem where the garage_light_automation_2 automation is firing twice (notify with 5 then 3, etc). From reading around it appears I need to solve this by placing the delay inside of a script which waits 1 minute and then decrements the count.

I’m ok with this, but I was wondering if someone could explain to me why this is happening?



{
    "event_type": "state_changed",
    "data": {
        "entity_id": "automation.garage_lights_counter_state_handler",
        "old_state": {
            "entity_id": "automation.garage_lights_counter_state_handler",
            "state": "on",
            "attributes": {
                "last_triggered": "2020-03-09T20:11:15.062080+00:00",
                "id": "garage_light_automation_2",
                "friendly_name": "Garage Lights Counter State Handler"
            },
            "last_changed": "2020-03-09T20:08:45.919139+00:00",
            "last_updated": "2020-03-09T20:11:15.062627+00:00",
            "context": {
                "id": "6aed3a96d82346c0904506c0331dc216",
                "parent_id": "4dc67bcebc074630bed88021a84277c1",
                "user_id": null
            }
        },
        "new_state": {
            "entity_id": "automation.garage_lights_counter_state_handler",
            "state": "on",
            "attributes": {
                "last_triggered": "2020-03-09T20:11:15.077279+00:00",
                "id": "garage_light_automation_2",
                "friendly_name": "Garage Lights Counter State Handler"
            },
            "last_changed": "2020-03-09T20:08:45.919139+00:00",
            "last_updated": "2020-03-09T20:11:15.077951+00:00",
            "context": {
                "id": "6aed3a96d82346c0904506c0331dc216",
                "parent_id": "4dc67bcebc074630bed88021a84277c1",
                "user_id": null
            }
        }
    },
    "origin": "LOCAL",
    "time_fired": "2020-03-09T20:11:15.078047+00:00",
    "context": {
        "id": "6aed3a96d82346c0904506c0331dc216",
        "parent_id": "4dc67bcebc074630bed88021a84277c1",
        "user_id": null
    }
}

Event 8 fired 4:11 PM:

{
    "event_type": "state_changed",
    "data": {
        "entity_id": "automation.garage_lights_counter_state_handler",
        "old_state": {
            "entity_id": "automation.garage_lights_counter_state_handler",
            "state": "on",
            "attributes": {
                "last_triggered": "2020-03-09T20:10:12.641937+00:00",
                "id": "garage_light_automation_2",
                "friendly_name": "Garage Lights Counter State Handler"
            },
            "last_changed": "2020-03-09T20:08:45.919139+00:00",
            "last_updated": "2020-03-09T20:10:12.642501+00:00",
            "context": {
                "id": "77aa1bc4d38f4acf956628cdf19ff127",
                "parent_id": "1609617444f944449db733710284721a",
                "user_id": null
            }
        },
        "new_state": {
            "entity_id": "automation.garage_lights_counter_state_handler",
            "state": "on",
            "attributes": {
                "last_triggered": "2020-03-09T20:11:15.062080+00:00",
                "id": "garage_light_automation_2",
                "friendly_name": "Garage Lights Counter State Handler"
            },
            "last_changed": "2020-03-09T20:08:45.919139+00:00",
            "last_updated": "2020-03-09T20:11:15.062627+00:00",
            "context": {
                "id": "6aed3a96d82346c0904506c0331dc216",
                "parent_id": "4dc67bcebc074630bed88021a84277c1",
                "user_id": null
            }
        }
    },
    "origin": "LOCAL",
    "time_fired": "2020-03-09T20:11:15.062709+00:00",
    "context": {
        "id": "6aed3a96d82346c0904506c0331dc216",
        "parent_id": "4dc67bcebc074630bed88021a84277c1",
        "user_id": null
    }
}

Event 7 fired 4:11 PM:

{
    "event_type": "state_changed",
    "data": {
        "entity_id": "input_number.garage_timer_input",
        "old_state": {
            "entity_id": "input_number.garage_timer_input",
            "state": "4.0",
            "attributes": {
                "initial": -1,
                "editable": false,
                "min": -1,
                "max": 10000,
                "step": 1,
                "mode": "slider",
                "unit_of_measurement": "minutes",
                "friendly_name": "Garage Timer",
                "icon": "mdi:clock-outline"
            },
            "last_changed": "2020-03-09T20:11:15.017914+00:00",
            "last_updated": "2020-03-09T20:11:15.017914+00:00",
            "context": {
                "id": "77aa1bc4d38f4acf956628cdf19ff127",
                "parent_id": "1609617444f944449db733710284721a",
                "user_id": null
            }
        },
        "new_state": {
            "entity_id": "input_number.garage_timer_input",
            "state": "3.0",
            "attributes": {
                "initial": -1,
                "editable": false,
                "min": -1,
                "max": 10000,
                "step": 1,
                "mode": "slider",
                "unit_of_measurement": "minutes",
                "friendly_name": "Garage Timer",
                "icon": "mdi:clock-outline"
            },
            "last_changed": "2020-03-09T20:11:15.042965+00:00",
            "last_updated": "2020-03-09T20:11:15.042965+00:00",
            "context": {
                "id": "4dc67bcebc074630bed88021a84277c1",
                "parent_id": "77aa1bc4d38f4acf956628cdf19ff127",
                "user_id": null
            }
        }
    },
    "origin": "LOCAL",
    "time_fired": "2020-03-09T20:11:15.043045+00:00",
    "context": {
        "id": "4dc67bcebc074630bed88021a84277c1",
        "parent_id": "77aa1bc4d38f4acf956628cdf19ff127",
        "user_id": null
    }
}

Event 6 fired 4:11 PM:

{
    "event_type": "state_changed",
    "data": {
        "entity_id": "input_number.garage_timer_input",
        "old_state": {
            "entity_id": "input_number.garage_timer_input",
            "state": "5.0",
            "attributes": {
                "initial": -1,
                "editable": false,
                "min": -1,
                "max": 10000,
                "step": 1,
                "mode": "slider",
                "unit_of_measurement": "minutes",
                "friendly_name": "Garage Timer",
                "icon": "mdi:clock-outline"
            },
            "last_changed": "2020-03-09T20:10:12.628256+00:00",
            "last_updated": "2020-03-09T20:10:12.628256+00:00",
            "context": {
                "id": "1609617444f944449db733710284721a",
                "parent_id": "5869fcfcafff406b900e899d59baf3fe",
                "user_id": null
            }
        },
        "new_state": {
            "entity_id": "input_number.garage_timer_input",
            "state": "4.0",
            "attributes": {
                "initial": -1,
                "editable": false,
                "min": -1,
                "max": 10000,
                "step": 1,
                "mode": "slider",
                "unit_of_measurement": "minutes",
                "friendly_name": "Garage Timer",
                "icon": "mdi:clock-outline"
            },
            "last_changed": "2020-03-09T20:11:15.017914+00:00",
            "last_updated": "2020-03-09T20:11:15.017914+00:00",
            "context": {
                "id": "77aa1bc4d38f4acf956628cdf19ff127",
                "parent_id": "1609617444f944449db733710284721a",
                "user_id": null
            }
        }
    },
    "origin": "LOCAL",
    "time_fired": "2020-03-09T20:11:15.017994+00:00",
    "context": {
        "id": "77aa1bc4d38f4acf956628cdf19ff127",
        "parent_id": "1609617444f944449db733710284721a",
        "user_id": null
    }
}

Event 3 fired 4:10 PM:

{
    "event_type": "state_changed",
    "data": {
        "entity_id": "automation.garage_lights_door_event_handler",
        "old_state": {
            "entity_id": "automation.garage_lights_door_event_handler",
            "state": "on",
            "attributes": {
                "last_triggered": "2020-03-09T20:01:04.209209+00:00",
                "id": "garage_light_automation_1",
                "friendly_name": "Garage Lights Door Event Handler"
            },
            "last_changed": "2020-03-09T20:08:45.891661+00:00",
            "last_updated": "2020-03-09T20:08:45.891661+00:00",
            "context": {
                "id": "c2c0a769d1bf4e84b9776529d6d9dc7a",
                "parent_id": null,
                "user_id": null
            }
        },
        "new_state": {
            "entity_id": "automation.garage_lights_door_event_handler",
            "state": "on",
            "attributes": {
                "last_triggered": "2020-03-09T20:10:12.659551+00:00",
                "id": "garage_light_automation_1",
                "friendly_name": "Garage Lights Door Event Handler"
            },
            "last_changed": "2020-03-09T20:08:45.891661+00:00",
            "last_updated": "2020-03-09T20:10:12.660096+00:00",
            "context": {
                "id": "1609617444f944449db733710284721a",
                "parent_id": "5869fcfcafff406b900e899d59baf3fe",
                "user_id": null
            }
        }
    },
    "origin": "LOCAL",
    "time_fired": "2020-03-09T20:10:12.660176+00:00",
    "context": {
        "id": "1609617444f944449db733710284721a",
        "parent_id": "5869fcfcafff406b900e899d59baf3fe",
        "user_id": null
    }
}

Event 2 fired 4:10 PM:

{
    "event_type": "state_changed",
    "data": {
        "entity_id": "automation.garage_lights_counter_state_handler",
        "old_state": {
            "entity_id": "automation.garage_lights_counter_state_handler",
            "state": "on",
            "attributes": {
                "last_triggered": "2020-03-09T20:04:17.152427+00:00",
                "id": "garage_light_automation_2",
                "friendly_name": "Garage Lights Counter State Handler"
            },
            "last_changed": "2020-03-09T20:08:45.919139+00:00",
            "last_updated": "2020-03-09T20:08:45.919139+00:00",
            "context": {
                "id": "2f9cbb56d97646aca75b9e6805632f50",
                "parent_id": null,
                "user_id": null
            }
        },
        "new_state": {
            "entity_id": "automation.garage_lights_counter_state_handler",
            "state": "on",
            "attributes": {
                "last_triggered": "2020-03-09T20:10:12.641937+00:00",
                "id": "garage_light_automation_2",
                "friendly_name": "Garage Lights Counter State Handler"
            },
            "last_changed": "2020-03-09T20:08:45.919139+00:00",
            "last_updated": "2020-03-09T20:10:12.642501+00:00",
            "context": {
                "id": "77aa1bc4d38f4acf956628cdf19ff127",
                "parent_id": "1609617444f944449db733710284721a",
                "user_id": null
            }
        }
    },
    "origin": "LOCAL",
    "time_fired": "2020-03-09T20:10:12.642578+00:00",
    "context": {
        "id": "77aa1bc4d38f4acf956628cdf19ff127",
        "parent_id": "1609617444f944449db733710284721a",
        "user_id": null
    }
}

Event 1 fired 4:10 PM:

{
    "event_type": "state_changed",
    "data": {
        "entity_id": "input_number.garage_timer_input",
        "old_state": {
            "entity_id": "input_number.garage_timer_input",
            "state": "-1.0",
            "attributes": {
                "initial": -1,
                "editable": false,
                "min": -1,
                "max": 10000,
                "step": 1,
                "mode": "slider",
                "unit_of_measurement": "minutes",
                "friendly_name": "Garage Timer",
                "icon": "mdi:clock-outline"
            },
            "last_changed": "2020-03-09T20:04:17.076674+00:00",
            "last_updated": "2020-03-09T20:04:17.076674+00:00",
            "context": {
                "id": "360463d62db146d1afc2a125128974e1",
                "parent_id": "2a0240a56056459e910cc4e214fbbecf",
                "user_id": null
            }
        },
        "new_state": {
            "entity_id": "input_number.garage_timer_input",
            "state": "5.0",
            "attributes": {
                "initial": -1,
                "editable": false,
                "min": -1,
                "max": 10000,
                "step": 1,
                "mode": "slider",
                "unit_of_measurement": "minutes",
                "friendly_name": "Garage Timer",
                "icon": "mdi:clock-outline"
            },
            "last_changed": "2020-03-09T20:10:12.628256+00:00",
            "last_updated": "2020-03-09T20:10:12.628256+00:00",
            "context": {
                "id": "1609617444f944449db733710284721a",
                "parent_id": "5869fcfcafff406b900e899d59baf3fe",
                "user_id": null
            }
        }
    },
    "origin": "LOCAL",
    "time_fired": "2020-03-09T20:10:12.628349+00:00",
    "context": {
        "id": "1609617444f944449db733710284721a",
        "parent_id": "5869fcfcafff406b900e899d59baf3fe",
        "user_id": null
    }
}


This collection of automations reminds me of Tediore’s Rube Goldberg machine.

My first impression is: Why is this so Byzantine? However, that’s a different discussion.

Automation_2 is triggered whenever input_number.garage_timer_input changes state. At the very end of this automation, it decrements the input_number thereby changing its state which causes Automation_2 to be triggered (i.e. the automation calls itself).

Why you’ve designed it to do that is unclear to me. I also don’t understand how that wait_template works. It’s waiting for the input_number’s value to no longer be equal to what it was when it triggered the automation? If nothing else is decreasing the input_number’s value, wouldn’t that wait_template simply wait until its 1-minute expiration? If something else is decreasing it then that will trigger Automation_2 all over again.


EDIT
If you want something to trigger according to a fixed interval (but with a fixed number of intervals and not infinitely), use a timer and not a recursive automation.

1 Like

From my original thread that I linked, the goal was to make an interactive light timer with actionable responses from the iOS companion app.

Automation_1: manages door events
Automation_2: updates iOS notification and light state every 60 seconds
Automation_3: handles actionable notification events

Originally I had been looking for a way to create relatively accurate timing intervals (instead of wall-clock timing) in a for loop. I had thought python_scripts would be the way of doing this but it doesn’t appear to support async wait so there are performance concerns. At that point the wait_template with timeout parameter was brought to my attention as a preferred route for HA. This lead to implementing the “for” loop as a self triggered automation on a counter event.

I do like the timer integration. Do you know if there is a way to fire a periodic action from this timer to update the iOS companion notification or would I just need to keep restarting the timer? Ultimately I may try a custom_component based on timer to make this more reusable in my installation.

I have this working now by moving the wait to a script. My question now is, what is the underlying mechanism that was resulting in this automation firing twice?

Beats me. I’ve untangled some twisted code but this one was my limit.

Timers don’t fire an event at each clock-tick.

Event Description
timer.cancelled Fired when a timer has been canceled
timer.finished Fired when a timer has completed
timer.started Fired when a timer has been started
timer.restarted Fired when a timer has been restarted
timer.paused Fired when a timer has been paused

You’d have to use the finished event to trigger something and then (if needed) start the timer again.

If you need to be notified repeatedly, you may want to explore the Alert integration. In fact, its very first example is notifications for a Garage door.

Just to be clear, I don’t think that the re-etry is an issue with my automation but rather, a way that automations appear to be implemented. Maybe a bug, I’ll open a issue.

Can you take the time to explain how that wait_template works now that you’ve moved it to a script? I don’t see how the template’s test is ever satisfied other than it runs to expiration.

Certainly!

To start with I was able to make a script that just runs the wait and increment:

light_timer_delay:
  description: Timed delay to decrement count
  fields:
    counter_id:
      description: entity_id of counter to modify
    expected_state:
      description: present state of counter_id
  sequence:
    # wait one minute to decrement counter unless iOS user changes count
    - wait_template: "{{ states(counter_id) != expected_state }}"
      timeout: '00:01:00'
    - service: input_number.decrement
      data_template:
        entity_id: "{{ counter_id }}"

With this I am able to update automation 2 as follows:

- id: garage_light_automation_2
  alias: Garage Lights Counter State Handler
  description: Decrement garage_timer_input and take actions
  trigger: 
    - platform: state
      entity_id: input_number.garage_timer_input
  action:
    - delay: '00:00:01'
    # a value of '-1' indicates that the timer has been disabled by iOS app 
    - condition: numeric_state
      entity_id: input_number.garage_timer_input
      above: -1
    # a count of '0' means we turn off, otherwise stay on
    - service_template: >
        {% if states('input_number.garage_timer_input')|int == 0 %}
          light.turn_off
        {% else %}
          light.turn_on
        {% endif %}
      entity_id: light.frst_garage_interior
    # Nothing further to do if the count is 0
    - condition: numeric_state
      entity_id: input_number.garage_timer_input
      above: 0
    # update iOS app notification (apns-collapse-id: garge-interior-light)
    - service: notify.mobile_app_devans_iphone
      data_template:
        title: Garage Timer
        message: Garage lights will turn off in {{ trigger.to_state.state|int }} minutes.
        data:
          apns_headers:
            apns-collapse-id: garge-interior-light
          push:
            category: gargetimer
          action_data:
            entity_id: input_number.garage_timer_input
    # call script to wait for timer change (ie disabled) or decrement on interval
    - service: script.light_timer_delay
      data_template:
        counter_id: input_number.garage_timer_input
        expected_state: "{{ trigger.to_state.state }}"

This seems to be running as expected. I still want to change it to a custom component (similar to alert) only with a config flow to specify trigger device, actionable device and notification service. Doubtful that time comes in the near future though.

Regards,
Devan