Days Until Date - Google Calendar

I have a “Recycling Day” notification, that I have frankensteined from others code. If the date is today or tomorrow, it displays that. If the date is this week, it displays the weekday. If the date is past 7 days, it shows the actual date. I would like to add the following features (no luck so far):

  1. Display ‘days until’ instead of the date if it is past 7 days.
  2. Have the sensor auto update if the google calendar date changes.

Any help would be greatly appreciated!

Below is the code I currently have:

    #------------------------------------------------
    #---- Recycling Pickup Day
    #------------------------------------------------

      - platform: template
        sensors:
          recycling_pickup_day:
            friendly_name: Recycling Pickup Day
            value_template: >
              {% set pickupday = strptime(states.calendar['recycling2'].attributes.start_time, "%Y-%m-%d %H:%M:%S").strftime("%A") %}
              {% set pickupday2 = strptime(states.calendar['recycling2'].attributes.start_time, "%Y-%m-%d %H:%M:%S").strftime("%b %d, %Y") %}
              {% if as_timestamp(states.calendar.test2.attributes.start_time) / 86400 - as_timestamp(as_timestamp(now()) | timestamp_custom("%Y-%m-%d 00:00:00",true)) / 86400 == 0.0 %}
                - TODAY -
              {% elif as_timestamp(states.calendar.recycling2.attributes.start_time) / 86400 - as_timestamp(as_timestamp(now()) | timestamp_custom("%Y-%m-%d 00:00:00",true)) / 86400 == 1.0 %}
                TOMORROW
              {% elif as_timestamp(states.calendar.recycling2.attributes.start_time) / 86400 - as_timestamp(as_timestamp(now()) | timestamp_custom("%Y-%m-%d 00:00:00",true)) / 86400 >= 7.0 %}
                {{ pickupday2 }}
              {% elif as_timestamp(states.calendar.recycling2.attributes.start_time) / 86400 - as_timestamp(as_timestamp(now()) | timestamp_custom("%Y-%m-%d 00:00:00",true)) / 86400 >= 2.0 %}
                {{ pickupday }}
              {% else %}
                Unknown
              {% endif %}

This should work:

      - platform: template
        sensors:
          recycling_pickup_day:
            friendly_name: Recycling Pickup Day
            value_template: >
              {% set pickupday = strptime(states.calendar['recycling2'].attributes.start_time, "%Y-%m-%d %H:%M:%S").strftime("%A") %}
              {% set pickupday2 = strptime(states.calendar['recycling2'].attributes.start_time, "%Y-%m-%d %H:%M:%S").strftime("%b %d, %Y") %}
              {% if as_timestamp(states.calendar.test2.attributes.start_time) / 86400 - as_timestamp(as_timestamp(now()) | timestamp_custom("%Y-%m-%d 00:00:00",true)) / 86400 == 0.0 %}
                - TODAY -
              {% elif as_timestamp(states.calendar.recycling2.attributes.start_time) / 86400 - as_timestamp(as_timestamp(now()) | timestamp_custom("%Y-%m-%d 00:00:00",true)) / 86400 == 1.0 %}
                TOMORROW
              {% elif as_timestamp(states.calendar.recycling2.attributes.start_time) / 86400 - as_timestamp(as_timestamp(now()) | timestamp_custom("%Y-%m-%d 00:00:00",true)) / 86400 >= 7.0 %}
                Pick up is in {{ (as_timestamp(states.calendar.recycling2.attributes.start_time) / 86400 - as_timestamp(now()) / 86400) | int  }} days
              {% elif as_timestamp(states.calendar.recycling2.attributes.start_time) / 86400 - as_timestamp(as_timestamp(now()) | timestamp_custom("%Y-%m-%d 00:00:00",true)) / 86400 >= 2.0 %}
                {{ pickupday }}
              {% else %}
                Unknown
              {% endif %}

But I’m confused about why you did all of the custom_timestamp conversions? I’m pretty sure it’s not necessary and only seems to make things more complicated.

Unless I’m missing something then this value_template should work exactly the same:

{% set pickupday = strptime(states.calendar['recycling2'].attributes.start_time, "%Y-%m-%d %H:%M:%S").strftime("%A") %}
{% set pickupday2 = strptime(states.calendar['recycling2'].attributes.start_time, "%Y-%m-%d %H:%M:%S").strftime("%b %d, %Y") %}
{% if as_timestamp(states.calendar.test2.attributes.start_time) / 86400 - as_timestamp(now()) / 86400 == 0.0 %}
  - TODAY -
{% elif as_timestamp(states.calendar.recycling2.attributes.start_time) / 86400 - as_timestamp(now()) / 86400 == 1.0 %}
  TOMORROW
{% elif as_timestamp(states.calendar.recycling2.attributes.start_time) / 86400 - as_timestamp(now()) / 86400 >= 7.0 %}
  Pick up is in {{ (as_timestamp(states.calendar.recycling2.attributes.start_time) / 86400 - as_timestamp(now()) / 86400) | int  }} days
{% elif as_timestamp(states.calendar.recycling2.attributes.start_time) / 86400 - as_timestamp(now()) / 86400 >= 2.0 %}
  {{ pickupday }}
{% else %}
  Unknown
{% endif %}

EDIT: I also noticed you have a stray “test2” calendar entry in there. You might need to remove that if it’s not correct.

Thanks so much, that works perfect! Good catch on the ‘test 2’, it was a remnant of copy/paste. As far as the custom timestamp conversions- thanks for simplifying this. I am still new to Home Assistant, and very new to this type of code. I gathered what I could find, and tried to reverse engineer it to figure out how it worked. This has been one of the more challenging tasks on my wish list so far, and I appreciate the assistance!

