How to list multiple calendar event in a template

your calendar entity needs to have a start and end time for it to work. That error is saying it doesn’t

Hi Petro,

I do see start and end time for the output of

{% set time = ' 00:00:00' %}
{% set start = now().date() %}
{% set end = (start + timedelta(days=1)) ~ time %}
{% set start = start ~ time %}
{{ states.calendar  | list }}
[<template TemplateState(<state calendar.family_calendar=off; offset_reached=False, friendly_name=family_calendar @ 2022-03-28T14:42:49.516789+02:00>)>, <template TemplateState(<state calendar.holidays_in_germany=off; message=Palm Sunday, all_day=True, start_time=2022-04-10 00:00:00, end_time=2022-04-11 00:00:00, location=, description=Observance
To hide observances, go to Google Calendar Settings > Holidays in Germany, offset_reached=False, friendly_name=Holidays in @ 2022-03-28T14:42:47.613284+02:00>)>, <template TemplateState(<state calendar.richard_calendar=off; message=Lunch, all_day=False, start_time=2022-03-29 13:30:00, end_time=2022-03-29 14:15:00, location=, description=, offset_reached=False, friendly_name=richard_calendar @ 2022-03-28T19:20:46.690701+02:00>)>, <template TemplateState(<state calendar.school_vacation=off; message=Vacation, all_day=False, start_time=2022-04-11 01:45:00, end_time=2022-04-11 02:00:00, location=, description=2:00, offset_reached=False, friendly_name=school_vacation @ 2022-03-28T14:42:43.547137+02:00>)>, <template TemplateState(<state calendar.sheng_ri=off; offset_reached=False, friendly_name=生日 @ 2022-03-28T14:42:45.989382+02:00>)>, <template TemplateState(<state calendar.task_important=off; message=Important: application form for Zehao and 60 Euro, all_day=False, start_time=2022-03-30 07:45:00, end_time=2022-03-30 08:00:00, location=, description=, offset_reached=False, friendly_name=task_important @ 2022-03-28T14:42:44.944210+02:00>)>, <template TemplateState(<state calendar.tv_time_calendar=off; message=TV_Time, all_day=False, start_time=2022-03-29 18:20:00, end_time=2022-03-29 18:40:00, location=, description=, offset_reached=False, friendly_name=tv_time_calendar @ 2022-03-28T18:35:46.402878+02:00>)>, <template TemplateState(<state calendar.vivian_calendar=off; message=面试, all_day=False, start_time=2022-03-29 10:00:00, end_time=2022-03-29 11:00:00, location=, description=, offset_reached=False, friendly_name=vivian_calendar @ 2022-03-28T14:42:47.164417+02:00>)>, <template TemplateState(<state calendar.weather=on; message=Forecast for 广东省深圳市 (19° | 15°), all_day=True, start_time=2022-03-28 00:00:00, end_time=2022-03-29 00:00:00, location=, description=, offset_reached=False, friendly_name=Weather @ 2022-03-28T14:42:47.998908+02:00>)>, <template TemplateState(<state calendar.zehao_calendar=off; message=School Time, all_day=False, start_time=2022-03-29 08:00:00, end_time=2022-03-29 12:40:00, location=, description=, offset_reached=False, friendly_name=zehao_calendar @ 2022-03-28T20:00:46.941931+02:00>)>, <template TemplateState(<state calendar.zherui_calendar=off; message=Sports, Sports and Sports, all_day=False, start_time=2022-04-01 07:30:00, end_time=2022-04-01 07:45:00, location=, description=, offset_reached=False, friendly_name=zherui_calendar @ 2022-03-28T14:42:49.217703+02:00>)>]

I have tried to adapt this code to work in template, but I still only get the current next entry in the calendar.
The problem I encounter is I am trying to send a notification 2hrs before a calendar event but can only read one event. so in my below example, there are 2 entries at 11am in the calendar for our cleaning company; HA is sending two notifications for the same address. Things work fine if the entries are at different times, but our calendar is fed from AirBNB for the checkin time / check out time of a guest and we have 5 properties, so there can be up to 5 entries at the same time for a cleaning task.

Any idea how I can trigger the text message for all entries in the calendar that are the same time?

