Trigger automation 15 minutes before date_time stored in input_datetime

For reference, a slightly longer template but which doesn’t compare strings, but seconds.

trigger:
  - platform: template
    value_template: "{{ as_timestamp(utcnow()) >= as_timestamp(utcnow().replace(hour=0, minute=0, second=0, microsecond=0)) + state_attr('input_datetime.wakeup_time', 'timestamp') - states('input_number.wakeup_duur') | int }}"

Comparison of the three templates (strings vs datetime objects vs seconds):

{{ now().ctime()[11:16] >= (state_attr('input_datetime.wakeup_time', 'timestamp') - states('input_number.wakeup_duur') | int) | timestamp_custom('%H:%M', false) }}
{{ now() >= now().replace(hour=state_attr('input_datetime.wakeup_time', 'hour'), minute=state_attr('input_datetime.wakeup_time', 'minute')) - timedelta(minutes=states('input_number.wakeup_duur') | int) }}
{{ as_timestamp(utcnow()) >= as_timestamp(utcnow().replace(hour=0, minute=0, second=0, microsecond=0)) + state_attr('input_datetime.wakeup_time', 'timestamp') - states('input_number.wakeup_duur') | int }}

Honestly, I don’t care about the length of the template, but, although working, doing string order comparisons of numerics is against my religion :smiley:

I posted that to show the irony of the situation. It’s simpler to compare the times as string values (hacky) than as native datetime objects or even integers.

In case anyone is wondering why the string comparison can be considered to be ‘hacky’ is because it has no mathematical understanding of time. Its success relies on proper string formatting and the native ASCII ordering of each character in the string (i.e. character 1 is ASCII 49, character 2 is ASCII 50, character 3 is ASCII 51, etc so when comparing character 3 to character 1 it’s “greater”).

An easy way to show how the string comparison technique has no concept of time is to simply try this in the Template Editor:

{{ '5:30' > '11:30' }}

We know that 11:30 is later than 5:30 so that template should report False but it reports True. It will produce False if the left hand side’s length is increased to 4 by prepending a zero. String comparisons use rules that have nothing to do with the concept of time.

Yet it appears your sect has no problem changing datetime objects to integers (when there’s no need to do that since datetime objects can be compared to one another directly). :wink:

Yeah, my religion finds it easier to put everything in seconds once it comes to adding and subtracting
At least, it leaves the issues of timezone, formats and whatnot out :slight_smile:

I think we just need a few more filters that give datetime objects.

today('08:00')

something like that

it assumes ‘HH:MM’ or ‘HH:MM:SS’ and returns a datetime object with todays date in the current tz

ex:

now() >= today(states('input_datetime.xyz')) - timedelta(minutes=15)

super easy to read

or make it today_at() or as_today(

1 Like

…or even easier: that a input_datetime with time only always return a full datetime with today’s date, at least in one of its attribute.

In the template I posted, a timedelta object is used for subtracting a quantity of time from the datetime object. In other words, date math can be easily done with datetime objects.

Not exactly. Your template employs a datetime object, produced by utcnow(), then gets its Unix Timestamp which is referenced to UTC. In other words, the timezone is ‘flattened’ to UTC but it’s still there in the arithmetic.

Yah, but then you wouldn’t have the benefits of ‘today()’ when people want to use a 8 am in a template without an input_datetime.

FWIW, I brought that up in an architecture discussion a year ago and no one responded

1 Like

ok, maybe not a year ago but it felt like that

1 Like

One of the headaches of a time-only input_datetime is that it stores time as local time (not UTC). So its Unix Timestamp is referenced to the Unix Epoch not with a UTC time but with local time.

Example:
If it’s 10:45 local time 5 time zones away from UTC, the input_datetime is:
1970-01-01T10:45+00:00
However, that’s not the correct time in UTC for 10:45 because it doesn’t incorporate +05:00. Attempting to use this as a datetime object becomes a challenge because it’s several hours off.

FWIW, I had raised this as an issue several version ago when datetimes were being re-engineered. However, this was considered to be normal because a time alone cannot be expressed as a Unix timestamp (which I find to be a specious reason because there it is above, referenced to the Unix Epoch).

Well, the “timestamp” attribute is a misnomer, imho. It just holds the number of seconds since midnight local time, which is handy but not quite a Unix epoch.

today() would definitely improve a template’s legibility.

I think the shortest way to get the same result with what’s available today might be this and it’s still far from straightforward: (EDIT: meh, on second thought, still far from “shortest” especially if the 08:00 has to come from some other entity)

{{as_datetime(now().date() ~ 'T08:00').astimezone()}}

The alternative is to use now() and then replacing portions of it but that’s still verbose.

I agree it bends the definition of “timestamp” compared to how that word is used elsewhere (and fairly consistently) in Home Assistant. One discovers just how much ‘bending’ when attempting to use it as a datetime object (and one’s calculations are off by one’s timezone offset).

Anyway, it’s better now that it was 2.5 years ago (when I first started using Home Assistant) and discovered data and time calculations (a staple of home automation logic) were treated like second-class citizens. A few more ‘helper functions’ and it should make juggling date and time even easier.

That would eliminate a lot of headaches. I’d vote for that.

This makes more sense to me:
image

It breaks the “false” timestamp though.
OTOH, intput_datetime with date+time and date-only both have real timestamps, so for the sake of consistency…

The challenge presented by that proposal is that it deviates from the norm and needs to update its value on a daily basis. The other flavors of input_datetime use a fixed value.

1 Like

Just for fun, paste this into the Template Editor:

{% set t = as_datetime('1970-01-01T08:45').astimezone()%}
{{ t }}
{{ t.timestamp() }}
{{ now().fromtimestamp(t.timestamp(), now().tzinfo) }}

There’s a potential hitch with the idea of storing the time (for a time-only input_datetime) on the day of the Unix Epoch plus one’s timezone. The hitch is DST.

The result of the three templates is this (for my timezone):

1970-01-01 08:45:00-05:00
49500.0
1970-01-01 08:45:00-05:00
  • The first line represents the 08:45 as a datetime object whose date is the Unix Epoch and whose timezone is set to the local timezone. It looks good.
  • The second line is the same datetime object but converted to a unix timestamp.
  • The third line creates a datetime object from the timestamp and the result matches the original datetime object. It looks good.

Everything looks perfect except the timezone offset now, in July August, is -04:00 and not -05:00 (which is OK for January).

This poses a challenge of how to use this arrangement to set a fixed time of 08:45 (for year round use) without it being offset by DST.

I’m beginning to see why it currently works the way it does … (namely as just the number of seconds since midnight).

Yeah, no-go.

What about

My as_datetime() and `as_timestamp() don’t appear to have the same magic as yours do.

What I showed is a proposal for a change that I implemented :slight_smile:
Indeed, currently, neither as_datetime nor as_timestamp work for a time-only input_datetime

1 Like