Is webhooks the right answer for multi-garage door automation?

Wanted to add for review as well as logic.

This weekend the door triggered an OPEN while I was sitting in the living room due to Life360 triggering an AWAY event and HOME event in the same minute.

To combat this I added the following this morning:

Original YAML:

alias: Garage - Open saved door
description: ""
triggers:
  - trigger: state
    entity_id:
      - device_tracker.life360_Person1
      - device_tracker.life360_Person2
    from: not_home
    to: home
    enabled: true
actions:
  - action: cover.open_cover
    target:
      entity_id: >-
        {{ states('input_text.door_' ~
        trigger.entity_id.removeprefix('device_tracker.life360_')) }}
  - delay:
      hours: 0
      minutes: 2
      seconds: 45
      milliseconds: 0
  - action: cover.close_cover
    target:
      entity_id: >-
        {{ states('input_text.door_' ~
        trigger.entity_id.removeprefix('device_tracker.life360_')) }}

Additional Code to Clear the previous entry after successful execution

  - action: input_text.set_value
    data:
      value: ""
    target:
      entity_id: >-
        {{ states('input_text.door_' ~
        trigger.entity_id.removeprefix('device_tracker.life360_')) }}

Then edited the setting of the creation of the Helper to wait 2:15 before setting the door.

My assumption here is that if there is an HOME->AWAY->HOME event in under 2:15 seconds, then no value will be set as the STATE wouldn’t be AWAY

alias: Garage - Save last door used
description: ""
triggers:
  - trigger: state
    entity_id:
      - device_tracker.life360_Person1
      - device_tracker.life360_Person2
    from: home
    to: not_home
    for:
      hours: 0
      minutes: 2
      seconds: 15
actions:
  - action: input_text.set_value
    target:
      entity_id: >-
        input_text.door_{{
        trigger.entity_id.removeprefix('device_tracker.life360_') }}
    data:
      value: >
        {% set doors = [
            states.cover.ratgdov25i_1be829_door,
            states.cover.ratgdov25i_1be833_door
          ]
        %} {% set last_door = (doors|sort(attribute="last_changed"))[-1] %} {{
        last_door.entity_id }}

Hmm. It didn’t clear the entry after opening. Do I need to place a space between the quotes?

Try these:

alias: Garage - Save last door used
description: ""
triggers:
  - trigger: state
    entity_id:
      - device_tracker.life360_Person1
      - device_tracker.life360_Person2
    from: home
    to: not_home
    for:
      hours: 0
      minutes: 2
      seconds: 15
variables:
  helper: >-
    input_text.door_{{
    trigger.entity_id.removeprefix('device_tracker.life360_') }}
actions:
  - action: input_text.set_value
    target:
      entity_id: "{{ helper }}"
    data:
      value: >
        {% set doors = [
            states.cover.ratgdov25i_1be829_door,
            states.cover.ratgdov25i_1be833_door
          ]
        %}
        {% set last_door = (doors|sort(attribute="last_changed"))[-1] %}
        {{ last_door.entity_id }}
alias: Garage - Open saved door
description: ""
triggers:
  - trigger: state
    entity_id:
      - device_tracker.life360_Person1
      - device_tracker.life360_Person2
    from: not_home
    to: home
variables:
  helper: >-
    input_text.door_{{
    trigger.entity_id.removeprefix('device_tracker.life360_') }}
  door: "{{ states(helper) }}"
conditions: "{{ door.startswith('cover') }}"
actions:
  - action: cover.open_cover
    target:
      entity_id: "{{ door }}"
  - delay:
      hours: 0
      minutes: 2
      seconds: 45
      milliseconds: 0
  - action: cover.close_cover
    target:
      entity_id: "{{ door }}"
  - action: input_text.set_value
    data:
      value: ""
    target:
      entity_id: "{{ helper }}"

UPDATE: Fixed incorrect indentation in last action of second automation.

Gave them a go.

Setting the door I don’t believe worked as the helper shows as being updated yesterday.

The opening of the door did work however as yesterday’s entry was in the field, but, it didn’t clear the entry after opening.

What do the traces show?

I just noticed there is a typo in my second automation – the indentation for the last action is wrong. I’ll fix it.

I was reviewing the trace and this was the error. I thought I caught that indentation error as the YAML wouldn’t save on the cut and paste.

I will reapply the above and let them run :slight_smile:

This was the error in the trace log:

Executed: December 18, 2024 at 5:42:53 PM
Error: extra keys not allowed @ data['target']
Result:
params:
  domain: input_text
  service: set_value
  service_data:
    value: ''
    target:
      entity_id: input_text.door_person2
  target: {}