alias: Notify Vicky of clean Today
description: ''
trigger:
  - platform: calendar
    event: start
    offset: '-2:0:0'
    entity_id: calendar.vicki_YYY
condition:
  - condition: template
    value_template: >-
      {{ 'cleaning' in
      states.calendar.vicki_YYY.attributes.description.lower() }}
action:
  - if:
      - condition: template
        value_template: >-
          {{ '30c' in states.calendar.vicki_YYY.attributes.location.lower()
          }}
    then:
      - service: rest_command.websms_txt
        data:
          message_to: 64271234567
          message_text: >-
            This is a friendly reminder that you have a clean at 30c XXX Dr
            today
  - if:
      - condition: template
        value_template: >-
          {{ '30d' in states.calendar.vicki_YYY.attributes.location.lower()
          }}
    then:
      - service: rest_command.websms_txt
        data:
          message_to: 64271234567
          message_text: >-
            This is a friendly reminder that you have a clean at 30d XXX Dr
            today
  - if:
      - condition: template
        value_template: >-
          {{ '132c' in states.calendar.vicki_YYY.attributes.location.lower()
          }}
    then:
      - service: rest_command.websms_txt
        data:
          message_to: 64271234567
          message_text: >-
            This is a friendly reminder that you have a clean at 132c XXX st
            today
  - if:
      - condition: template
        value_template: >-
          {{ '30e' in states.calendar.vicki_YYY.attributes.location.lower()
          }}
    then:
      - service: rest_command.websms_txt
        data:
          message_to: 64271234567
          message_text: >-
            This is a friendly reminder that you have a clean at 30e XXX Dr
            today
mode: queued

Like others, I’m trying pull up a list of google calendar entries.
My use case is that I have Tesco deliveries twice a week. The Tesco app puts the delivery into google calendar which has all my other events too. I book the slot a few weeks in advance and then populate the order the evening before the delivery is due. Yesterday I forgot I had a delivery due today so am paying £6.50 for a few raspberries (a placeholder). I want HA to look at my google calendar once a day and notify me if there is an event tomorrow with the title or message ‘Tesco order due’.
Like others, all i can see is the next event and its attributes.

1 Like

My use case is that I am interested in all events of the day from a single calendar.
And the calendar entity only contains the next event.

For example, there is a meeting at 10:00 and a presentation at 14:30.

Then I am trying to have a preview of the day by being notified everyday at 8:30 about the events of the day if only if there is any.

Currently, I am using Alexa, but it notifies me even when there isn’t any event.

There is a feature request for this functionality.

If you are also interested in getting a calender event list, please vote for this feature: Calendar - new entitites that show a list of events with a certain scope

My use case is to show a list of upcoming events on a epaper display. I already tried some ICS/ICAL custom_components, however they all seem to have huge problems with reoccuring events (e.g. birthdays events).

I’m just curious if anyone has solved this. I can only pull the next event in a given calendar, but sometimes even that doesn’t work (perhaps because it’s more than X hours away?).

Is there any way to pull a list of entities/attributes for upcoming events? Ideally in Node Red.

Office integration and calendar works

I ended up figuring out how to get the Node Red integration to work and use the ical-events integration. The hard part was getting the right URL for the “iCloud - Secure” option to work since it’s not the standard URL you get from the calendar itself.

{{ entities[0].attributes.message }}

1 Like

For anyone that comes across this in the future, here is my solution which was created based on the code provided here: Google calendar get more than one event - #11 by der-optimist

This will create and populate a new sensor, the attributes will contain all the upcoming events within X number of days and the state will be the next event date.

  1. Install the AppDaemon addon and add the packages requests, datetime and humanize required in the python section of the configuration

  2. Create a config/appdaemon/apps/calendar.py file with the following code

import appdaemon.plugins.hass.hassapi as hass
from requests import get
import json
import datetime
import humanize