Not to toot my own horn :wink:, but I just created a thread on this very subject last week:

Maybe it can help you out.

I implemented this for my garbage collcetion too. But somehow its always counting 1 day less than it actually is. Anyone got a clue what that could be? My google calendar ist set to time zone “Berlin” and so is my HA. Still if its 12 days it shows 11 days in the sensor

Problem with 1 day less displayed is start_time attribute. If changed to end_time it will show proper counter. Other condition is that events in calendar should be wholeday events.

Thanks for the awesome template @finity it works just as I would expect it to. The only issue I’m having right now is that it returns “Unknown” if it is 2 days away. Any idea why this would happen?

I’m not really sure. It’s hard to tell unless you post the code exactly as you have it right now.

And post a screenshot of the calendar attributes if possible.

Right, of course!

value_template: >-
        {% set pickupday = strptime(states.calendar['plastic_collect'].attributes.start_time, "%Y-%m-%d %H:%M:%S").strftime("%A") %}
        {% set pickupday2 = strptime(states.calendar['plastic_collect'].attributes.start_time, "%Y-%m-%d %H:%M:%S").strftime("%b %d, %Y") %}
        {% if as_timestamp(states.calendar.plastic_collect.attributes.start_time) / 86400 - as_timestamp(now()) / 86400 == 0.0 %}
          TODAY
        {% elif as_timestamp(states.calendar.plastic_collect.attributes.start_time) / 86400 - as_timestamp(now()) / 86400 == 1.0 %}
          TOMORROW
        {% elif as_timestamp(states.calendar.plastic_collect.attributes.start_time) / 86400 - as_timestamp(now()) / 86400 >= 2.0 %}
          {{ (as_timestamp(states.calendar.paper_collect.attributes.end_time) / 86400 - as_timestamp(now()) / 86400) | int  }} days
        {% else %}
          Unknown
        {% endif %}

The Data from the Calendar looks like:

message: Gelbe Tonne
all_day: true
start_time: '2021-01-07 00:00:00'
end_time: '2021-01-08 00:00:00'
location: ''
description: ''
offset_reached: false
friendly_name: Plastic Waste Collection Date

Oddly enough, if I use end_time, it works (though offset by a day). It was working fine a day ago, so I’m guessing the issue is where the code checks for >= 2.0

This is a pretty old template thread so I looked at the whole template and decided it could be done better. there are a few things in the original that I’m not even sure why they are there.

Here is the newest template to try:

{% set start_time = states.calendar.plastic_collect.attributes.start_time %}
{% set pickupday = strptime(states.calendar.plastic_collect.attributes.start_time, "%Y-%m-%d %H:%M:%S").strftime("%A") %}
{% if as_timestamp(start_time) > (as_timestamp(now()) + 86400) %}
  Pick up is in {{ ((as_timestamp(start_time) - as_timestamp(now())) / 86400) | float | round(0, 'ceil') }} days ({{ pickupday }})
{% elif as_timestamp(start_time) > as_timestamp(now())  %}
  Pick up is TOMORROW
{% elif (as_timestamp(start_time) - as_timestamp(now())) > -86400 %}
  Pick up is TODAY
{% else %}
  Unknown
{% endif %}

You can test it by putting the following into the dev tools template editor and manipulating the “start_time” date string:

{% set start_time = '2021-01-11 00:00:00' %}
{% set pickupday = strptime(start_time, "%Y-%m-%d %H:%M:%S").strftime("%A") %}
{% if as_timestamp(start_time) > (as_timestamp(now()) + 86400) %}
  Pick up is in {{ ((as_timestamp(start_time) - as_timestamp(now())) / 86400) | float | round(0, 'ceil') }} days ({{ pickupday }})
{% elif as_timestamp(start_time) > as_timestamp(now())  %}
  Pick up is TOMORROW
{% elif (as_timestamp(start_time) - as_timestamp(now())) > -86400 %}
  Pick up is TODAY
{% else %}
  Unknown
{% endif %}
2 Likes

This works flawlessly (for now). What was the issue with the older template that resulted in it reverting to “Unknown” (apart from it being rather convoluted and not DRY)?

Because the use of the “is equal to” comparison (==) would never result in true unless the start time exactly matched now.

You can see what was going on by putting the following into the template editor and again manipulating the start_time variable:

{% set start_time = '2021-01-07 00:00:00' %}
{% if as_timestamp(start_time) / 86400 - as_timestamp(now()) / 86400 == 0.0 %}
  TODAY
{% elif as_timestamp(start_time) / 86400 - as_timestamp(now()) / 86400 == 1.0 %}
  TOMORROW
{% elif as_timestamp(start_time) / 86400 - as_timestamp(now()) / 86400 >= 2.0 %}
   {{ (as_timestamp(start_time) / 86400 - as_timestamp(now()) / 86400) | int  }} days
{% else %}
   Unknown
{% endif %}

{{ as_timestamp(start_time) / 86400 - as_timestamp(now()) / 86400 }}
{{ as_timestamp(start_time) / 86400 - as_timestamp(now()) / 86400 == 0.0 }}

{{ as_timestamp(start_time) / 86400 - as_timestamp(now()) / 86400 == 1.0 }}

{{ as_timestamp(start_time) / 86400 - as_timestamp(now()) / 86400 >= 2.0 }}

the only time that you will return true is if the date is >= 2 (the last line of the template) if the date is the 8th or later. every other date at midnight will return false for all three tests → hence ‘unknown’.

1 Like

Makes sense! Thanks for clarifying and for taking the time to basically do all the work for me. It was not necessary and I appreciate you making my life easier! :clap: