Automation Calendar conditions

So… I’m working on my first real automation. Basically, I want it to run at 3 different times (3 triggers), and depending on what time the trigger runs it should (choose action) play one of a few messages on all our speakers.

I had this setup in Google Assistant without the calendar conditions. That’s why I wanted to move it into HA. (and I wanted to start using HA for stuff because it’s a LOT more powerful)

However, I needed to use conditions because I only want it to run if the School calendar has a SchoolDay event AND if there isn’t a No School event in our Family calendar. (my wife isn’t going to use my HA calendar so I had to look at our google calendar)

The issue with the condition has to do with the now.day() state for SchoolDay. I had to add it in because even though the calendar event didn’t start until Monday, the alert went off on Sunday because it was the next event on the calendar I think. So I’m trying to test if the date on the calendar is the same as today. But I don’t think that is the right way to do it. (because it didn’t go off today lol)

Here is the YAML of what I created. I used the GUI to create the automation because I’m still learning YAML.

Here is the condition statement:

condition:
  - condition: and
    conditions:
      - condition: state
        entity_id: calendar.school_calendar
        attribute: message
        state: SchoolDay
      - condition: state
        entity_id: calendar.school_calendar
        attribute: start_time
        state: now.day()
      - condition: not
        conditions:
          - condition: state
            entity_id: calendar.family
            attribute: message
            state: No school

And here is the whole automation.

alias: School Routine
description: ""
trigger:
  - platform: time
    at: "07:00:00"
    id: "1"
  - platform: time
    at: "07:30:00"
    id: "2"
  - platform: time
    at: "08:10:00"
    id: "3"
condition:
  - condition: and
    conditions:
      - condition: state
        entity_id: calendar.school_calendar
        attribute: message
        state: SchoolDay
      - condition: state
        entity_id: calendar.school_calendar
        attribute: start_time
        state: now.day()
      - condition: not
        conditions:
          - condition: state
            entity_id: calendar.family
            attribute: message
            state: No school
action:
  - choose:
      - conditions:
          - condition: trigger
            id: "1"
        sequence:
          - service: media_player.play_media
            target:
              entity_id: media_player.all_google_speakers
            data:
              media_content_id: >-
                media-source://tts/cloud?message=Lylah%2C+you+should+be+ready+to+leave+for+school.+Pierce%2C+you+should+go+eat+breakfast.&language=en-IE&gender=female
              media_content_type: provider
            metadata:
              title: >-
                Lylah, you should be ready to leave for school. Pierce, you
                should go eat breakfast.
              thumbnail: https://brands.home-assistant.io/_/cloud/logo.png
              media_class: app
              children_media_class: null
              navigateIds:
                - {}
                - media_content_type: app
                  media_content_id: media-source://tts
                - media_content_type: provider
                  media_content_id: >-
                    media-source://tts/cloud?message=Lylah%2C+you+should+be+ready+to+leave+for+school.+Pierce%2C+you+should+go+eat+breakfast.&language=en-IE&gender=female
      - conditions:
          - condition: trigger
            id: "2"
        sequence:
          - service: media_player.play_media
            target:
              entity_id: media_player.all_google_speakers
            data:
              media_content_id: >-
                media-source://tts/cloud?message=Pierce%2C+it%27s+time+to+get+ready+for+school.+Don%27t+forget+to+brush+your+teeth.&language=en-IE&gender=female
              media_content_type: provider
            metadata:
              title: >-
                Pierce, it's time to get ready for school. Don't forget to brush
                your teeth.
              thumbnail: https://brands.home-assistant.io/_/cloud/logo.png
              media_class: app
              children_media_class: null
              navigateIds:
                - {}
                - media_content_type: app
                  media_content_id: media-source://tts
                - media_content_type: provider
                  media_content_id: >-
                    media-source://tts/cloud?message=Pierce%2C+it%27s+time+to+get+ready+for+school.+Don%27t+forget+to+brush+your+teeth.&language=en-IE&gender=female
      - conditions:
          - condition: trigger
            id: "3"
        sequence:
          - service: media_player.play_media
            target:
              entity_id: media_player.all_google_speakers
            data:
              media_content_id: >-
                media-source://tts/cloud?message=Pierce%2C+it%27s+time+to+leave+for+school.+Get+your+lunch%2C+water+bottle%2C+and+brush+your+teeth.&language=en-IE&gender=female
              media_content_type: provider
            metadata:
              title: >-
                Pierce, it's time to leave for school. Get your lunch, water
                bottle, and brush your teeth.
              thumbnail: https://brands.home-assistant.io/_/cloud/logo.png
              media_class: app
              children_media_class: null
              navigateIds:
                - {}
                - media_content_type: app
                  media_content_id: media-source://tts
                - media_content_type: provider
                  media_content_id: >-
                    media-source://tts/cloud?message=Pierce%2C+it%27s+time+to+leave+for+school.+Get+your+lunch%2C+water+bottle%2C+and+brush+your+teeth.&language=en-IE&gender=female
