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

HI,

using this for my automation:

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

works just fine.

While I had this same template with now() working to before, it suddenly stopped, after editing it. cant find the error though, must have a black spot…

please help me finding it again?

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

btw, nevermind the __, I am still on 84.3 so thats not the issue.

Is there a reason you’re not using as_timestamp in the second example?

yes, I like the ease of now() :wink:

and use to in my setup in other places. I must have created a slip of the keyboard here.

can’t subtract an integer from a datetime object. When it hits the default it will produce a config error.

Gotta add safety

{% if state_attr('automation.low_light__hallway_motion_sensors','last_triggered') %}
 ... etc...

EDIT: So to clarify, if you restart the system that will error because last_triggered will be null.

isn’t that what the default(0) was for? and the .total_seconds makes it an int, not a date time object?

default turns it into an integer. datetime objects can only add or subtract datetime objects.

If it defaults, its attempting to add or subtract an integer, which isn’t allowed.

All of that is done PRIOR to the .total_seconds()

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