Trigger only if no more matching calender events for 24hrs

I’m fairly new to HA but I currently have working an automation that on days my google calendar has a night shift event (10pm-6am) It closes my blackout blind at 6.30 then opens again at 15.00.

What I would like is another automation that at the end of a night shift event checks to see if there are anymore night shift events within 24 or 48 hours and if not close the blind at 6.30 and open at 12.00. There may be other shifts within 24 hours from when the last night shift ends so it needs to check specifically for future night shifts. How would I go about this? Obviously I can change the trigger to event end and alter the delay for opening but have no idea how to write the condition.

Calendar entries consist of either NTS, LTS, ERS or OT followed but the machine name I’m assigned to that shift

My other blind automation for reference:

alias: Night Shift Blind Control
description: ""
triggers:
  - trigger: calendar
    entity_id: calendar.shift_patterns
    event: start
    offset: "-15:30:0"
conditions:
  - condition: template
    value_template: |
      {{ trigger.calendar_event.summary | contains("NTS") }}
actions:
  - device_id: aa5415e69934813a2df579b2deaa7ed4
    domain: cover
    entity_id: d8af09d701cc01c15b737495c89d9061
    type: close
  - delay:
      hours: 8
      minutes: 30
      seconds: 0
      milliseconds: 0
  - device_id: aa5415e69934813a2df579b2deaa7ed4
    domain: cover
    entity_id: d8af09d701cc01c15b737495c89d9061
    type: open
mode: parallel

Using extended delays like your current automation does is generally not advised.

A better method would be to have your automation(s) set values to Input Time helpers, then use those in Time triggers to fire the opening and closing actions.

For the new automation you would need to use the calendar.get_events action to query the calendar’s future events. If you search for that action on these forums you will find dozens of example how to use it and filter the results.

I just set up something similar for my son who temporarily lives in our house and is working shift. His shift pattersc onsist of a 42 day cycle with Day, Off, Early, Late and Night. Depending on the current and previous days shifts, I have defined a time window for increasing the underfloor heating in his room and setting it back to reduced temperature.

My solution consists of two automations and a helper input to flag when the automation has run.

  • One that runs every day directly after midnight. It creates the respective time slot(s) for the day depending on the shift schedule. A flag is set to indicate that the schedule has been written to the local calendar for the day.
  • A second one that switches the heating - in your case the blinds, triggered by the calendar events.

I am posting both automations for your information. Maybe you can use the approach for your own purposes.

1. Create calendar entries

alias: Schichtabhängige FBH-Zeiten
description: Trägt die Schichtzeitblöcke ein und löscht die vom Vortag
triggers:
  - at: "00:02:00"
    trigger: time
conditions:
  - condition: state
    entity_id: input_boolean.automation_schicht_eingetragen
    state: "off"
actions:
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ bloecke | length == 0 }}"
        sequence:
          - target:
              entity_id: input_boolean.automation_schicht_eingetragen
            action: input_boolean.turn_on
          - stop: Keine Zeitblöcke für diese Schichtkombination.
  - repeat:
      for_each: "{{ bloecke }}"
      sequence:
        - variables:
            start_raw: "{{ repeat.item.start }}"
            ende_raw: "{{ repeat.item.ende }}"
            start_dt: |
              {% if start_raw == '24:00' %}
                {{ tomorrow }}T00:00:00
              {% else %}
                {{ today }}T{{ start_raw }}:00
              {% endif %}
            ende_dt: |
              {% if ende_raw == '24:00' %}
                {{ tomorrow }}T00:00:00
              {% elif ende_raw < start_raw %}
                {{ tomorrow }}T{{ ende_raw }}:00
              {% else %}
                {{ today }}T{{ ende_raw }}:00
              {% endif %}
        - target:
            entity_id: calendar.planer
          data:
            summary: "Schicht: {{ heute }}"
            start_date_time: "{{ start_dt }}"
            end_date_time: "{{ ende_dt }}"
          action: calendar.create_event
mode: single
variables:
  heute: "{{ states('sensor.schicht_heute') }}"
  gestern: "{{ states('sensor.schicht_gestern') }}"
  today: "{{ now().strftime('%Y-%m-%d') }}"
  tomorrow: "{{ (now() + timedelta(days=1)).strftime('%Y-%m-%d') }}"
  bloecke: |
    {% set g = gestern %} {% set h = heute %}
    {% if h == 'Tag' %}
      {{ [{'start':'15:00','ende':'23:00'}] }}

    {% elif h == 'Früh' %}
      {{ [{'start':'15:00','ende':'22:00'}] }}

    {% elif g == 'Früh' and h == 'Spät' %}
      {{ [
        {'start':'09:00','ende':'14:00'},
        {'start':'21:30','ende':'24:00'}
      ] }}

    {% elif g == 'Spät' and h == 'Spät' %}
      {{ [
        {'start':'00:00','ende':'01:30'},
        {'start':'10:00','ende':'13:30'},
        {'start':'21:30','ende':'24:00'}
      ] }}

    {% elif g == 'Spät' and h == 'Nacht' %}
      {{ [
        {'start':'00:00','ende':'01:30'},
        {'start':'09:00','ende':'20:30'}
      ] }}

    {% elif g == 'Nacht' and h == 'Nacht' %}
      {{ [{'start':'07:00','ende':'21:00'}] }}

    {% elif g == 'Nacht' and h == 'Frei' %}
      {{ [{'start':'09:00','ende':'21:30'}] }}

    {% elif g == 'Frei' and h == 'Frei' %}
      {{ [{'start':'09:00','ende':'21:30'}] }}

    {% else %}
      {{ [] }}
    {% endif %}

2. Actions - in my case heating

alias: Scheduler – Heizung
description: |-
  Steuert die FBH-Heizung im Haus. Schaltet zwischen heat_temp und drop_temp um
   triggers:
  - event: start
    entity_id: calendar.planer
    id: start
    trigger: calendar
  - event: end
    entity_id: calendar.planer
    id: end
    trigger: calendar
  - event: start
    id: ha_start
    trigger: homeassistant
conditions: []
actions:
  - choose:
      - conditions:
          - condition: trigger
            id: start
        sequence:
          - action: climate.set_temperature
            target:
              entity_id: climate.jean_t_soll
            data:
              temperature: "{{ heat_temp }}"
              hvac_mode: heat
      - conditions:
          - condition: trigger
            id: end
        sequence:
          - action: climate.set_temperature
            target:
              entity_id: climate.jean_t_soll
            data:
              temperature: "{{ drop_temp }}"
      - conditions:
          - condition: trigger
            id: ha_start
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: "{{ event_active }}"
                sequence:
                  - action: climate.set_temperature
                    target:
                      entity_id: climate.jean_t_soll
                    data:
                      temperature: "{{ heat_temp }}"
                      hvac_mode: heat
    default:
      - action: climate.set_temperature
        metadata: {}
        target:
          entity_id: climate.jean_t_soll
        data:
          temperature: "{{ drop_temp }}"
          hvac_mode: heat
mode: single
variables:
  heat_temp: 20
  drop_temp: 19.5
  event_active: "{{ is_state('calendar.planer', 'on') }}"

3. Reset help flag

alias: Reset Schicht-Automation Flag
description: Setz den Helper jeden Tag um 23:59 zurück
triggers:
  - at: "23:59:30"
    trigger: time
actions:
  - target:
      entity_id: input_boolean.automation_schicht_eingetragen
    action: input_boolean.turn_off

I was aware extended delays weren’t ideal but it was a quick temporary solution while I was on nights last week to test the condition was matching. I’ve created 3 datetime helpers for close time, regular open time and final night shift open time.

I’m not convinced the value_template is correct for this now and the else section is presumably where I will put actions for if a night shift has just finished but no more within 24hrs once I figure out how to do that, but is it looking a little better trigger wise?

description: ""
mode: single
triggers:
  - trigger: time
    at: input_datetime.blackout_blind_default_close_time
conditions: []
actions:
  - service: calendar.get_events
    target:
      entity_id:
        - calendar.shift_patterns
    data:
      duration:
        hours: 24
    response_variable: nights
  - if:
      - condition:  template
        value_template: |
          {{ trigger.calendar_event.summary | contains("NTS") }}"
    then:
      - device_id: aa5415e69934813a2df579b2deaa7ed4
        domain: cover
        entity_id: d8af09d701cc01c15b737495c89d9061
        type: close
      - wait_for_trigger:
          - trigger: time
            at: input_datetime.blackout_blind_default_open_time
        continue_on_timeout: true
      - device_id: aa5415e69934813a2df579b2deaa7ed4
        domain: cover
        entity_id: d8af09d701cc01c15b737495c89d9061
        type: open
    else:

It’s not, the condition needs to be based off the nights variable not the trigger variable.

description: ""
mode: single
triggers:
  - trigger: time
    at: input_datetime.blackout_blind_default_close_time
conditions: []
actions:
  - service: calendar.get_events
    target:
      entity_id:
        - calendar.shift_patterns
    data:
      duration:
        hours: 24
    response_variable: nights
  - if:
      - alias: Check if the calendar events in response contain NTS in their summary.
        condition:  template
        value_template: |
          {% set events = nights['calendar.shift_patterns']['events'] %}
          {{ events | map(attribute='summary') | select('contains', 'NTS') | list | count | bool }}
...

That same advice applies for all Waits.

Instead of using a Wait for trigger, that might last 8+ hours either…

  • Create a second automation to handle the “open” sequence.
  • Add the trigger to this automation and branch the actions using an If/Then or Choose action. There are a number of ways to set up the logic to branch actions, one option can be seen in the follow community guide: Automations, from Zero to Hero

I wouldn’t be surprised if I’ve over complicated this now but not a wait or delay in sight so how’s this looking?

I still haven’t sussed out how to check that there was a night shift just gone but no more in the evening yet. If there isn’t a way to search for previous events I’m thinking maybe a flag triggered by the end of a night shift that resets at midnight

alias: Night Shift Blackout Blind Control Test A
description: ""
mode: single
triggers:
  - trigger: time
    at: input_datetime.blackout_blind_default_close_time
    id: blind_close
  - trigger: time
    at: input_datetime.blackout_blind_default_open_time
    id: blind_open
  - trigger: time
    at: input_datetime.final_night_shift_blackout_blind_open_time
    id: blind_final_open
conditions: []
actions:
  - choose:
# blind closing
      - conditions:
          - condition: trigger 
            id: blind_close
        sequence:
          - target:
              entity_id:
                - calendar.shift_patterns
            data:
              duration:
                hours: 24
            response_variable: nights
            action: calendar.get_events
          - choose:
              - conditions: # If night shift this evening, close blind
                  - alias: Check if the calendar events in response contain NTS in their summary.
                    condition: template
                    value_template: |
                      {% set events = nights['calendar.shift_patterns']['events'] %}
                      {{ events | map(attribute='summary') | select('contains', 'NTS') | list | count | bool }} 
                sequence:
                  - device_id: aa5415e69934813a2df579b2deaa7ed4
                    domain: cover
                    entity_id: d8af09d701cc01c15b737495c89d9061
                    type: close
              - conditions: # If no night shift this evening but there was a night shift before, close blind
                  - alias: Check if the calendar events in response contain NTS in their summary.
                    condition: template
                    value_template: | # still needs a check for a previous night shift
                      {% set events = nights['calendar.shift_patterns']['events'] %}
                      {{ events | map(attribute='summary') | select('contains', 'NTS') | list | count == 0 | bool }} 
                sequence:
                  - device_id: aa5415e69934813a2df579b2deaa7ed4
                    domain: cover
                    entity_id: d8af09d701cc01c15b737495c89d9061
                    type: close
              - conditions: # If no night shift this evening or night before then do nothing
                  - alias: Check if the calendar events in response contain NTS in their summary.
                    condition: template
                    value_template: | 
                      {% set events = nights['calendar.shift_patterns']['events'] %}
                      {{ events | map(attribute='summary') | select('contains', 'NTS') | list | count == 0 | bool }} 
                sequence: []
# at default blind opening time check if there is a night shift this evening
# if true then open blind, else- do nothing
      - conditions:
          - condition: trigger
            id:
              - blind_open
        sequence:
          - target:
              entity_id:
                - calendar.shift_patterns
            data:
              duration:
                hours: 24
            response_variable: nights
            action: calendar.get_events
          - if:
              - alias: Check if the calendar events in response contain NTS in their summary.
                condition: template
                value_template: |
                  {% set events = nights['calendar.shift_patterns']['events'] %}
                  {{ events | map(attribute='summary') | select('contains', 'NTS') | list | count | bool }} 
            then:
              - device_id: aa5415e69934813a2df579b2deaa7ed4
                domain: cover
                entity_id: d8af09d701cc01c15b737495c89d9061
                type: open
# at default time after last night shift check if there is both no night shift in the evening and there was no night shift before
# if there is a previous night shift but no further night shift then open blind, else do nothing
      - conditions:
          - condition: trigger
            id: blind_final_open
        sequence:
          - target:
              entity_id:
                - calendar.shift_patterns
            data:
              duration:
                hours: 24
            response_variable: nights
            action: calendar.get_events
          - if:
              - alias: Check if the calendar events in response contain NTS in their summary.
                condition: template
                value_template: | # still needs a check for a previous night shift
                  {% set events = nights['calendar.shift_patterns']['events'] %}
                  {{ events | map(attribute='summary') | select('contains', 'NTS') | list | count == 0 | bool }}
            then:
              - device_id: aa5415e69934813a2df579b2deaa7ed4
                domain: cover
                entity_id: d8af09d701cc01c15b737495c89d9061
                type: open