Find the error in: automation last_triggered with now() or timestamp templates

Also, attributes on the state objects are NOT equivalent to attributes in the attribute section. You’re messing that up as well seeing as you are trying to compare last_changed to attributes.last_triggered.

I know i’ve said this before but you REALLY need to take basic python tutorials. You keep bum rushing your code and you continually run into these basic issues. (when i say basic, i’m talking basic for the level you should be at with the amount of work you put into this stuff, I wouldn’t expect beginners to consider this basic).

No, I am aware of that, was simply trying to give another example of the use of the now() template.

Lol, never heard of that before either … will look that up too :wink:

so, one more question then before I take that extra class: if I wouldn’t use state_atrr() but states.entity_id.attributes.last_triggered, would that go?

As a matter of fact it was designed like that…to be populated after getting called, and stays like that during runtime of course. I havent run into operational issues. I only noticed just now, because of some change in the logic of my setup… So you’re right.

Also, this should work too for a datetime object instead of default()

{% set last_triggered = state_attr('automation.low_light__hallway_motion_sensors','last_triggered') or now() %}
{{ (now() - last_triggered).total_seconds() > 120 }}

Ive tried that too, but it doesn’t evaluate to a true or false in the dev-template

nor does:

{% set last_triggered = state_attr('automation.low_light__hallway_motion_sensors','last_triggered') or now() %}
{{ (now() - last_triggered).total_seconds() > 120 }}

all empty in the dev_template

and

error:
plate>", line 2, in top-level template code TypeError: unsupported operand type(s) for -: 'datetime.datetime' and 'str'

second one resolves to false for me.

That means that last_triggered is not a datetime object, but a string. So you need to convert the string into a datetime object.

EDIT: Just confirmed, its a string. Gotta convert it using strptime

well, that’s not very ‘easy’ anymore… compared to my original timestamp template

  {{ (as_timestamp(now()) - 
      as_timestamp(state_attr('automation.low_light__hallway_motion_sensors','last_triggered')) 
      | default(0) | int > 120) }}

and I cant get that to show up true of als either.

Have some template using strptime doing their job:

          {{ (now().strftime("%d-%m-%Y")) == 
          (as_timestamp(strptime(states('sensor.trash_gft'), "%d-%m-%Y")) - 
          (1 * 86400 )) | timestamp_custom("%d-%m-%Y") }}

but they all use as_timestamp to start with, while I would like the now()…bit lost really.

to explain a bit better why I was trying to find the issue, heres another automation that works just perfectly, and has the exact same format:

  - condition: template
    value_template: >
      {{ (now() - 
          state_attr('automation.boiler_switch','last_triggered')|default(0)).total_seconds() > 240 }} 

have a look:

somehow the low_light automation throws in the T in the last_triggered, where the other don’t…
Would you know why that is the case?

After manually triggering the automation the T disappears and I get an evaluation, rendering the same format:

you must be getting lucky. That’s still a string

do this and see what you get

{{ state_attr('automation.boiler_switch','last_triggered') * 2 }}

I bet your result will be the date&time twice. Meaning its a string. So… the proper way to do this is:

