Dynamic drive time reminders with Home Assistant

Tired of manually checking Google Maps and your calendar to figure out when to leave for your next meeting?

I recently realized that Home Assistant already has all the necessary pieces – my current geolocation, calendar events, and Waze travel time – so I put together an automation to provide real-time notifications telling me exactly when I need to head out the door.

It’s pretty simple, following these steps every five minutes:

  • Retrieve all events in the next 4 hours from my work and personal calendars
  • Eliminate all-day events, events with no location, or events I’ve already been reminded of (stored in a helper input sensor called last_notified_event_id)
  • For the remaining events, retrieve Waze travel time from my current location to the event location (using entity location versus a static address or lat/long is important because I might not be at home, affecting travel time)
  • Determine departure time (start time minus travel time minus 5 minutes of wiggle room)
  • Determine reminder time (30 minutes prior to departure time)
  • Send a notification to my phone alerting me to the upcoming event, estimated travel time, and the time I need to leave
  • Store a synthetic “event ID” (the start time and summary) in an input helper sensor, so we can make sure I’m not reminded again

Note: I’m pretty new to Home Assistant templating (though a past life as a Perl dev has hammered YAML syntax into my head irreversably) so there may be stupidity in this automation. But, it works for me (so far!)

alias: Drive time alert
description: ""
# fire every five minutes
triggers:
  - trigger: time_pattern
    minutes: /5
conditions: []
actions:
  - action: calendar.get_events
    metadata: {}
    data:
      duration:
        hours: 4
        minutes: 0
        seconds: 0
    target:
      entity_id:
        - calendar.personal_calendar
        - calendar.work_calendar
    response_variable: cal_events
  - repeat:
      for_each: "{{ cal_events.values() | map(attribute='events') | sum(start=[]) }}"
      sequence:
        - variables:
            thisEventId: "{{ repeat.item.start }} {{ repeat.item.summary }}"
        - condition: template
          value_template: |
            {{ repeat.item.location is not none and
               (repeat.item.location | string | trim != "") and
               'T' in (repeat.item.start | string) and
               states('input_text.last_notified_event_id') and
               thisEventId != states('input_text.last_notified_event_id') }}
        - data:
            origin: person.mike
            destination: "{{ repeat.item.location }}"
            region: us
          response_variable: waze_eta_result
          continue_on_error: false
          action: waze_travel_time.get_travel_times
        - variables:
            wiggle_room: "{{ 5 }}"
            waze_duration: "{{ waze_eta_result['routes'][0].duration | round(0) }}"
            start_time: "{{ repeat.item.start }}"
            start_time_friendly: "{{ as_datetime(start_time).strftime('%I:%M %p') }}"
            location: "{{ repeat.item.location }}"
            summary: "{{ repeat.item.summary }}"
            departure_time: >-
              {{ as_datetime(start_time) - timedelta(minutes=waze_duration) -
              timedelta(minutes=wiggle_room) }}
            departure_time_friendly: "{{ as_datetime(departure_time).strftime('%I:%M %p') }}"
            reminder_time: "{{ as_datetime(departure_time) - timedelta(minutes=30) }}"
        - condition: template
          value_template: |
            {{ as_datetime(reminder_time) <= now() }}
        - action: notify.mobile_app_pixel_6_mike
          metadata: {}
          data:
            data:
              ttl: 0
              priority: high
            message: >-
              Drive time to your next appointment ({{summary}}) is 
              {{waze_duration}} minutes. Depart by {{departure_time_friendly}}
              for arrival by {{start_time_friendly}}.
            title: Drive Time Alert - {{ repeat.item.summary }}
        - action: input_text.set_value
          metadata: {}
          data:
            value: "{{ thisEventId }}"
          target:
            entity_id: input_text.last_notified_event_id
mode: single

9 responses to “Dynamic drive time reminders with Home Assistant”

  1. Fantastico!!!! Grazie mille ho preso spunto per replicare con qualche mio accorgimento questa funzione!!!

    1. Prego, buon divertimento!

  2. Però funziona solo se hai un appuntamento, se ne hai più di uno continua ogni 5 minuti a notificarlo

    1. Hai più calendari? Mi sembra di aver già visto questo bug.

      1. No, se nelle 4 ore hai più di un appuntamento, ogni 5 minuti continua a notificare perchè hai messo un solo last_notified_event_id che sarà impostato sull’ultimo appuntamento notificato. Come si può risolvere?

        1. Hmm, ci darò un’occhiata quando potrò. Credo che ci sia una soluzione semplice.

          1. Grazie aspetto

          2. Questa modifica dovrebbe risolvere il bug. Dopo aver effettuato ulteriori testing, aggiornerò il post. Grazie!


            --- origAutomation 2025-12-10 14:29:45
            +++ origAutomation 2025-12-12 13:11:32
            @@ -23,12 +23,16 @@
            - variables:
            thisEventId: "{{ repeat.item.start }} {{ repeat.item.summary }}"
            - condition: template
            - value_template: |
            + value_template: >
            + {% set notified = states('input_text.notified_events') %}
            +
            + {% set notified_list = notified.split(',') if (notified not in
            + ['unknown', 'unavailable', '']) else [] %}
            +
            {{ repeat.item.location is not none and
            (repeat.item.location | string | trim != "") and
            'T' in (repeat.item.start | string) and
            - states('input_text.last_notified_event_id') and
            - thisEventId != states('input_text.last_notified_event_id') }}
            + thisEventId not in notified_list }}
            - data:
            origin: person.mike
            destination: "{{ repeat.item.location }}"
            @@ -65,8 +69,20 @@
            - action: input_text.set_value
            metadata: {}
            data:
            - value: "{{ thisEventId }}"
            + value: >-
            + {% set current = states('input_text.notified_events') %} {% set
            + current_list = current.split(',') if (current not in
            + ['unknown', 'unavailable', '']) else [] %}
            + {% set cutoff = now() - timedelta(hours=24) %} {% set ns =
            + namespace(valid_events=[]) %} {% set ns.valid_events = [] %} {%
            + for event_id in current_list %}
            + {% set event_time = event_id.split(' ')[0] %}
            + {% set parsed_time = as_datetime(event_time) %}
            + {% if parsed_time is not none and parsed_time > cutoff %}
            + {% set ns.valid_events = ns.valid_events + [event_id] %}
            + {% endif %}
            + {% endfor %} {{ (ns.valid_events + [thisEventId]) | join(',') }}
            target:
            - entity_id: input_text.last_notified_event_id
            + entity_id: input_text.notified_events
            mode: single

  3. Purtroppo non funziona, la nuova variabile notified_events continua ad accodarsi ogni volta che viene attivata l’automazione. Ci sto provando da giorni ma non sembra essere la soluzione! Help

Leave a Reply

Your email address will not be published. Required fields are marked *