Text to Speech Notification to Leave for Appointment

I’m pretty sure this is possible but not sure where to start. Basically I would like to set up an automation that does the following.

  1. Reads Google Calendar for my next appointment time and location
  2. uses the location to figure out the travel time
  3. Using the travel time determines the time to leave
  4. If the time to leave is within x minutes, generates a text to speech to my Sonos speaker.

I’m just not very good with the scripting and templating but if someone has some ideas pointing me in the right direction, I’d love to give this a try. In the meantime I’ll try to experiment on my own. Thanks.

6 Likes

This sounds interesting and I might have a go at something similar for myself.

I can’t help you much at this point but I can tell you where I would start. To do this I think you will first need to set up a google developers account to get access to some of the apis, it can come in handy for other stuff later on too. It’s free for basic stuff.

https://developers.google.com/console

First you will to need to set up the calendar component.

then google maps travel time.

The rest is my best guess so either wait for the rest of the world to wake up and give you something more definitive or click the arrow to read on.

Ideas and guesses

Once you have these set up and working I think you will be able to create a combination of sensors and input booleans to achieve your automations. I think the tricky part will be passing the variables of “destination” and “arrival_time” from the calender on an as needed basis. Otherwise you could end up with a gazillion notifications around midnight every night. This is the bit where you will need templating. It may be something that is going to be much easier to accomplish via AppDaemon so it might be worth asking in the AppDaemon thread if nobody sees this here in the next few days.

I have a sensor set up for my morning train which tells me how long in minutes my travel time will be so I will arrive at work via public transport at a certain time but it does not tell me when the train leaves, if using car ( mode: driving ) I guess it would be easy enough to subtract the duration and use that as the trigger…

- platform: google_travel_time
  name: morning_train
  api_key: !secret google_travel_key
  origin: !secret home_station_key
  destination: !secret work_station_key
  options:
    mode: transit
    arrival_time: 08:00:00

Thanks @BarryHampants. You’ve set me in the right direction. I’ll give it a try this week and report back on the progress I’m making. :slight_smile:

Any progress so far?

I haven’t had much time lately. Hopefully I’ll have an afternoon soon to sit and hack away at it. :slight_smile:

The problem I’m running into is I can’t seem to figure out how to pass variables into the Google Travel Time component. I don’t know if it’s even possible. Here’s what I have:

 - platform: google_travel_time
   api_key: XXXX
   origin: zone.home
   destination: '{{ states.calendar.jeremy.attributes.location }}'
   options: 
     arrival_time: '{{ states.calendar.jeremy.attributes.start_time }}'

ok, I figured it out, the Google Travel Time component dynamically gets the address and time from the google calendar…now the next stage is to do some calculations to and announce at the time to leave to my Sonos. I’ll post the rest when I get that working for anyone interested.

 - platform: google_travel_time
   api_key: XXXX
   origin: zone.home
   destination: sensor.cal_location
   options: 
     arrival_time: sensor.cal_start_time
 - platform: template
   sensors:
     cal_location:
       value_template: '{{ states.calendar.jeremy.attributes.location }}'
       friendly_name: 'Calendar Location'
       unit_of_measurement: 'address'
     cal_start_time:
       value_template: '{{ states.calendar.jeremy.attributes.start_time }}'
       friendly_name: 'Calendar Start'
       unit_of_measurement: 'time'
5 Likes

Thanks for sharing. I’m curious about the time calculation part so please keep us posted

Got the time calculation working now…just add this as another sensor. The problem I’ve been having all afternoon is 1) error handling (what to do when there’s no location and 2) the automation part to announce the time to leave to my Sonos. I’ll keep working and post when I get it figured out.

     calc_leave_time:
       value_template: '{{ (as_timestamp(states.calendar.jeremy.attributes.start_time) - states.sensor.google_travel_time__driving.attributes.duration.split(" ")[0] | int *60 ) | timestamp_local }}'
       friendly_name: 'Leave Time'
       unit_of_measurement: 'time'
1 Like

I got most of this project working. I’m sure there are a few bugs, but I’ll have to leave that for another time. In the meantime, a mostly working Travel Time with Google Maps and Street View:

First you’ll need to set up the Google Travel Time and Calendar components. In addition to the instructions on the component page, you should enable the Google Street View API.

The next part is to add some sensors:

 - platform: google_travel_time
   api_key: YOUR_GOOGLE_API_HERE
   origin: zone.home
   destination: sensor.new_cal_location        
   options: 
     arrival_time: sensor.cal_start_time
 - platform: template
   sensors:
     cal_title:
       value_template: '{{ states.calendar.jeremy.attributes.message }}'
       friendly_name: 'Title'
     cal_location:
       value_template: '{{ states.calendar.jeremy.attributes.location }}'
       friendly_name: 'Location'
     new_cal_location:
       value_template: '{% if is_state("sensor.cal_location", "None") %}{{ zone.work }}{% else %}{{ states.calendar.jeremy.attributes.location }}{% endif %}' 
       friendly_name: 'Location'
     cal_start_time:
       value_template: '{{ states.calendar.jeremy.attributes.start_time }}'
       friendly_name: 'Start Time'
     calc_leave_time:
       value_template: '{{ (as_timestamp(states.calendar.jeremy.attributes.start_time) - states.sensor.google_travel_time__driving.attributes.duration.split(" ")[0] | int *60 ) | timestamp_custom("%Y-%m-%d %H:%M") }}'
       friendly_name: 'Leave Time'
       unit_of_measurement: 'time'
     sys_time:
       value_template: '{{ (now().strftime("%s") | int | timestamp_custom("%Y-%m-%d %H:%M")) }}'
       unit_of_measurement: 'time'

