Using google.tts_say to speak the day's calendar events

I’ve been looking to add more speech messages to my daily briefing announcements. One thought that came to mind was verbalizing the current day’s Google calendar events for morning routines, and tomorrow’s events for evening routines. Each part of my briefing is separated into macros.

I’ve been looking around, and I can’t quite seem to find a relatively easy way to:

  1. Have the automation look at the current day in the calendar
  2. If morning, read today’s events
  3. If evening, read tomorrow’s events

I have multiple calendars in HA, but I wanted to start off with mine. Then, if all goes well, reuse that code as a template for the other calendars.

Hi,

I’m almost doing that. Here are some of my stuff to inspire you.

date.jinja (a custom_templates to build usefull date functions)

{%- macro today() -%}
{{- today_at() -}}
{%- endmacro -%}

{%- macro tomorrow() -%}
{{- today_at() + timedelta(days=1) -}}
{%- endmacro -%}

An automation to send the event to my phone as text

- id: '1675873517447'
  alias: Annonce Olivier
  description: ''
  trigger:
  - platform: time
    at: 07:00:00
    id: morning
  - platform: time
    at: '22:00:00'
    id: evening
  condition: []
  action:
  - if:
    - condition: trigger
      id: morning
    then:
    - service: script.agenda
      data:
        date: '{% import ''date.jinja'' as dt %} {{ dt.today() }}'
        calendrier: calendar.olivier
      response_variable: olivier
    - if:
      - condition: template
        value_template: '{{ olivier|length > 0 }}'
      then:
      - service: notify.mobile_app_iphone
        data:
          title: Today
          message: "{% for key, value in olivier.items() %}\n  {%- if key[-5:] !=\
            \ \"00:00\" %}\n    {{- key[-5:] + \" \" + value }}\n  {%- else %}\n \
            \   {{ value }}  \n  {%- endif %}\n{% endfor %}"
    else:
    - service: script.agenda
      data:
        date: '{% import ''date.jinja'' as dt %} {{ dt.tomorrow() }}'
        calendrier: calendar.olivier
      response_variable: olivier
    - if:
      - condition: template
        value_template: '{{ olivier|length > 0 }}'
      then:
      - service: notify.mobile_app_iphone
        data:
          title: Tomorrow
          message: "{% for key, value in olivier.items() %}\n  {%- if key[-5:] !=\
            \ \"00:00\" %}\n    {{- key[-5:] + \" \" + value }}\n  {%- else %}\n \
            \   {{ value }}  \n  {%- endif %}\n{% endfor %}"
  mode: single

The script used in the automation

agenda:
  alias: Agenda
  sequence:
  - service: calendar.list_events
    data:
      start_date_time: '{{ date }}'
      duration:
        hours: 24
        minutes: 0
        seconds: 0
    target:
      entity_id: '{{ calendrier }}'
    response_variable: evenements
  - variables:
      reponse: "{%- set ns = namespace(events={}) %}\n{%- for event in evenements.events\
        \ %}\n  {%- set default_time = \"00:00:00Z\" %}\n  {%- set date_format = \"\
        %Y-%m-%dT%H:%M:%S%z\" %}\n  {%- set datetime_var = strptime(event.start if\
        \ \"T\" in event.start else event.start + \"T\" + default_time, date_format)\
        \ %}\n  {%- set hour = datetime_var.strftime(\"%d/%m/%Y %H:%M\") if \"T\"\
        \ in event.start else datetime_var.strftime(\"%d/%m/%Y 00:00\") %}\n  {%-\
        \ set ns.events = dict(ns.events, **{hour:event.summary}) %}\n{%- endfor %}\n\
        {{ ns.events }}"
  - stop: Success
    response_variable: reponse
  mode: single
  icon: mdi:calendar-alert-outline

Using the olivier response in the TTS should be easy enough. Just replace the notify.mobile_app_iphone with the call to tts.google_translate_say service

1 Like

And for multiple calendars, this is the exact reason why I did a script.
So you can call it multiple times, like I do in an automation of mine

  - service: script.agenda
    data:
      date: |-
        {%- import "date.jinja" as dt %}
        {{ dt.today() }}
      calendrier: calendar.conges_scolaires
    response_variable: scolaire
  - service: script.agenda
    data:
      date: |-
        {%- import "date.jinja" as dt %}
        {{ dt.today() }}
      calendrier: calendar.chloe
    response_variable: chloe