running_script: false

@pnbruckner your last edits worked. It clears the entries upon use as it should. :+1:

Only issue now is when we both leave together. It triggered on my girl and opened the correct door, however, an hour later, I triggered as being home. It then ran the automation again. Opened the same door, waited, and then closed it, per the catch to close the door.

I am pondering how to clear both entries when we both leave and return together.

I have created a script to manually run when we both come home as a stop gap while i figure this out.

alias: Clear Garage Helpers
sequence:
  - action: input_text.set_value
    metadata: {}
    data:
      value: "Cleared "
    target:
      entity_id:
        - input_text.door_Person1
        - input_text.door_Person2
description: ""

I added the text “Cleared” to the entity as a marker of running the manual script.

Now RatGDO has a new sensor with a Laser, that I could swap in that will actually tell me if the vehicle is in the spot. Easy catch to add to the automation then. If car in slot, Stop.

Will see if I go that way or come up with something else …

This is awesome regardless, so happy with the added functionality

In this scenario, what would you have it do differently? Should it not open a door when you get home? Should it open a different door, and if so, which one?

Do you usually play musical chairs, or does everyone usually park in one position? If the latter, then why not just have it open a particular door for each person when they get home? And, if you have a sensor for each car position, even better. Like you said, when a person gets home, if their car’s position in the garage is not occupied, then open that door. If it is occupied, then don’t.

In the above scenerio is when we left and returned together in one car.

When we leave at different times and different cars, everything works awesome.

When we both leave in the same car is where this last issue pops up (sometimes) as it stores the correct door we left from in each of our helpers, but then upon return, it only triggers one of us and if the next persons return trigger was delayed in triggering, then it will run the automation again as the helper wasn’t cleared.

Does that make sense?

Ok. How about something like this:

alias: Garage - Save last door used
description: ""
triggers:
  - trigger: state
    entity_id:
      - device_tracker.life360_Person1
      - device_tracker.life360_Person2
    from: home
    to: not_home
    for:
      hours: 0
      minutes: 2
      seconds: 15
variables:
  helper: >-
    input_text.door_{{
    trigger.entity_id.removeprefix('device_tracker.life360_') }}
  last_door: >-
    {% set doors = [
        states.cover.ratgdov25i_1be829_door,
        states.cover.ratgdov25i_1be833_door
      ]
    %}
    {% set last_door = (doors|sort(attribute="last_changed"))[-1] %}
    {{ last_door.entity_id }}
conditions: >-
  {{
    states.input_text
    |selectattr('entity_id', 'contains', 'input_text.door_')
    |select('eq', last_door)
    |list|length == 0
  }}
actions:
  - action: input_text.set_value
    target:
      entity_id: "{{ helper }}"
    data:
      value: "{{ last_door }}"

It will only save the door for the person if that same door was not already saved for somebody else.

Taking the above example @pnbruckner how would I add the following scenario?

Only saving the “last door” used within the last XX minutes?

New Logic Issue:

Last night at 9PM ET, AWAY triggered for myself. It stored the “Last_door” as my girls from earlier, as that was the last door used, which occurred at 6PM. Then at midnight, HOME was triggered, thus opening the door.

My thought it, if we limit it to only save the last triggered door within the last 30 minutes. IF no door was used recently it would just fail and not continue, thus eliminating the issues of ghost triggering automations and grabbing whatever was lat used under an unrestricted time scale.

When i look at Life360, it shows that i have been home for 3 days, which is accurate.

Thoughts?

I think you need to work on resolving why a tracker entity is changing to not_home when it shouldn’t be. E.g., maybe your Home zone is too small.

In theory, even if a tracker reports a fairly inaccurate location reading, it should also indicate that with a larger accuracy attribute at the same time. The result should be that the tracker’s location circle (i.e., latitude/longitude at the center, with a radius defined by the accuracy attribute) still overlaps where the device really is, and if it’s in the Home zone, then the device should still be reported as home.

In practice, though, that isn’t always the case. Sometimes, for some reason, a tracker will report an inaccurate location without increasing the accuracy attribute accordingly. (If you track the entity on a map with breadcrumbs/history, you’ll see a “jump” in its location.) For situations like this, it might be best to increase the size of the Home zone to help avoid these issues.

Anyway, how about:

conditions: >-
  {{
    (now() - states[last_door].last_changed).total_seconds()
    <= 30 * 60
    and states.input_text
    |selectattr('entity_id', 'contains', 'input_text.door_')
    |select('eq', last_door)
    |list|length == 0
  }}

