Calendar trigger stopped working after 2024.7.x

Hello gurus,
I have the following calendar automation which stopped working after upgrading from 2024.6.x to 2024.7.x. Any pointers to set me on the right path would be appreciated.

trigger:
  - platform: time
    at: "10:00:00"
    enabled: true
  - platform: calendar
    event: start
    offset: "0:0:0"
    entity_id: calendar.esco_cal
condition:
  - condition: time
    weekday:
      - fri
      - mon
      - wed
    enabled: true
  - condition: template
    value_template: "{{ 'Vacuum: MBR, MBaR, Dad bedrooms' in trigger.calendar_event.summary }}"
    enabled: true
action:
  - service: vacuum.send_command
    data:
      command: app_segment_clean
      params:
        - segments:
            - 1
            - 2
    target:
      device_id: <some_device_id>
  - service: notify.signal
    data:
      message: |-
        Started {{ event_summary }}  @
        {{(as_local(as_datetime(trigger.calendar_event.start)) |
        string)[:16]}} 
mode: parallel

Trace log:

-->Trigger is successful. 1st of 2 condition passes.
Executed: July 24, 2024 at 10:00:00 AM
Result:

after:
  __type: <class 'datetime.time'>
  isoformat: '00:00:00'
now_time:
  __type: <class 'datetime.time'>
  isoformat: '10:00:00.277800'
before:
  __type: <class 'datetime.time'>
  isoformat: '23:59:59.999999'
weekday:
  - fri
  - mon
  - wed
now_weekday: wed
result: true

--> 2nd condition fails
Executed: July 24, 2024 at 10:00:00 AM
Error: In 'template' condition: UndefinedError: 'dict object' has no attribute 'calendar_event'

value_template has appropriate string quotes for {{ }}.

HA calendar trigger docs here and here confirm the trigger variables are correct and should be available in condition and action sections.

Restarts don’t make a dent.

Help please.

Your trigger is a time trigger, yet you’re trying to access calendar information. If you want to view future calendar information without a calendar trigger, you need to use the calendar entity or the get_events service call.

This configuration wouldn’t have worked before 2024.7.x either.

Thank you @petro for your guidance.

I do have a time trigger (1st criteria).

  - platform: time
    at: "10:00:00"
    enabled: true

But, also a calendar trigger (2nd criteria).

  - platform: calendar
    event: start
    offset: "0:0:0"
    entity_id: calendar.esco_cal

This automation was created ~1.5 yrs ago and has been working since without errors. Perhaps by chance?

The function of the automation was to trigger roborock robot to clean segments 1,2 every mon,wed,fri @10am. Segments 1,2 were identified based on calendar summary text of Vacuum: Office.

Per your recommendation, should I then remove time trigger, and make it into a condition like so:

trigger:
  - platform: calendar
    event: start
    offset: "0:0:0"
    entity_id: calendar.esco_cal
condition:
  - condition: template
    value_template: "{{ 'Vacuum: Office' in trigger.calendar_event.summary }}"
  - condition: time
    before: "10:02:00"
    after: "10:00:00"
    weekday:
      - fri
      - wed
      - mon

Perhaps without a Time Trigger.

Here’s what’s available in the trigger object when a Time Trigger is triggered. What is not available is trigger.calendar_event, which explains the error message.

trigger.calendar_event is only available when the Calendar Trigger is triggered.

@123, thank you for taking the time to respond. To test out the logic (per @petro’s recommendation) I consolidated various {time + calendar} triggers like so; but now calendar doesn’t trigger at all. Verified this via checking Developer Tools-->Events-->Start Listening.

trigger:
  - platform: calendar
    event: start
    entity_id: calendar.esco_cal
    offset: "0:0:5"