then I’m checking if one of the 2 contains events and do some action like so

          {% if scolaire | length > 0 or chloe | length > 0 %}
            Today's events are
            {% for key, value in scolaire.items() %}
              {%- if key[-5:] != "00:00" %}
                {{- key[-5:] + " " + value }}
              {%- else %}
                {{ value }}  
              {%- endif %}
            {% endfor %}
            {% for key, value in chloe.items() %}
              {%- if key[-5:] != "00:00" %}
                {{- key[-5:] + " " + value }}
              {%- else %}
                {{ value }}  
              {%- endif %}
            {% endfor %}
          {% endif %}

Sorry, I’ve been away for a few days. Thank you for this info!

My briefing template, which covers morning and evening announcements (with some elements reading certain text based on strftime) are all in macros. In this case, how would I call the target calendar entity to list all events for the current/next day? For example:

  {%- macro getSomeCalendar() -%}
  {% if now().strftime('%H')|int < 12 %}
    Placeholder to read todays calendar information
  {% elif now().strftime('%H')|int >= 12 and now().strftime('%H')|int < 21 %}
    Placeholder to read tomorrows calendar information
  {% else %}
    Placeholder to read tomorrows calendar information
  {% endif %}
  {%- endmacro -%}

Can you call a service in a macro :thinking:?
I don’t think so.
Macro is a (set of) jinja command(s) only.
You should convert the macro into a script.

Hello together

I’ve used the sample above with list_events and converted it now to get_events. Some modifications are built in.
I make notifications on my sony-tv and let my speakers talk, depending on boolean flags.

I always put everything in one file and put them into packages-directory.

Hope it’s useful for others

automation:
- id: 'calendar_events_announcments_get'
  alias: calendar events announcments get
  description: calendar_events_announcments is in the file usto_set_calendar_events.yaml
  trigger:
    - platform: time
      at: "07:00:00"
      id: morning
    - platform: time
      at: "22:00:00"
      id: evening
    - platform: time_pattern
      minutes: /30
      id: next30minutes
  condition: []
  action:
    - if:
        - condition: trigger
          id: next30minutes
        - condition: time
          after: "05:59:00"
          before: "22:35:00"
          weekday:
            - sun
            - mon
            - tue
            - wed
            - thu
            - fri
            - sat
      then:
        - service: script.calendar_events_announcments_agenda_get
          data:
            start_date_time: "{{ (now() | as_datetime + timedelta(minutes=59))  }}"
            end_date_time: "{{ (now() | as_datetime + timedelta(minutes=89))  }}"
            calendrier: calendar.thomas_example_com
            description: "on"
            location: "on"
          response_variable: resp_array
        - if:
            - condition: template
              value_template: "{{ resp_array|length > 0 }}"
          then:
            - if:
                - condition: state
                  entity_id: input_boolean.notify_tts_prio_medium
                  state: "on"
              then:
                - service: script.turn_on
                  entity_id: script.notify_tts_speak
                  data:
                    variables:
                      volume: 0.5
                      speakers: group.tts_notify_speaker_medium
                      language: de-CH
                      message: >-
                        Kommende Termine: {% for key, value in resp_array.items()
                        %}
                          {%- if key[-5:] != "00:00" %}
                            {{- key[-5:] + " " + value }}
                          {%- else %}
                            {{ value }}  
                          {%- endif %}
                        {% endfor %}
            - if:
                - condition: state
                  entity_id: input_boolean.notify_tv_prio_medium
                  state: "on"
              then:
                - service: notify.sony
                  data:
                    title: Kommende Termine
                    message: |-
                      {% for key, value in resp_array.items() %}
                        {%- if key[-5:] != "00:00" %}
                          {{- key[-5:] + " " + value }}
                        {%- else %}
                          {{ value }}  
                        {%- endif %}
                          {% endfor %}
                    data:
                      fontsize: medium
                      position: center
                      duration: 15
                      transparency: 0%
                      color: indigo
                      interrupt: 0

    - if:
        - condition: trigger
          id: morning
      then:
        - service: script.calendar_events_announcments_agenda_get
          data:
            start_date_time: "{% import 'date.jinja' as dt %} {{ dt.today() }}"
            end_date_time: "{% import 'date.jinja' as dt %} {{ dt.tomorrow() }}"
            calendrier: calendar.thomas_example_com
            description: "off"
            location: "on"
          response_variable: resp_array
        - if:
            - condition: template
              value_template: "{{ resp_array|length > 0 }}"
          then:
            - if:
                - condition: state
                  entity_id: input_boolean.notify_tts_prio_medium
                  state: "on"
              then:
                - service: script.turn_on
                  entity_id: script.notify_tts_speak
                  data:
                    variables:
                      volume: 0.5
                      speakers: group.tts_notify_speaker_medium
                      language: de-CH
                      message: >-
                        Termine von heute: {% for key, value in resp_array.items()
                        %}
                          {%- if key[-5:] != "00:00" %}
                            {{- key[-5:] + " " + value }}
                          {%- else %}
                            {{ value }}  
                          {%- endif %}
                        {% endfor %}
            - if:
                - condition: state
                  entity_id: input_boolean.notify_tv_prio_medium
                  state: "on"
              then:
                - service: notify.sony
                  data:
                    title: Heutige Termine
                    message: |-
                      {% for key, value in resp_array.items() %}
                        {%- if key[-5:] != "00:00" %}
                          {{- key[-5:] + " " + value }}
                        {%- else %}
                          {{ value }}  
                        {%- endif %}
                          {% endfor %}
                    data:
                      fontsize: medium
                      position: center
                      duration: 15
                      transparency: 0%
                      color: indigo
                      interrupt: 0
    - if:
        - condition: trigger
          id: evening
      then:
        - service: script.calendar_events_announcments_agenda_get
          data:
            start_date_time: "{% import 'date.jinja' as dt %} {{ dt.tomorrow() }}"
            end_date_time: "{% import 'date.jinja' as dt %} {{ dt.day_after_tomorrow() }}"
            calendrier: calendar.thomas_example_com
            description: "off"
            location: "on"
          response_variable: resp_array
        - if:
            - condition: template
              value_template: "{{ resp_array|length > 0 }}"
          then:
            - if:
                - condition: state
                  entity_id: input_boolean.notify_tts_prio_medium
                  state: "on"
              then:
                - service: script.turn_on
                  entity_id: script.notify_tts_speak
                  data:
                    variables:
                      volume: 0.5
                      speakers: group.tts_notify_speaker_medium
                      language: de-CH
                      message: >-
                        Termine von morgen: {% for key, value in
                        resp_array.items() %}
                          {%- if key[-5:] != "00:00" %}
                            {{- key[-5:] + " " + value }}
                          {%- else %}
                            {{ value }}  
                          {%- endif %}
                        {% endfor %}
            - if:
                - condition: state
                  entity_id: input_boolean.notify_tv_prio_medium
                  state: "on"
              then:
                - service: notify.sony
                  data:
                    title: Morgige Termine
                    message: |-
                      {% for key, value in resp_array.items() %}
                        {%- if key[-5:] != "00:00" %}
                          {{- key[-5:] + " " + value }}
                        {%- else %}
                          {{ value }}  
                        {%- endif %}
                          {% endfor %}
                    data:
                      fontsize: medium
                      position: center
                      duration: 15
                      transparency: 0%
                      color: indigo
                      interrupt: 0
  mode: single
  