Thx @pnbruckner . I have been trying to understand the ghost jumps. It happens so infrequently it i shard to catch. Still diving in there. (Hence the 2 weeks between postings as it was flawless)

Just to keep everyone updated whom is following for easy reference. Here is the current complete code that is now in place for saving the last door used:

alias: Garage - Save last door used v1.12
description: ""
triggers:
  - trigger: state
    entity_id:
      - device_tracker.life360_Person1
      - device_tracker.life360_Person2
    from: home
    to: not_home
conditions:
  - condition: template
    value_template: |-
      {{
        (now() - states[last_door].last_changed).total_seconds()
        <= 30 * 60
        and states.input_text
        |selectattr('entity_id', 'contains', 'input_text.door_')
        |select('eq', last_door)
        |list|length == 0
      }}
actions:
  - action: input_text.set_value
    target:
      entity_id: "{{ helper }}"
    data:
      value: "{{ last_door }}"
variables:
  helper: >-
    input_text.door_{{ trigger.entity_id.removeprefix('device_tracker.life360_')
    }}
  last_door: >-
    {% set doors = [
        states.cover.ratgdov25i_1be829_door,
        states.cover.ratgdov25i_1be833_door
      ]
    %} {% set last_door = (doors|sort(attribute="last_changed"))[-1] %} {{
    last_door.entity_id }}

So the above wouldn’t trigger, so I moved the condition under actions (see below) and it still didnt trigger.

I am clearly screwing up the syntax.

alias: Garage - Save last door used v1.13
description: ""
triggers:
  - trigger: state
    entity_id:
      - device_tracker.life360_Person1
      - device_tracker.life360_Person2
    to: not_home
    from: home
    for:
      hours: 0
      minutes: 0
      seconds: 30
actions:
  - condition: template
    value_template: |-
      {{
        (now() - states[last_door].last_changed).total_seconds()
        <= 30 * 60
        and states.input_text
        |selectattr('entity_id', 'contains', 'input_text.door_')
        |select('eq', last_door)
        |list|length == 0
      }}
  - action: input_text.set_value
    target:
      entity_id: "{{ helper }}"
    data:
      value: "{{ last_door }}"
mode: single
variables:
  helper: >-
    input_text.door_{{ trigger.entity_id.removeprefix('device_tracker.life360_')
    }}
  last_door: >-
    {% set doors = [
        states.cover.ratgdov25i_1be829_door,
        states.cover.ratgdov25i_1be833_door
      ]
    %} {% set last_door = (doors|sort(attribute="last_changed"))[-1] %} {{
    last_door.entity_id }}

You shouldn’t need to move the condition under actions.

What does the trace show? Do the variables evaluate to the expected values?

The only part of the template I wasn’t 100% sure about was:

states[last_door]

But I tested it in the Template editor in the dev tools and it worked ok for me.

What do you get if you put the following in the Template editor?

{% set last_door = 'cover.ratgdov25i_1be829_door' %}
{{
  (now() - states[last_door].last_changed).total_seconds()
  <= 30 * 60
}}

Does it evaluate to true or false, or report an error? If it evaluates to true or false, does it change to the opposite if you change <= to > in the template?

I dumped this into the template to see output:


  {% set doors = [
            states.cover.ratgdov25i_1be829_door,
            states.cover.ratgdov25i_1be83c_door
          ]
        %} {% set last_door = (doors|sort(attribute="last_changed"))[-1] %} {{
        last_door.entity_id }}

        {% set last_door = 'cover.ratgdov25i_1be829_door' %}
{{
  (now() - states[last_door].last_changed).total_seconds()
  <= 30 * 60
}}```


Result
cover.ratgdov25i_1be83c_door

False

So interesting, Looking at what is in the template editor from earlier:

{% set doors = [
  states.cover.ratgdov25i_1be829_door,
  states.cover.ratgdov25i_1be83c_door
]
%}
{% set last_door = (doors|sort(attribute="last_changed"))[-1] %}
ratgdov25i_1be829_door: {{ states.cover.ratgdov25i_1be829_door.last_changed|as_local }}
ratgdov25i_1be83c_door: {{ states.cover.ratgdov25i_1be83c_door_door.last_changed|as_local }}

last changed: {{ last_door.entity_id }}

It actually doesn’t return anything


# Result

'None' has no attribute 'last_changed'

Not sure why you have two definitions of last_door in the template editor at the same time.

Anyway, at least it shows the statement that determines if the last door changed in the last 30 minutes works.

I think the problem is:

ratgdov25i_1be83c_door: {{ states.cover.ratgdov25i_1be83c_door_door.last_changed|as_local }}

Do you have an entity whose ID is cover.ratgdov25i_1be83c_door_door?