I tried playing with the above to handle the errors when there is either no calendar item or no location in one to look up zone.work. I have to test this more, as it wasn’t quite functioning. Maybe someone can help there.

Next, add some input_boolean’s to toggle the notification options:

input_boolean:
  announce_time_to_leave:
    name: Announce over Living Room Sonos
    initial: on
    icon: mdi:speaker-wireless
  display_time_to_leave:
    name: Display with Persistent Notification
    initial: on
    icon: mdi:cards-variant

Now comes the automation part. I have one for the voice announcement to my Living Room Sonos and the other as a persistent notification:

  - alias: 'Announce Calendar Leave time'
    trigger:
      platform: time
      minutes: '/1'
      seconds: 0
    condition:
      condition: and
      conditions:
        - condition: template
          value_template: '{{ states.sensor.sys_time.state == states.sensor.calc_leave_time.state }}'
        - condition: state
          entity_id: input_boolean.announce_time_to_leave
          state: 'on'
    action:
      - service: media_player.sonos_snapshot
        data_template:
          entity_id: "media_player.living_room"
      - service: tts.google_say
        data_template:
          entity_id: "media_player.living_room"
          message: 'Excuse me. It is now time to leave for {{ states.calendar.jeremy.attributes.message }}  It will take you {{ states.sensor.google_travel_time__driving.attributes.duration }} travel time.'
      - delay: '00:00:{{ states.media_player.living_room.attributes.media_duration | int }}'
      - service: media_player.sonos_restore
        data_template:
          entity_id: "media_player.living_room"
  - alias: 'Display Calendar Leave time'
    trigger:
      platform: time
      minutes: '/1'
      seconds: 0
    condition:
      condition: and
      conditions:
        - condition: template
          value_template: '{{ states.sensor.sys_time.state == states.sensor.calc_leave_time.state }}'
        - condition: state
          entity_id: input_boolean.display_time_to_leave
          state: 'on'
    action:
      service: persistent_notification.create
      data:
        message: 'It is now time to leave for {{ states.calendar.jeremy.attributes.message }}  It will take you {{ states.sensor.google_travel_time__driving.attributes.duration }} travel time.'
        title: "Calendar Event"

Now add some cameras to display the google maps and street view. Don’t forget to add your API Key:

camera:
 - platform: generic
   name: Destination
   still_image_url: https://maps.googleapis.com/maps/api/staticmap?center={{states.calendar.jeremy.attributes.location}}&zoom=17&size=600x300&maptype=roadmap&markers=color:blue
   limit_refetch_to_url_change: true
 - platform: generic
   name: Street View
   still_image_url: https://maps.googleapis.com/maps/api/streetview?size=600x300&location={{states.calendar.jeremy.attributes.location}}&key=YOUR_API_KEY
   limit_refetch_to_url_change: true

Finally, add some groups. I used the customize section as well (optional):

 calendar_view:
  view: yes
  name: Calendar
  entities:
   - group.next_appointment
   - camera.street_view
   - camera.destination
   
 next_appointment:
  view: no
  name: Next Appointment
  entities:
   - sensor.cal_title
   - sensor.cal_location
   - sensor.cal_start_time
   - sensor.google_travel_time__driving
   - input_boolean.announce_time_to_leave
   - input_boolean.display_time_to_leave

And that’s it!

9 Likes

Well done! Makes me wish I had some appointments in my calendar.

wow, awesome!

I’ll try this out myself as soon as I get home tonight

1 Like

Let me know how it works for you guys. For some reason, it doesn’t seem to pick up the next appointment on my calendar (or it does at random). I think it’s an issue with the way my Google calendar component is setup. It shows no meetings when I definitely have some and then it won’t refresh unless I delete everything for that day, add a new item and then restart HA. Not sure if it’s just me.

Can’t make full use of your setup yet since 80% off all my apointments are on my exchange calendar and thats not yet supported.

I’ll make some fake appointments with location to test if I get the same result

If I look at your code, it looks like it only works for travel times under 1 hour. Is that correct? Or does it allways give the output in minutes (so 1:30 hour will be 90 minutes)?

I created an event with start time 22:00 and location “Amsterdam Schiphol Airport”. This is about 50 minutes drive from my home. For some reason, the “Leave Time” was set for 20 minutes (which is the current travel time to my work). Allthough “Amsterdam Schiphol Airport” is recognized by Google Maps, it look like HA didn’t regognize it as an valid address and therefor used my work as address (since you scripted that in the custom sensor).

I now changed it to a complete address, also around 50 minutes driving time and currently waiting when it will be updated in my frontend.

Update: same output unfortunately and I just received a notification on my Android phone with a leaving time suggestion with a driving time of 55 minutes.

The google travel time reports time in minutes (even if it’s over an hour). I then took that time in mins and converted it into seconds (x 60) and subtracted that from the calendar start time.

Did you make sure you changed all of the “calendar.jeremy” to whatever your calendar is named? It looks like it’s not finding your calendar and then using your work as a back-up (as intended in my script)

FYI If you’re on 36.1 there’s a bug where the calendar doesn’t read after the first appointment. https://github.com/home-assistant/home-assistant/issues/5438

Yes I did, all the sensor information displays correctly except for the leaving time

Change the ‘destination:sensor.new_cal_location’ to just 'destination: sensor.cal_location. I had created the extra sensor to handle the if statement when it finds no address. But if it’s not working correctly (and I can’t troubleshoot until I get the calendar component working again) then just bypass that part.

 - platform: google_travel_time
   api_key: YOUR_GOOGLE_API_HERE
   origin: zone.home
   destination: sensor.cal_location