Template Next Appointment Once Current is Started

Hello all,

I do consults at various properties throughout the day, and I’m trying to make a card that shows when I have to leave for my next appointment, like this:

(context for card - that’s an appointment that starts at 20:00, and finishes at 21:00 and it’s 20:59 when I made the screenshot)

Mostly this all works, but I have a couple bugs still and I was hoping someone might point me in the right direction.

My templates/project works great if there is not a current appointment. i.e. nothing on schedule “now”, but looking forward at the next appointment.

My problem is that once an appointment starts my templates still focuses on the current appointment, and I need them to look at the next appointment as soon as the “start time” has passed.

Here is my foundational template. I have others, but all they do is manipulate the event that this one grabs from the schedule.

YAML
- trigger:
    - platform: homeassistant
      event: start
    - platform: time_pattern
      hours: "/6"
    - trigger: state
      entity_id:
        - input_button.update_next_event
  action:
    - action: calendar.get_events
      target:
        entity_id: calendar.personal_calender
      data:
        duration:
          hours: 168
          minutes: 0
          seconds: 0
      response_variable: agenda
    - alias: Find upcoming events with locations
      variables:
        upcoming: "{{ agenda['calendar.personal_calender']['events'] | rejectattr('location', 'undefined') | list }}"
  sensor:
    - name: "Next Location"
      state: >
        {% if upcoming|length > 0 %}
          {{ upcoming[0].location }}
        {% endif %}
    - name: "Next Location Summary"
      state: >
        {% if upcoming|length > 0 %}
          {{ upcoming[0].summary }}
        {% endif %}
    - name: "Next Location Start"
      state: >
        {% if upcoming|length > 0 %}
          {{ upcoming[0].start }}
        {% endif %}
      device_class: timestamp

Reading the docs for calendar.get_events, I thought it is supposed to only grab events that start after now by default, however it seems to include events that are currently occurring. I may misunderstand that one.

I think I need to add something to the if of each sensor to compare the start time to “now”, but I’m not sure how to approach that and haven’t found a relatable example.

I have a related template question if I may…

  • In the above template, what does the [0] after upcoming do?

Thank you!

A follow up question…

Here is a response for:

action: calendar.get_events
data:
  duration:
    hours: 72
    minutes: 0
    seconds: 0
  start_date_time: "2024-11-25 19:00:00"
target:
  entity_id: calendar.personal_calender
calendar.personal_calender:
  events:
    - start: "2024-11-25T20:00:00-08:00"
      end: "2024-11-25T21:00:00-08:00"
      summary: work source everett
      location: WorkSource Everett, 3201 Smith Ave, Everett, WA 98201, USA
    - start: "2024-11-25T21:00:00-08:00"
      end: "2024-11-25T22:00:00-08:00"
      summary: bellingham highscchool
      location: Bellingham High School, 2020 Cornwall Ave, Bellingham, WA 98225, USA

How can I format that response so that I can play with it in the Template Tool so that it’s similar to how it’s formatted with the trigger above:? Or do I have to use an automation to test it?

response_variable: agenda
    - alias: Find upcoming events with locations
      variables:
        upcoming: "{{ agenda['calendar.personal_calender']['events'] | rejectattr('location', 'undefined') | list }}"

Part of why I’ve found this especially hard is that the foundation template is a trigger that requires restarting to tweak vs being live in the Template Tool.

You were mistaken, any currently active events will be selected as well. If you want to remove all currently active events, you can add a rejectattr filter to your template like:

    - alias: Find upcoming events with locations
      variables:
        upcoming: |
          {{ agenda['calendar.personal_calender']['events'] 
          | rejectattr('location', 'undefined') 
          | rejectattr('start', 'le', (now().replace(microsecond=0)).isoformat())
          | list }}"

It selects the first calendar event in the list of events being stored in the variable upcoming.

After running the action in the Actions tab, scroll to the bottom and click “Copy to Clipboard (Template)”. This will save a Jinja set statement to the clipboard which you can paste directly into the Template Editor tool. By default it uses the variable ID action_response, but you can change that to whatever you want in the Template Editor.

2 Likes

Thank you so much @Didgeridrew! This is very helpful not just for this project, but for my learning in general. The python link is great too.

For someone like myself with a rudimentary knowledge of programming, but an interest in learning Python at least to read and tweak scripts, is there a resource that you would suggest? (that link may be the answer I’m looking for too.)