{% set dt = strptime(state_attr('automation.boiler_switch','last_triggered')[:-6],"%Y-%m-%dT%H:%M:%S.%f")

That will return a timestamp in utc.

in fact it doesnt…

while:

Well, it looks like that one is a datetime object. Not sure why they are different. Anyways, if you see a T in your return or it is multiplied by 2 as in you see the date twice, it’s a string. You can also just check the type

{{ state_attr('automation.boiler_switch','last_triggered') is string }}

apparently they are not. It is a matter of being triggered or not, and hence getting set. at startup this is the situation:

after triggering manually (the set conditions aren’t yet met):

so, I need a safeguard for the case the automation hasn’t been triggered yet. And that s why I had the default(0) in there, but that obviously isn’t correct.
Id hate to have it triggered at startup , simply for ‘hacking’ this, I want the automation template to be correct in itself.

What would you do? Other than use the as_timestamp or Find the error in: automation last_triggered with now() or timestamp templates - #4 by petro? Can I take out the default(0) without pain. Seems not to hurt when triggered already, and not to do anything if not…

I need to fix both. The boiler switch seems to behave alright, but it is saved by the fact it triggers on the binary_sensor.workday_sensor, which is set on each startup, so the automation is triggered automatically… I think.

Edit triggering on workday cant be it. Ive added that to the automaton, without result, as I have done with all triggers of the boiler_switch… what is happening here? It keeps saying it never got triggered, even after adding the home assistant startup event… only a manual trigger causes it to showup in the dev-template.

So, I would check to see if it’s a string inside the template. If so convert it. I’d also check to see if it’s None, then just use now(). And lastly assume it’s a datetime object.

{% set last_triggered = state_attr('automation.boiler_switch','last_triggered') %}
{% if last_triggered is string %}
{% set last_triggered = strptime(last_triggered[:-6],"%Y-%m-%dT%H:%M:%S.%f") %}
{% elif last_triggered == None %}
{% set last_triggered = now() %}
{% endif %}
{{ (now()-last_triggered).total_seconds() > 120 }}

That should always work. And if for some reason you need to add another elif, you can pretty easily. Just always overwrite last_triggered and you’ll be good.

All this to avoid using as_timestamp(now())?

“Ease” :wink:

as_timestamp() would have the same issue. In fact, as_timestamp would only affect the last line.

{{ (as_timestamp(now())-as_timestamp(last_triggered)) > 120 }}

Yep, but couldn’t resist some ribbing though! :slight_smile:

1 Like

HI, and thanks again!

this does evaluate to true, when my automation has been triggered. Then again, my own template

        value_template: >
          {{ (now() - 
              state_attr('automation.low_light__hallway_motion_sensors','last_triggered')).total_seconds() > 120 }} 

does too. It doesnt at startup. NO return:

individual test:

It seems to boil down to the quest for the answer why my boiler automation does populate the last_triggered attribute and why the low_light automation doesn’t.
I use restore state for automations, and both are ‘on’.

couldn’t it be, the last_triggered only gets populated if and when the automation truly gets to the action part? I mean, if it is stopped during the condition section, it has in fact been triggered, but no action follows. Would the last_triggered be set in that case? Ive always assumed it would, but maybe this is an edge case ?

just for summarizing the issue:

this works fine:

        value_template: >
          {{ as_timestamp(now()) - 
              as_timestamp(state_attr('automation.low_light__hallway_motion_sensors','last_triggered')) >120 }}

while this doesnt do anything

      - condition: template
        value_template: >
          {{ (now() - 
              state_attr('automation.low_light__hallway_motion_sensors','last_triggered')).total_seconds() > 120 }} 

haha, keep that ribbing, probably rubbing, coming…
while in the act, please think along ? :wink:
appreciated though, love some positive sarcasm

Cringe-worthy …

2 Likes

please let me get back on this automation @petro
Ive given up using the now() variant when needing the last_triggered attribute. It simply would only work if triggered once before… the as_timestamp variant works solidly.

Still have a second issue though.

Ive setup the automation to trigger every 5 minutes:

    trigger:
      - platform: time
        minutes: '/5'
        seconds: 00

I would expect the following template to max on 5 minutes, ie 300, because of that:

      {{ as_timestamp(now()) - 
          as_timestamp(state_attr('automation.low_light__hallway_motion_sensors','last_triggered')) }}

but it only records the timestamp of the automation being actually executed:

is that to be expected? Or does it mean my trigger isn’t working.

could be the trigger is somewhat magical, because I tested another variant with this trigger:

trigger:
  - platform: time
    minutes: '/1'

and it triggered each second…

must add this system is on 84.3 so the new time_pattern isn’t yet needed.

Update

thought id try something that has always lingered in my mind after @NotoriousBDG shared his thoughts on another automation technique: put all conditions in the action part. This was mainly to be able to test the conditions.

I can confirm this also actually updates the last_triggered attributes correctly now. Apparently the last_triggered is in fact last_run, or last_came_into_action…

anyways, extra note to my self: when in need for an automation last_triggered template in an automation, have the conditions set in the action part, so one can truly work with the last_triggered attribute, not depending on the actual full actionability of the automation conditions…

it also enables the possibility using now(), with which I started this thread. So it all boils down to HA/the automation not setting the attribute last_triggered before it arrives at the action part. Which is a real architectural issue imho.