condition: []
action:
  - variables:
      match_found: 0
  - choose:
      - conditions:
          - condition: time
            before: "10:02:00"
            after: "10:00:00"
            weekday:
              - mon
              - wed
              - fri
          - condition: template
            value_template: >-
              {{ 'Vacuum: MBR' in
              trigger.calendar_event.summary }}
        sequence:
          - service: vacuum.send_command
            data:
              command: app_segment_clean
              params:
                - segments:
                    - 16
                    - 28
            target:
              device_id: <some_id>
          - alias: Set match_found
            variables:
              match_found: 1
      - conditions:
          - condition: time
            before: "22:32:00"
            after: "22:30:00"
            weekday:
              - sun
              - mon
              - tue
              - wed
              - thu
              - fri
              - sat
          - condition: template
            value_template: "{{ '10:30pm' in trigger.calendar_event.summary }}"
        sequence:
          - service: light.turn_off
            data: {}
            target:
              entity_id: light.outdoor_lights_upon_outside_sensor_triggers
              device_id: <some_deviceid>
          - service: switch.turn_off
            data: {}
            target:
              entity_id: switch.outdoor_switches
          - alias: Set match_found
            variables:
              match_found: 1
    default:
      - if:
          - condition: template
            value_template: "{{ match_found == 0 }}"
        then:
          - service: persistent_notification.create
            metadata: {}
            data:
              message: >-
                Event {{ trigger.calendar_event.summary }} @ {{
                trigger.calendar_event.start }}
  - if:
      - condition: template
        value_template: "{{ match_found == 1 }}"
    then:
      - service: notify.signal
        data:
          message: |-
            Started {{ event_summary }}  @
            {{(as_local(as_datetime(trigger.calendar_event.start)) |
            string)[:16]}} 
mode: parallel

calendar state shows the next scheduled event correctly.

Tried to trigger it by creating a new one-time calendar event both within 15mins and after 15mins of calendar re-check cycles. No signal message (rightfully so). No persistent notification either; but seeing that its the default action for this automation no notification implies no triggering.

Where might I be going wrong this time?

I doubt that automation represents what petro suggested.

What specific event were you listening for?

Your automation uses the match_found variable incorrectly due to scoping rules. The variable’s value is set to 1 inside a conditions section and that’s the limit of its scope. Outside of that section, the variable’s value remains 0.

Scripts - Scope of variables

morikplay
(per petro’s recommendation) I consolidated various {time + calendar} triggers like so;
123 Taras: I doubt that automation represents what petro suggested.