script:
  calendar_events_announcments_agenda_get:
    alias: Calendar Events Announcments Agenda Get
    sequence:
    - service: calendar.get_events
      target:
        entity_id: "{{ calendrier }}"
      data:
        start_date_time: "{{ start_date_time }}"
        end_date_time: "{{ end_date_time }}"
      response_variable: eventlist
    - variables:
        reponse: >-
          {%- set ns = namespace(events={} , itemtext="" , get_descr = 0 , get_loc = 0) %}
          {%- if description == "on" %}
            {%- set ns.get_descr = 1 %}
          {%- endif %}
          {%- if location == "on" %}
            {%- set ns.get_loc = 1 %}
          {%- endif %}

          {%- for key, value in eventlist.items() %}
            {%- for event in value.events %}

              {%- set default_time = "00:00:00Z" %}
              {%- set date_format = "%Y-%m-%dT%H:%M:%S%z" %}
              {%- set datetime_var = strptime(event.start if "T" in event.start else event.start + "T" + default_time, date_format) %}
              {%- set hour = datetime_var.strftime("%d/%m/%Y %H:%M") if "T" in event.start else datetime_var.strftime("%d/%m/%Y 00:00") %}

              {%- set ns.itemtext = event.summary %}

              {%- if event.location is defined and ns.get_loc == 1 %}
                {%- set ns.itemtext = ns.itemtext + " ("+event.location+")" %}
              {% endif -%}

              {%- if event.description is defined and ns.get_descr == 1 %}
                {%- set ns.itemtext = ns.itemtext + " ("+event.description+")" %}
              {% endif -%}

              {%- set ns.events = dict(ns.events, **{hour:ns.itemtext}) %}

            {%- endfor %}
          {%- endfor %}
          {{ ns.events }}

    - stop: Success
      response_variable: reponse
    mode: single
    icon: mdi:calendar-alert-outline


1 Like

I am trying to implement your automation. I’ve added the script part as a script but when I try to add the automation part as an automation I get this error message. Not sure what is exactly wrong:

Message malformed: extra keys not allowed @ data['0']