class calendar(hass.Hass):

    def initialize(self):
        # --- define variables ---
        self.ha_url = self.args["url"]
        self.token = self.args["token"]
        self.calendar_name = self.args["calendar"]
        self.days_to_display = self.args["days_to_display"]
        self.sensor_name = self.args["sensor_name"]

        self.run_hourly(self.check_calendar_events, datetime.time(hour=0, minute=1, second=0))

        # --- do all the stuff at restarts ---
        self.listen_event(self.startup, "plugin_started")
        self.listen_event(self.startup, "appd_started")

        # --- initialize ---
        self.check_calendar_events(None)
        
    def startup(self, event_name, data, kwargs):
        self.log("Startup detected")
        self.check_calendar_events(None)
      
    def check_calendar_events(self, kwargs):
        utc_offset = self.utc_offset(None)
        start_dt = (datetime.datetime.now() - utc_offset).strftime("%Y-%m-%dT%H:%M:%S") # results in UTC time => "Z" in url
        end_dt = (datetime.datetime.now() + datetime.timedelta(days=self.days_to_display) - utc_offset).strftime("%Y-%m-%dT%H:%M:%S") # results in UTC time => "Z" in url

        _list = self.load_calendar(self.calendar_name,start_dt,end_dt)
        # self.log(_list)

        if _list == "error":
            self.log("received http error - will retry later")
            self.run_in(self.check_calendar_events, 600)
        else:
            _nextEvent:datetime = None
            _events = []

            for element in _list:

                _summary = ""
                _start_date = ""
                _end_date = ""
                _all_day_event = "dateTime" not in element["start"]

                if "summary" not in element:
                    self.log("No summary in event, ignore")
                    continue

                _summary = element["summary"]

                if(_all_day_event):
                    _start = datetime.datetime.strptime(element["start"]["date"], "%Y-%m-%d")
                    _end = datetime.datetime.strptime(element["end"]["date"], "%Y-%m-%d")
                    _start_date = datetime.datetime.combine(_start, datetime.datetime.min.time()).astimezone()
                    _end_date = datetime.datetime.combine(_end, datetime.datetime.min.time()).astimezone()
                else: 
                    _start_date = datetime.datetime.strptime(element["start"]["dateTime"],"%Y-%m-%dT%H:%M:%S%z")
                    _end_date = datetime.datetime.strptime(element["end"]["dateTime"], "%Y-%m-%dT%H:%M:%S%z")

                _diff = _end_date - _start_date
                _duration = humanize.precisedelta(_diff)
                friendly_start = humanize.naturalday(_start_date)

                _events.append({
                    "summary": _summary,
                    "start_date": _start_date,
                    "end_date": _end_date,
                    "all_day_event": _all_day_event,
                    "duration": _duration,
                    "friendly_start": friendly_start,
                })

                if _nextEvent is None:
                    _nextEvent = _start_date
                else:
                    if _start_date < _nextEvent:
                        _nextEvent = _start_date
                
            # self.log(_events)

            self.set_state(self.sensor_name,state=_nextEvent,attributes= {"events" : _events})

         
    def load_calendar(self,calendar,start_dt,end_dt):
        headers = {'Authorization': "Bearer {}".format(self.token)}
        # self.log("Try to load calendar events")
        apiurl = "{}/api/calendars/{}?start={}Z&end={}Z".format(self.ha_url,calendar,start_dt,end_dt)
        # self.log("ha_config: url is {}".format(apiurl))
        try:

            r = get(apiurl, headers=headers, verify=False, timeout=10)
        except:
            self.log("Error while loading calendar {}. Maybe connection problem".format(calendar))
            return "error"
        # self.log(r)
        # self.log(r.text)

        if r.status_code == 200:
            if "summary" in r.text:
                resp = json.loads(r.text) # List
            else:
                resp = []
        else:
            self.log("loading calendar {} failed. http error {}".format(calendar,r.status_code))
            resp = "error"
        return resp        

    def utc_offset(self, kwargs):
        now_utc_naive = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")
        now_loc_naive = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
        utc_offset_dt = datetime.datetime.strptime(now_loc_naive, "%Y-%m-%dT%H:%M:%S") - datetime.datetime.strptime(now_utc_naive, "%Y-%m-%dT%H:%M:%S")
        #self.log("utc offset: {}d {}sec".format(utc_offset_dt.days, utc_offset_dt.seconds))
        return utc_offset_dt

 
  1. Update config/appdaemon/apps/apps.yaml and add the following code
