Multiple triggers in a time window

What happens in this case if the router registers the device has arrived ‘home’ only after the garage door has been opened?

Then it does what the OP’s requirements state: nothing. :wink:

Not sure the sequence is set in stone that way - we’ll let the OP decide :hushed:

Thanks for the solution, much more simple and elegant than mine - never used the ‘wait’ function before but it’s definitely a good thing to have in one’s back pocket :+1:

1 Like

But seriously … you may be right. They could be interpreted that the door should be unlocked if the events happen in either order, as long as they happen within one minute of each other. I suppose to support that my above automation could be basically duplicated, but the roles switched. So, the above plus:

  - alias: Unlock door if come home within 1 min of opening garage door
    trigger:
      platform: state
      entity_id: GARAGE_DOOR_ENTITY_ID
      to: 'on'
    action:
      - wait_template: "{{ is_state('DEVICE_TRACKER_ENTITY_ID', 'home') }}"
        timeout: '00:01:00'
        continue_on_timeout: false
      - service: lock.unlock
        entity_id: LOCK_ENTITY_ID

But now that I think about it more, these don’t handle the cases where, e.g., the garage door was already open (for some time) when the device comes home, and vice versa. So let’s try this instead:

automation:
  - alias: Unlock door if garage opened within 1 min after coming home
    trigger:
      platform: state
      entity_id: DEVICE_TRACKER_ENTITY_ID
      to: 'home'
    condition:
      condition: template
      value_template: "{{ not is_state('GARAGE_DOOR_ENTITY_ID', 'on') }}"
    action:
      - wait_template: "{{ is_state('GARAGE_DOOR_ENTITY_ID', 'on') }}"
        timeout: '00:01:00'
        continue_on_timeout: false
      - service: lock.unlock
        entity_id: LOCK_ENTITY_ID
  - alias: Unlock door if come home within 1 min after opening garage door
    trigger:
      platform: state
      entity_id: GARAGE_DOOR_ENTITY_ID
      to: 'on'
    condition:
      condition: template
      value_template: "{{ not is_state('DEVICE_TRACKER_ENTITY_ID', 'home') }}"
    action:
      - wait_template: "{{ is_state('DEVICE_TRACKER_ENTITY_ID', 'home') }}"
        timeout: '00:01:00'
        continue_on_timeout: false
      - service: lock.unlock
        entity_id: LOCK_ENTITY_ID
1 Like

I use a condition to if the mud room door has been open in the last 4 mins meaning we are leaving the house instead of coming.

          - condition: template
            value_template: '{% if states.binary_sensor.mud_room_door.last_changed  %}{{(as_timestamp(now())-as_timestamp(states.binary_sensor.mud_room_door.last_changed)) > 360 }}{% else %}True{% endif %}'

There also seems to be an option to put this into the trigger directly - very interesting approach here:

Might be possible to do this by looking at the attributes for when the states were last changed.

Cool, a bunch of things to try out! I never heard of the wait_template.

To answer a previous question, the order of the events is not important, just that they happen in a window of time. Some times the door opens before I connect to router, other times its the router first, but both are valid reasons to unlock the internal door.

Here is the wait template docs.

1 Like

FYI, there is a slight chance that my solution won’t work if the transition to home and the door opening happen very close to each other. This is basically a race condition. The more I think about it, I think a better approach is to use one automation with a template trigger that requires both presence to be home and the garage door to be open, and that they both changed within the last minute. Not only would this be simpler, I’m pretty sure it will handle the case of both changing very close together. I’ll give it some more thought, but this is what I’m thinking:

automation:
  - alias: Unlock door when coming home and garage opens within a minute of each other
    trigger:
      platform: template
      value_template: >
        {{ is_state('DEVICE_TRACKER_ENTITY_ID', 'home') and
           is_state('GARAGE_DOOR_ENTITY_ID', 'on') and
           (now() - states.DEVICE_TRACKER_ENTITY_ID.last_changed).total_seconds() < 60 and
           (now() - states.GARAGE_DOOR_ENTITY_ID.last_changed).total_seconds() < 60 }}
    action:
      service: lock.unlock
      entity_id: LOCK_ENTITY_ID

The way this works is, the template will be evaluated when either of the entities change. It will be true if you’re home and the garage door is open, and they both changed to their current states within the last minute.

