Trigger automation 15 minutes before date_time stored in input_datetime

Home Assistant is an open-source project that incorporates the work of other open-source projects. For example, its “automation language” employs a mix of YAML, Jinja2, and python (and not all of the available features of each one). Therefore there’s no one reference that comprehensively explains all three and how they are used in Home Assistant.

I advise reviewing the following sections of Home Assistant’s documentation:

YAML
Templating

Beyond that, you will need to jump into Jinja2 documentation which is definitely not written for non-programmers. I suggest looking at the list of filters and tests. Home Assistant doesn’t support all of them but by just browsing through them it will give you a sense of what is potentially available.

@123 Thanks, will look into this!

Just one last question, (sorry to bother you so much): I have also a input_number.wakeup_duur, which contains the number of seconds the wake-up routine takes from 0 to 100 % brightness. How do I replace the 900 in your last formula with this input_number value?
I tried the following in my automation trigger

alias: "WakeUp Start"
trigger:
  platform: template
  value_template: "{{ now().ctime()[11:16]  >= (state_attr('input_datetime.wakeup_time', 'timestamp') - ((states.input_number.wakeup_duur.state) | int)) | timestamp_custom('%H:%M', false) }}"
condition:
  - condition: state
    entity_id: input_boolean.wakeup_enabled
    state: 'on'
  - condition: or
    conditions:
      - condition: state
        entity_id: input_boolean.wakeup_weekend
        state: 'on'
      - condition: time
        weekday:
          - mon
          - tue
          - wed
          - thu
          - fri
action:
  service: mqtt.publish
  data_template:
    topic: cmnd/WAKEUP-LED/WAKEUP
    payload: "{{ states('input_number.wakeup_bright')|int }}"

but my automation still triggers on the input_datetime.wakeup_time value, not on the input_number.wakeup_duur seconds before that time. Any suggestions much appreciated!

trigger:
  - platform: template
    value_template: "{{ now().ctime()[11:16] >= (state_attr('input_datetime.wakeup_time', 'timestamp') - states('input_number.wakeup_duur') | int) | timestamp_custom('%H:%M', false) }}"

Thank you very much!

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