calendar:
  module: calendar
  class: calendar
  token: !secret calendar_token
  url: http://192.168.1.3:8123
  sensor_name: 'sensor.calendar_events'
  calendar: 'calendar.family'
  days_to_display: 60
1 Like

For future reference, you can now get a list of upcoming events using calendar.list_events.

This new service call was added in the 2023.7.0 release (Services can now respond) and is one of the first that’s able to provide a response.

3 Likes

Thanks Tara, would you know how to call this service (calendar.list.events)i n a Jinja template to use for a TTS announcement?

Sure Gar, here’s the example from the link I posted above.

service: calendar.list_events
target:
  entity_id: calendar.school
data:
  duration:
    hours: 24
response_variable: agenda

The variable agenda will contain a list of all events scheduled in the next 24 hours. That’s just one way to use the service call. The documentation explains the service call’s options.

Sorry for my ignorance, yep understand that however I am trying to construct a script using macros, I am trying to construct a TTS response using a number of macros, I have worked out 3/4 of it, however the service call to agenda and retrieving the info eludes me.
Macros for “greetings”, “weather”, hopefully “Calanda 1 agenda”, “Calanda 2 agenda” and so on here is an example - see the ??? needs filling :slight_smile:

{%- macro getGreeting() -%}
{% if now().strftime(’%H’)|int < 12 %}
Good morning.
{% elif now().strftime(’%H’)|int >= 12 and now().strftime(’%H’)|int < 17 %}
Good afternoon.
{% else %}
Good evening.
{% endif %}

{% if is_state(‘binary_sensor.morning’,‘on’) %}
Today is {{ states.sensor.date_and_time.state }},
{% else %}
It is {{ now().strftime("%I:%M %p") }}.
{% endif %}

{%- endmacro -%}

{%- macro getWeatherToday() -%}
the Temperature in the living area is currently
{{states(‘sensor.dining_room_fan_temperature’)}} degrees, the Outside
Temperature is currently {{states(‘sensor.gw1100c_v2_1_3_temperature_1’)}}
degrees, We are expecting a high of
{{states(‘sensor.parkerville_temp_max_0’)}} degrees, with a low of
{{states(‘sensor.parkerville_temp_min_0’)}} degrees, the chance of it
raining today is around {{states(‘sensor.parkerville_rain_chance_0’)}}
percent.
{%- endmacro -%}

{%- macro getGarysAgendaToday() -%}
Garys agenda for today is Today is ???
{%- endmacro -%}

Thanks for sharing this!

I’m trying to read google calendar multiple ‘all day’ events and want automations on them (to use as birthday reminders).

I followed your scripts, but sensor.calendar_events isn’t being shown.

What did I do:

  1. Install AppDaemon via hassio/dashboard Add-ons.
  2. SSH’d to config > appdaemon and added these lines to appdaemon.yaml:
python_packages:
  - requests
  - datetime
  - humanize
  1. I went into the apps folder and edited apps.yaml with the following:
calendar:
  module: calendar
  class: calendar
  token: !secret calendar_token
  url: http://<myinternalIP>:8123
  sensor_name: 'sensor.calendar_events'
  calendar: 'calendar.verjaardagen_2'
  days_to_display: 60

Question: Does this need to be the internal IP or my external (I’ve set a subdomain on my own domain to enter HA).

  1. Created calendar.py with your code.

What am I doing wrong?

Does the appdaemon log show anything useful, eg. The HomeAssistant API failing?

The URL is used to make a call to the API to get the calendar events, the API may not be accessible externally so I use the internal url

Thanks for the quick reply!

I’m can visit the AppDaemon. I don’t see any logs (no data available) on main_log, error_log. Also ‘Dashboards’ doesn’t show ‘calendar’.

The AppDaemon Dashboard doesn’t show the calendar for me either. main_log only seems to show something for me when I have the page open and then save the calendar.py file, then the logs will appear.

Also the Token should be a long-lived access token, this can be created in the profile section of HA.

My appdaemon.yaml does not contain python_packages, I added my packages in the front end.

Ok thanks. So I need to do something with the access token?

How can I check if the python packages are installed correctly? Or if the calendar.py is even loaded by appDaemon? I don’t see it when viewing State > Apps …