Time of day sensor

I have a calender entity with “End time” attribute that contains a date and time value, the value is formated like “2021-08-05 18:00:00”.

I made a sensor that i want to contain one of the folowing values, day, night and change. Day is between 07:00:00 and 18:00:00, night is between 18:00:00 and 07:00:00. The change is 15 minutes before day or night.

This is my sensor


    time_of_day:
      friendly_name: Time of day
      value_template: >-
        {% if as_timestamp(state_attr("calendar.aiai", "end_time")) | int < (as_timestamp(now()) | int -15*60)-%}
          Passbyte
        {% elif as_timestamp(state_attr("calendar.aiai", "end_time")) | timestamp_custom('%H') | int < 17 -%}
          Nattpass
        {%- elif as_timestamp(state_attr("calendar.aiai", "end_time")) | timestamp_custom('%H') | int > 6 -%}
          Dagpass
        {%- endif %}

I get the day and night values correctly but I never get the change value.

Any ideas what is wrong with my code?

I believe you need to subtract from the timestamp and not from now.
And make a condition that it’s not a timestamp in the past.

{{ as_timestamp(state_attr("calendar.aiai", "end_time")) | int -15*60 < (as_timestamp(now()) | int ) and as_timestamp(now()) < as_timestamp(state_attr("calendar.aiai", "end_time")) }}

    time_of_day:
      friendly_name: Time of day
      value_template: >-
        {% if as_timestamp(state_attr("calendar.aiai", "end_time")) | int -15*60 < (as_timestamp(now()) | int ) and as_timestamp(now()) < as_timestamp(state_attr("calendar.aiai", "end_time"))-%}
          Passbyte
        {% elif as_timestamp(state_attr("calendar.aiai", "end_time")) | timestamp_custom('%H') | int < 17 -%}
          Nattpass
        {%- elif as_timestamp(state_attr("calendar.aiai", "end_time")) | timestamp_custom('%H') | int > 6 -%}
          Dagpass
        {%- endif %}

I will check it later today.

It seems to be working as expected.

If you use datetime objects, the resulting template is more compact and easier to read.

    time_of_day:
      friendly_name: Time of day
      value_template: >
        {% set t = as_datetime(state_attr('calendar.aiai', 'end_time')).astimezone() %}
        {% if t - timedelta(minutes=15) < now() < t %} Passbyte
        {% elif t.hour < 17 %} Nattpass
        {% elif t.hour > 6 %} Dagpass
        {% else %} Unknown
        {% endif %}

EDIT

Corrected time calculation.

The code you posted works great. How do I extend the change (passbyte) for 5 minutes after end time?

Checks if current time is within -15 and +5 of the calendar’s end_time.

    time_of_day:
      friendly_name: Time of day
      value_template: >
        {% set t = as_datetime(state_attr('calendar.aiai', 'end_time')).astimezone() %}
        {% if t - timedelta(minutes=15) < now() < t + timedelta(minutes=5) %} Passbyte
        {% elif t.hour < 17 %} Nattpass
        {% elif t.hour > 6 %} Dagpass
        {% else %} Unknown
        {% endif %}

This seems to work

{% if as_timestamp(state_attr('calendar.aiai', 'end_time')) | int -15*60 < (as_timestamp(now()) | int ) and as_timestamp(now()) < as_timestamp(state_attr('calendar.aiai', 'end_time')) or (as_timestamp(now()) | int ) < as_timestamp(state_attr('calendar.aiai', 'end_time')) | int +5*60 and (as_timestamp(now()) | int ) > as_timestamp(state_attr('calendar.aiai', 'end_time')) -%}

Is the goal to determine if the current time is within -15 and +5 minutes of the end_time or something else?

In other words, is the goal this (in pseudo-code):

end_time - 15 < now < end_time + 5

or something different?

It is. How ever the tricky part is that when current even a new one is started. For example, current events end time is 2021-08-06 07:00:00. Next event start at 2021-08-06 07:00:01.

The use of the now() function within a template means that the template will be evaluated every minute on the minute (and not every second).

Therefore the template will not be evaluated at 07:00:01 but only at 07:00:00, 07:01:00, 07:02:00, etc. In other words, the template’s ‘resolution’ is per minute and not per second.

From the Templating documentation:

Using now() will cause templates to be refreshed at the start of every new minute.