So, e.g., if you’re not home or the garage door is closed, then it will evaluate to False and the action won’t run. Also if you come home and the garage door is open, but it was opened more than a minute ago, the action also won’t run. Same if the garage opens and you’re home, but you’ve been home for more than a minute, the action also won’t run. But if you come home and the garage opens within a minute of each other, then when which happens last happens, the action will run to unlock the door.

3 Likes

@pnbruckner, genius!

Wait until you try it! I wouldn’t be too surprised if I’ve overlooked something. lol!!

One issue might be how this reacts at startup. Generally one of the entities in the template has to have a state change (either the state itself, or an attribute) for the trigger to “wake up” and be evaluated. But entities can act “funny” during startup. So it may be possible that one of the entities has a state change (e.g., an attribute) at startup which causes the trigger to be evaluated, and if this happens within a minute of startup, it’s possible this could unlock the door. If that does happen I can think of two solutions. 1) Set the initial state of the automation to off, which will prevent it from running after startup. Then have an automation that runs a few minutes after startup that turns this automation on. 2) Add a condition that HA’s “up-time” is more than a few minutes. I believe there is even an up-time sensor you can use for that. I think I’ve seen other people solve similar problems this way.

Bottom line - it needs testing! :slight_smile:

Ok, I implemented it so I will let you know how it goes :slight_smile:

I didnt receive any unlocks at startup so thats good.

1 Like

Promising template. I have a minor annoyance dealing with Lights turning on with motion and then off with no motion. Lights turn on with motion, then off when there has been no motion for 10 seconds. Works pretty well, but occasionally, I’m still and the lights turn off. Even that is not a huge deal, that’s what its supposed to do. :slight_smile:
But as soon as the lights go off, you move, then the binary sensor is already “on” before the automation finsihes, so the trigger “to: on” of course fails, so you stand there in a dark room for a few seconds until the sensor goes back to Off then move then start over. Usually I just end up telling Alexa to turn on the lights.

Again, not that common, but it happens. I just saw this and I think it should work using similiar logic from above.

- alias: Motion Lighting in the Kitchen
  trigger:
  - platform: state
    entity_id: binary_sensor.motion_sensor_kitchen_motion
    to: 'on'
  - platform: template
    value_template: >
      {{  is_state('binary_sensor.motion_sensor_kitchen_motion', 'on') and
         (now() - states.binary_sensor.motion_sensor_kitchen_motion.last_changed).total_seconds() < 20 }}
  condition:
  - condition: state
    entity_id: group.kitchen_zwave_lights
    state: 'off'
  - condition: numeric_state
    entity_id: sun.sun
    value_template: '{{ state.attributes.elevation }}'
    below: 3.0
  action:
  - service: homeassistant.turn_on
    entity_id: group.kitchen_zwave_lights
  - wait_template: "{{ is_state('binary_sensor.motion_delay_kitchen', 'off') }}"
  - condition: state
    entity_id: group.kitchen_zwave_lights
    state: 'on'
  - service: homeassistant.turn_off
    entity_id: group.kitchen_zwave_lights

The wait delay template is 10 seconds. I think I could get rid of the 1st trigger, right? That is the initial trigger.

Ah interesting, I always used two automation, one of on and one for off so never ran into this issue.

I wanted my off to be quick, and based on motion vs a time limit, which is what mostly found. My motion sensors have a quick on/off state (like 30 seconds), which I like. If I turned off as soon as motion stopped, I had to move around at least a little bit, so I added a short delay to keep the lights from turning on/off/on/off, but that ended up causing the issue described above. It’s like whack-a-mole. haha. Its actually about 90% good, and the 10% is not terrible, its not dark in the kitchen even with the lights off, but if I could get this 100%, I’d be pretty happy. :slight_smile:

So, with these two triggers:

  trigger:
  - platform: state
    entity_id: binary_sensor.motion_sensor_kitchen_motion
    to: 'on'
  - platform: template
    value_template: >
      {{  is_state('binary_sensor.motion_sensor_kitchen_motion', 'on') and
         (now() - states.binary_sensor.motion_sensor_kitchen_motion.last_changed).total_seconds() < 20 }}

the second one is (probably) useless.

Note that a template trigger will only fire when the template evaluates to True, and (normally) after it had been evaluated as False (although that’s not necessary for the first time it is evaluated after startup.) And it will only be evaluated when one of the referenced entities causes a state_changed event, which in this case is only when binary_sensor.motion_sensor_kitchen_motion’s state, or one of its attributes, change. And if it has no attributes that change (I don’t know, you don’t say), then only when its state (i.e., ‘on’ vs ‘off’) changes.

So, basically, the second trigger will only fire when binary_sensor.motion_sensor_kitchen_motion changes from something other than ‘on’ (probably just ‘off’) to ‘on’, which is exactly what the first trigger does. Also, the second part of the template is useless because it will always be less than 20 seconds since the state change when the state changes.

So, unless binary_sensor.motion_sensor_kitchen_motion has other attributes that change, the second trigger is effectively the same as the first trigger.

My lights off automation is based on motion and time. Basically I wait for the sensor to be off for a period of time:

- alias: Front motion - lights off
  trigger:
    platform: state
    entity_id: binary_sensor.front_motion
    to: 'off'
    for:
      minutes: 5

I was thinking I would only use the second trigger ( I left it for comparison). I do realize though that what I’m trying to do with that trigger is more of a conditon. :frowning:

So, when my automation completes (If I was still in the room, but still enough for this to happen)

wait_template: "{{ is_state('binary_sensor.motion_delay_kitchen', 'off') }}"

I then move while the automation is probably still in progress turning off the lights. The binary_sensor.motion_sensor_kitchen_motion changes to on. But since the automation is not quite finished, it does not trigger. When the automation finishes, the motion is still on, not changing TO: on. So it does not trigger until it goes off and then back on.

I was thinking (incorrectly), that that the trigger template would be on. and would trigger if it had recently triggered.

I dont like the on/off for X minute automations (probably just being picky), I like it when the lights turn off quickly after leaving a room. Well most the time anyway. :slight_smile: I’m going to rethink this a bit. I’ll share anything i come up with.

I haven’t completely followed how your binary_sensor reacts to motion, and exactly what you’re ultimately trying to achieve, but, yes, it is problematic if an automation’s trigger fires while the actions of the automation are still running from a previous trigger. I definitely try to avoid that. So one thing I can suggest (in general, and especially if there are any waits or delays in an automation’s actions) is to move the actions to a script, and start the script from the automation. In fact, the automation should first cancel the script, then call it. So, like this:

action:
  - service: script.turn_off
    entity_id: script.MY_SCRIPT
  - service: script.MY_SCRIPT

Thanks for your help. I feel like I’m starting to wrap my head around the value template more and more. Does this look like it will work. I have two possible work locations, and I used to always work at each location on specific days, so my automation was hard coded for which day of the week, but lately It’s been more fluid, so I wanted to make a single automation and have it trigger based on an input select of work locations. The sensor changes to true when I should be ready to head out the door. That works well.

- alias: "Notify Me when its time to leave for work"
  trigger:
  - platform: template
    value_template: >
      {{ is_state('sensor.time_to_leave_for_work1', 'True') and
         is_state('input_select.work_location', 'Work1')  }}
  - platform: template
    value_template: >
      {{ is_state('sensor.time_to_leave_for_work2', 'True') and
         is_state('input_select.work_location', 'Work2')  }}
  condition:
  - condition: numeric_state
    entity_id: sensor.ha_runtime_in_minutes
    above: 1    
  - condition: state
    entity_id: binary_sensor.workday_sensor
    state: 'on'
  action:
  - service: script.audio_notify
    data_template: >-
      {% if states.input_select.work_location == 'Work1' %}
        tts_msg: "Work1d on current traffic, you have 10 minutes to leave if you want to get to the work1 by 8:30am.  Traffic is {{ states('sensor.me_traffic_density_to_work1') }} today."
      {% elif states.input_select.work_location == 'Work2' %}
        tts_msg: "Work1d on current traffic, you have 10 minutes to leave if you want to get to khaki by 8:30am.  Traffic is {{ states('sensor.me_traffic_density_to_work2') }} today."
      {% else %}
        tts_msg: "I'm not sure where you're going today"
      {% endif %}
    mplayer: "main"

I believe that should work, assuming the time_to_leave… sensors change to something other than 'True' at some reasonable point.

But, you could probably do this in one template trigger:

  platform: template
  value_template: >
    {{ is_state('sensor.time_to_leave_for_work1', 'True') and
       is_state('input_select.work_location', 'Work1')  or
       is_state('sensor.time_to_leave_for_work2', 'True') and
       is_state('input_select.work_location', 'Work2')  }}