Thank you for the continued guidance. Originally I had two triggers time + calendar. I understood the issue w/ the setup. Calendar trigger had no offset. Which meant both calendar and time trigger would be triggered at the same time. The reason why it kept working was most likely that calendar trigger was being triggered a few nano/micro seconds, in os queues, before time trigger. Something in the internals of HA processing logic changed such that time trigger is now being received+processed before calendar. This would explain the errors (and is congruent w/ both @123 + @petro 's explainations.

What I meant by consolidating {time + calendar} can be seen in the new automation. time trigger is removed and instead made as a condition in the actions section. Hopefully, this helps clarify the situation.

What specific event were you listening for?

state_change events.

Your automation uses the match_found variable incorrectly due to scoping rules. The variable’s value is set to 1 inside a conditions section and that’s the limit of its scope. Outside of that section, the variable’s value remains 0.

Ah. I did read through the link before created the automation. But, I misunderstood this part

if a variable is changed in a nested sequence block, that change will not be visible in an outer sequence block.

My choice block is different to last if block. What would be the recommended way to achieve the desired result: if any of the non-default conditions of the choice block match, then send signal notification
The easy way is to duplicate the signal notification in each choice block. But, thats makes the code bloated and not easy to change (in future).

I did come across few threads on lack of global variables. Trigger Template sensor for this use may cause issues when multiple calendar automations are executed in parallel.

Open to ideas.

If Calendar Trigger and Time Trigger are scheduled to occur at the same time, after being triggered by the Calendar Trigger, it would have been immediately triggered by the Time Trigger. Two triggers, microseconds apart. What happens next depends on the automation’s mode.

  • If mode is single, the second triggering is ignored because the automation is still busy processing the first trigger. A warning message is logged, reporting the incident.

  • If mode is parallel, the second triggering is processed by another instance of the same automation. It will complain about an undefined reference to trigger.calendar_event because it was triggered by the Time Trigger.

I wouldn’t test a Calendar Trigger that way. Simply check the automation’s trace. The trace will show all the details of the automation’s execution. If there’s no trace, then the automation wasn’t triggered.

Thank you @123 for the followup.

Traces do show successful run. It also shows the flaw (you pointed) wrt match_found logic. Lights turn off per trigger but signal notification isn’t sent due to local variable scoping.

Not sure why you want to be notified when there is no matching event but this version preserves that function.

alias: example 1
trigger:
  - platform: calendar
    event: start
    entity_id: calendar.esco_cal
    offset: "0:0:5"
condition: []
action:
  - variables:
      event_type: >
        {% set t = (now().hour, now().minute) %}
        {% if (10, 0) <= t <= (10, 2)
          and now().isoweekday() in [1, 3, 5] 
          and 'Vacuum: MBR' in trigger.calendar_event.summary %}
          {% set x = 1 %}
        {% elif (22, 30) <= t <= (22, 32)
          and '10:30pm' in trigger.calendar_event.summary %}
          {% set x = 2 %}
        {% endif %}
        {{ x | default(0) }}

  - choose:
      - conditions: "{{ event_type == 1 }}"
        sequence:
          - service: vacuum.send_command
            data:
              command: app_segment_clean
              params:
                - segments:
                    - 16
                    - 28
            target:
              device_id: <some_id>
      - conditions: "{{ event_type == 2 }}"
        sequence:
          - service: light.turn_off
            data: {}
            target:
              entity_id: light.outdoor_lights_upon_outside_sensor_triggers
              device_id: <some_deviceid>
          - service: switch.turn_off
            data: {}
            target:
              entity_id: switch.outdoor_switches
    default: []

  - if: "{{ event_type == 0 }}"
    then:
      - service: notify.signal
        data:
          message: |-
            Started {{ event_summary }}  @
            {{(as_local(as_datetime(trigger.calendar_event.start)) |
            string)[:16]}} 
    else:
      - service: persistent_notification.create
        metadata: {}
        data:
          message: >-
            Event {{ trigger.calendar_event.summary }} @ {{
            trigger.calendar_event.start }}
mode: single

If there was no need to report when no matching event is found, I would move the definition of the event_type variable into a Trigger Variable.

alias: example 2
trigger:
  - platform: calendar
    event: start
    entity_id: calendar.esco_cal
    offset: "0:0:5"
    variables:
      event_type: >
        {% set t = (now().hour, now().minute) %}
        {% if (10, 0) <= t <= (10, 2)
          and now().isoweekday() in [1, 3, 5] 
          and 'Vacuum: MBR' in trigger.calendar_event.summary %}
          {% set x = 1 %}
        {% elif (22, 30) <= t <= (22, 32)
          and '10:30pm' in trigger.calendar_event.summary %}
          {% set x = 2 %}
        {% endif %}
        {{ x | default(0) }}
condition:
  - condition: template
    value_template: "{{ event_type != 0 }}"
action:
  - if: "{{ event_type == 1 }}"
    then:
      - service: vacuum.send_command
        data:
          command: app_segment_clean
          params:
            - segments:
                - 16
                - 28
        target:
          device_id: <some_id>
    else:
      - service: light.turn_off
        data: {}
        target:
          entity_id: light.outdoor_lights_upon_outside_sensor_triggers
          device_id: <some_deviceid>
      - service: switch.turn_off
        data: {}
        target:
          entity_id: switch.outdoor_switches

  - service: persistent_notification.create
    metadata: {}
    data:
      message: >-
        Event {{ trigger.calendar_event.summary }} @ {{
        trigger.calendar_event.start }}
mode: single

Thank you @123 !

Not sure why you want to be notified when there is no matching event but this version preserves that function.

The default block is a catch-all to capture erroneous or defunct calendar entry.

1 Like