Text to Speech Notification to Leave for Appointment

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

Thanks a lot for this awesome script! Works great.

The only problem I have is when there is no future appointment collected (for some reason there’s no next appointment collected/loaded after the first appointment), is that it seems to show a random map and streetview somewhere in Italy… But that might be a bug/default setting on the Google API side…it’s just a random location…

At the moment my Sonos_Restore is not working though (or it might be caused by the Sonos_Snapshot not working), so unfortunately it doesn’t resume playing music afterwards. But that should be a minor and not too hard fix…

Yes, there’s a problem with the calendar component that doesn’t retrieve calendar items after the first one of the day. I saw someone had reported it on github so hopefully it should be fixed soon in an upcoming release. Seemed to have broken with 0.36.1

As for the Sonos restoring…I did see someone came up with a better version of the time delay script. I haven’t tried it yet, since it was resuming fine for me but you may want to give that a try…

@Bob_NL Check out
https://www.fieldstonsoftware.com/software/gsyncit4/download.shtml
to sync your exchange calendar with your gmail calendar.
It’s for free for up to 1 exchange to 1 gmail and does the job pretty good IMHO.

Hope this helps!

@Jer78 Very nice project! Gonna play with it myself. Bookmarked!