mode: single

You cannot use templates in State Conditions, you would need to use a Template Condition. However, you need to be cautious using a calendar entity’s state in that way. If there is any possibility that the either of your calendars might have overlapping events (including all-day events) your condition will be unreliable. It would be more reliable to use a boolean helper controlled by an automation or a trigger-based binary sensor to act as a conditional flag.

ok, so there are a lot of words there… haha

Again, I’m super new. Are you saying that it would be better for me to leave the time triggers, but for the conditions use something like a toggle (boolean) helper that is set by the start of an event? (keep in mind the events I’m talking about are all day events)

So, when the SchoolDay event start, if there is no No school event starting, set the helper the On, else off… I’ll see what I can figure out.

Keep in mind it’s hard to give specific advice because there are so many ways people use any given integration. If you don’t share how you use them and the specific goals of your automation, we have to make educated guesses and give more generic answers.

Two important questions to move forward:
Are all the events all-day?
Is it possible that any other events in these calendars overlap with “SchoolDay” or “No School”?

LOL I was typing when I saw your message come in explaining some of those items.

Basically, I want the speakers to remind the kids to get ready for school. But I don’t want to trigger it on the weekends or during breaks/holidays. So I created a calendar in HA so I can specify what days are school days. Then I need to figure out if there is no school that day based on our family Google. calendar. I do this by telling my wife she needs to put in a calendar entry that says “No school” so that the routine doesn’t run.

Yes, they are all day events. It sounds like HA isn’t very good with calendars. I was just looking through and it sounds like you cant’ see multiple events on the same day that overlap. So if I have something like “Dad Houston” as an event that spans all week, but there is a calendar event that says No School, it might not see it because HA only sees one event at a time… That’s kind of annoying. Would be nice if it was an array I could drop through a for each loop to see if one of the events match… bla bla bla…

So… is there another/better way of doing this? I might just add a trigger that will let me check school day and just manually toggle the helper boolean on a dashboard… but I don’t want to have to do that. (But for now… I will until I figure this out.)

Calendars have been a somewhat neglected function, but that has been improving recently. For Google calendars you can use the Hass Calendar Addon to create a sensor that exposes an array of events. For Local Calendar there’s a “hack” to get the data via REST.

But you can accomplish the goal without using either of those option. You could use template binary sensors triggered by you calendar events.

template:
  - trigger:
      - platform: calendar
        event: start
        entity_id: calendar.school_calendar
      - platform: time
        at: "23:59"
    binary_sensor:
      - name: School Day Cal
        state: >
          {% set current = this.state %}
          {% if trigger.platform == 'time' %} off
          {% elif trigger.platform == 'calendar' %}
            {{ iif(trigger.calendar_event.summary == "SchoolDay", 'on', current) }}
          {% endif %}

  - trigger:
      - platform: calendar
        event: start
        entity_id: calendar.family
      - platform: time
        at: "23:59"
    binary_sensor:
      - name: School Day Family
        state: >
          {% set current = this.state %}
          {% if trigger.platform == 'time' %} on
          {% elif trigger.platform == 'calendar' %}
            {{ iif(trigger.calendar_event.summary == "NoSchool", 'off', current) }}
          {% endif %}

Then you would put those sensors in the conditions instead of the calendar states.

...
condition:
  - alias: "School Calendar says it's a school day"
    condition: state
    entity_id: binary_sensor.school_day_cal
    state: 'on'
  - alias: "Family Calendar says it's a school day"
    condition: state
    entity_id: binary_sensor.school_day_family
    state: 'on'
  - alias: "Not Saturday or Sunday"
    condition: template
    value_template: "{{ now().isoweekday() < 6 }}
...

EDIT: Removed double-negative in second sensor

1 Like

Is the issue here that (1) its checking for a condition where the message is something specific but (2) its not checking that the sensor state is on?

