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

trying to follow here, but having serious issues doing so…

before this exact template would return true or false in the dev-template, and now it shows nothing.

another example of this technique, whis is probably one of the more used ones in my setup:

  - condition: template
    value_template: >
      {{ (now() - trigger.from_state.last_changed | default(0)).total_seconds() > 
                                        states('input_number.presence_timer')|int }}

works just fine. Can’t spot the difference, and hasn’t got a safeguard either.

guess you’re right about the error:

, line 4, in top-level template code
TypeError: unsupported operand type(s) for -: 'datetime.datetime' and 'str'

never seen that before either ;-(

thats a DIFFERENT last_changed. It’s always an object and can return null.

You are using state_attr() which returns a string ‘None’.

Yes, these are operators. You can’t add or subtract types that arn’t handled. Simple as that. Always been like this. You just have gotten lucky that those values have always been populated.

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 }}