So, I might actually try what Didgeridrew is saying about using the binary sensors. (I need to learn how to set up stuff like that anyway and get better with YAML).

Right now I have it setup to check the local Schood_Day calendar (because I can control that one) but I setup boolean helper that I dropped on a dashboard instead of checking the Google calendar. I’m going to mess around with building the other sensors via the template. It’s the first time I’m going direct to the config file. Should be fun.

Allenporter, the issue is that there could be other calendar events in the family calendar that are active at any given time. If I add a “No School” event, then my wife adds a “Spa Day” event (both all day events), I have no clue what HA is going to say/do. HA is terrible when it comes to calendar events I’m noticing, which is weird because it’s an automation platform where people base things off of time/date/calendars a lot of the time. I’m thinking about finding another way around the issue than the Google calendar but for now, I’ll keep learning. hehe

Yes, I’m trying to fix it while keeping users of existing solutions happy.

The new path I added is discussed in the calendar integration docs. They use triggers that fix the issues with state sensor and overlapping events. Note that the triggers also support offsets like x minutes before or after an event.

However if you then want to use a condition with the sensor state not only can it not deal with overlapping events, it can’t deal with offsets either.

However the triggers don’t work with conditions as you might imagine, no need something else to store the state. The temporary solution I know of is using a template binary sensor to store the state based on the trigger.

I’m pursuing a helper to try to combined both the state approach and trigger approach. The problem is the calendar entity means two things: access to events on a schedule, and some state about a single current event, and these are more like separate concepts and have to be managed.

1 Like

HAHAHA that is always the battle with programming, you want to make something better, but it breaks something people are using. So you’ll be thanked and hated for it at the same time.

So are you working on the integrations for HA? (sorry, not familiar with who does what yet)

I’m going to go down the route of changing a sensor state, boolean help, etc… to flag when certain events happen. I haven’t really setup any public dashboards for the wife yet, so we’ll see what she thinks about it.

Yeah I work on local calendar and Google calendar.

Let us know where you end up

Hello!
I stumbled upon this thread since I’m trying to control some lights using an automation based on time of day as well as certain calendar events… To be specific: I’ve used the “Hass Calendar Addon” you referenced to import a calendar that lists all bank holidays in my country. I called this sensor.holidays … If I check the state of that sensor I get a long list of holidays.

I’m too much of a newbie to figure out how to utilize this data to drive (or in this case prevent) an automation. Simply put, I want to control some power outlets using Shellys to switch things on and off at given times but not on weekends (easy) nor on holidays (hard)…

Here’s a chunk of the data that is in sensor.holidays:

 - summary: New Year's Day
    label: Public holiday
    startDate: 1.1.2024
    startDateISO: "2024-01-01T00:00:00.000+01:00"
    year: 2024
    yearShort: 24
    startMonth: Jan
    startMonthDigits: 1
    startDay: 1
    startTime: null
    wholeDay: true
    endDate: 2.1.2024
    endDateISO: "2024-01-02T00:00:00.000+01:00"
    endMonth: Jan
    endMonthDigits: 1
    endDay: 2
    endTime: null
  - summary: Epiphany (regional holiday)
    label: Public holiday in Baden-Württemberg, Bavaria, Saxony-Anhalt
    startDate: 6.1.2024
    startDateISO: "2024-01-06T00:00:00.000+01:00"
    year: 2024
    yearShort: 24
    startMonth: Jan
    startMonthDigits: 1
    startDay: 6
    startTime: null
    wholeDay: true
    endDate: 7.1.2024
    endDateISO: "2024-01-07T00:00:00.000+01:00"
    endMonth: Jan
    endMonthDigits: 1
    endDay: 7
    endTime: null
  - summary: Shrove Monday
    label: |-
      Observance
      To hide observances, go to Google Calendar Settings > Holidays in Germany
    startDate: 12.2.2024
    startDateISO: "2024-02-12T00:00:00.000+01:00"
    year: 2024
    yearShort: 24
    startMonth: Feb
    startMonthDigits: 2
    startDay: 12
    startTime: null
    wholeDay: true
    endDate: 13.2.2024
    endDateISO: "2024-02-13T00:00:00.000+01:00"
    endMonth: Feb
    endMonthDigits: 2
    endDay: 13
    endTime: null

There are four cases to consider when looking at the label field:

  1. Public Holiday (lights stay off)
  2. Public Holiday <with list of affected prefectures> (lights stay off)
  3. Observance <...optionally with list of affected prefectures> (lights stay on)

So, I need to set a boolean (I guess) to true (or on) when the label contains Public Holiday and if the label contains Public holiday in Baden-Württemberg. In all other cases the boolean should be false (or off).

Any pointers on how this can be achieved would be greatly appreciated… I’ve been fussing with this for a while now and can’t make heads or tails of it. It was a small victory for me to finally get the calendar integration linked to the google account etc. :slight_smile: … I’ve been staring at the long list of data wondering how to tackle this and I’m stumped. I assume some sort of template statement, but I just don’t know where to start.

Thanks in advance to anyone that can point me in the right direction…!

Best regards,
Andrew

Thanks to the work of @allenporter and others, Home Assistant’s ability to use calendars has expanded massively since my post in April… to the point where the addon is no longer necessary for most use cases.

However, if you want to use it as you have described you should be able to use the following template binary sensor.

template:
  - binary_sensor:
      - name: Public Holiday
        state: |
          {% set data = state_attr('sensor.holiday', 'data') %}
          {% set public = data | selectattr('label', 'match', '^Public holiday$') | list %}
          {% set bw = data | selectattr('label', 'match', '(.*)(Public holiday)(.*)(Baden-Württemberg)(.*)') | list %}
          {{ (public + bw) | map(attribute='startDateISO') | map('as_datetime') | select('eq', today_at()) | list | count > 0 }}

1 Like

Hello Drew,

Thank you so much for this! Reading your code I can get the gist of what is going in, but the syntax isn’t obvious to me… At some point I’ll have to really dive into templates…

Trying this out, I got this message in the log:

(TemplateSyntaxError: expected token ':', got '}') for dictionary value 'binary_sensor->0->state', got "{% set data = {{state_attr('sensor.holiday', 'data') }} %}\n{% set public = data | selectattr('label', 'match', '^Public holiday$') | list %}\n{% set bb = data | selectattr('label', 'match', '(.*)(Public holiday)(.*)(Baden-Württemberg)(.*)') | list %}\n{{ (public + bb) | map(attribute='startDateISO') | map('as_datetime') | select('eq', today_at()) | list | count > 0 }}"

I need to switch a } for a : but I don’t know where… Any ideas?

I also played around with the google calendar integration and got it set up, but switched tracks when I saw this thread. How would one go about addressing the same task using a ‘normal’ calendar in Home Assistant?

Thanks again!

Best regards,
Andrew

… I take it back. Now it works. I copied and pasted the code you provided a second time and it worked. I have no clue what went wrong the first time around - been fiddling with it all morning :slight_smile:

In any case, I would be pleased if you could tell me how to do this with the google calendar (or HA native calendar) integration as well

Thanks!

Best regards,
Andrew

There are different ways to handle it depending on your needs and how the calendar in question is set up. The method that will work most generally is as follows:

template:
  - trigger:
      - platform: time
        at: "00:01:00"
    action:
      - service: calendar.get_events
        data:
          start_date_time: "{{ today_at() }}"
          end_date_time: "{{ today_at('23:59:00') }}"
        target:
          entity_id: calendar.YOUR_CALENDAR
        response_variable: agenda
    binary_sensor:
      - name: Public Holiday
        state: |-
          {% set data = agenda['calendar.YOUR_CALENDAR'].events %}
          {% if data | count == 0 %}
            false
          {% else %}
            {% set public = data | selectattr('description', 'match', '^Public holiday$') | list %}
            {% set bw = data | selectattr('description', 'match', '(.*)(Public holiday)(.*)(Baden-Württemberg)(.*)') | list %}
            {{ (public + bw) | count > 0 }}
          {% endif %}

Thank you so much for this! Very helpful!

Have a great day.

Best regards,
Andrew

It’s a bit of a learning curve, but trust me, the flexibility is totally worth it. Now, about those pesky calendar conditions… You’re definitely on the right track with using conditions to make your automation smarter. It’s all about refining those triggers to suit your needs perfectly. Dealing with dates in automations can be a bit tricky, especially when syncing with Google Calendar. Your approach with now.day() seems logical, but I get why it’s not giving you the results you want. Sometimes the logic in automations can be a bit wonky, especially when dealing with date comparisons. Also,check out Calendar Management Guides | Calendar Geek that guide on Calendar Management and Synchronization. It looks super helpful for anyone tinkering with calendar-based automations.