If statement in automation to determine triggers "for:" time

Yes, it is true

So now if you activate the motion sensor the light should come on. Then after you deactivate the sensor and wait the set number of minutes afterwards the light should be turned off.

Yes, it seems that the problem is the timestamp. That does not seem like a good value and it does not show correctly. For example I tested the sensor and like 5minutes later is showed under 1 value and suddently it jumped to over 3…

If your looking at the result of the time calculation in the template editor it doesn’t update in real-time.

It only updates when you make a change to the data in the left hand window. So if you enter a space or a carriage return it will update. If you don’t do anything but watch it, it will just sit there statically until you make another change.

I used my entities for motion sensors and humidity and the trigger fires for me at the correct time of 1 minute.

The real test is does the automation trigger?

It does not trigger. Currently the sensor has last updated 2018-12-29T13:35:55 and

{{ ((as_timestamp(now()) - as_timestamp(states.sensor.bathroom_motion_sensor.last_updated)) / 60 ) }}

shows 0.14470538298288982

try using this format for conditional timing:

  - condition: template
    value_template: >
      {{ (now() - trigger.from_state.last_changed | default(0) ).total_seconds() > 120 }} 

and adjust naming and timing of course

I am using this slightly more complex template:

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

to have a frontend input_number allow for easy experimenting its the timings…

Are you sure you are looking at the right sensor? that last updated time is from 7 or 8 days ago…?

And i just noticed that at some point my originally suggested template from post #2 got changed from “last_changed” to “last_updated” and I let you talk me into just copying and pasting that the whole time since then. :smiley:

I’m not sure what that will do but try this. It has the updated conversion to int for the humidity sensor:

trigger: 
  - platform: template 
    value_template: "{{ (states.sensor.bathroom_motion_sensor.state == 'off' and states.sensor.zha_01ddaf89_1_1029.state | int < 50 and ((as_timestamp(now()) - as_timestamp(states.sensor.bathroom_motion_sensor.last_changed)) / 60 )| int > 5) or (states.sensor.bathroom_motion_sensor.state == 'off' and states.sensor.zha_01ddaf89_1_1029.state | int >= 50 and ((as_timestamp(now()) - as_timestamp(states.sensor.bathroom_motion_sensor.last_changed)) / 60 )| int > 15) }}"

EDIT: I just checked my motion sensor and the last_updated time is equal to the last_changed time (66 minutes ago) so that shouldn’t hurt anything.

So my question above still stands. Are you sure you are looking at the correct sensor?

It’s not much different than mine but either way that won’t help them as a condition because they want to have two different times for a trigger depending on humidity. So the time has to be in the trigger not the conditions.

as I suggested: create an intermediary template binary_sensor with this as value_template, and use that for trigger. makes it so much easier

Btw, it’s rather a fundamental difference using Timestamp or not …

Instead maybe you could just use the motion sensor trigger after five minutes, then in the action a wait_template for humidity to go below 50 with continue on timeout, another condition that the motion sensor is still off, and then turn off the light.

The sensor last update time was from different sensor yes but the fact remained the same. Now I had to do something different and suddenly the automation has indeed triggered. But the time window is really weird so the time is still wrong on that.

Is it normal that the

((as_timestamp(now()) - as_timestamp(states.sensor.bathroom_motion_sensor.last_changed))

gives Epoc time eventhough there is “as_timestamp”? Can HA compare Epoc time and normal date ok?

And I changed the last_changed to last_updated since that is the value the sensor has in its attributes. Does those differ in some way?

I can’t try change the code today but will try tomorrow.

Thank you all for your time, it is really great to get so much help :slight_smile:

yes, that’s a solid strategy :wink:

close to what Ive done in places where I need a timed sensor and don’t want to mess around with timestamps, but with actual timings.
Example (using my binary_sensor posted above)

  - alias: 'Switch Masterbed outlet when movement'
    id: 'Switch Masterbed outlet when movement'
#    initial_state: 'on'
    trigger:
      platform: state
      entity_id: sensor.master_bedroom_motion_sensor
      to: 'on'
    condition:
      - condition: template
        value_template: >
          {{ is_state ('sensor.activity_selection', 'Slapen')}}
      - condition: template
        value_template: >
          {{is_state('switch.master_bed_outlet', 'off')}}
    action:
      - service: switch.turn_on
        entity_id: switch.master_bed_outlet
#      - wait_template: >
#          {{as_timestamp(now()) | int - 
#            as_timestamp(states.sensor.master_bedroom_motion_sensor.last_changed) | default(0) | int > 120 }}
      - wait_template: >
          {{ is_state('binary_sensor.master_bedroom_motion_sensor_timed','off')}}
#          {{ is_state('sensor.master_bedroom_motion_sensor', 'off') }}
#      - delay:
#          minutes: 1
      - service: switch.turn_off
        entity_id: switch.master_bed_outlet
      - condition: template
        value_template: >
          {{ is_state('input_boolean.notify_system', 'on')}}
      - service: notify.notify
        data_template:
          message: >
            {{as_timestamp(now()) | timestamp_custom("%X") }}: You've walked safely lit. Signing off.

Ive left the comments and hashes for your information to see what has been moved to the intermediary sensor and how that simplifies the actual automation.

Of course, easy enough to add conditions for humidity or what have you.

If you don’t want an extra sensor, something like below is of course also possible, and again, just add the conditions you need.

Main design thinking is have a trigger without too many frills, and let the conditions decide whether action should take place. Don’t try to cram everything in a conditional trigger, or, and that might hamper the attempts here, try all conditions in one condition:

  - alias: 'Switch Bedside table off when room left'
    id: 'Switch Bedside table off when room left'
#    initial_state: 'on'
    trigger:
      platform: state
      entity_id: sensor.master_bedroom_motion_sensor
      to: 'off'
      for:
        minutes: 20
    condition:
      - condition: template
        value_template: >
          {{states('input_boolean.home_mode_vacation') != 'on'}}
      - condition: template
        value_template: >
          {{states('sensor.activity_selection') in ['Opstaan','Aan de slag']}}
      - condition: template
        value_template: >
          {{ is_state('light.master_bedroom', 'on')}}
    action:
      - service: light.turn_off
        entity_id: light.master_bedroom
      - service: switch.turn_off
        entity_id: switch.master_bed_outlet
      - delay:
          seconds: 10
      - service: switch.turn_off
        entity_id: switch.master_bedroom_motion_sensor_switch
      - condition: template
        value_template: >
          {{ is_state('input_boolean.notify_system', 'on')}}
      - service: notify.notify
        data_template:
          message: >
            {{as_timestamp(now()) | timestamp_custom("%X") }}: Bedside table and Master bed is switched off.

last thought for improvement maybe: as you can see I wrote the conditions in the form of states(‘entity_id’), and is_state(‘entity_id’,‘state’). This has several advantages:

  • easier to read
  • easier to debug
  • easier to test in the dev_template
    but most of all, less error prone and never a failing state if the state hasn’t been initialize yet, which happens often in the form of states.entity_id.state

yes that’s what “as_timestamp” does. It converts a date string (which is what now and last_changed are) to Epoch time which is in seconds since some date in the far past (12/31/69).

The template you put above is still in the Epoch time format since you haven’t converted it back to a time string.

So what we are doing in comparing the two times is converting both times into a timestamp that gives the number of seconds from the same reference. Then we subtract the two numbers which gives us the number of total seconds between the two. then we divide by 60 to get total minutes. then we compare that number to whatever the minutes are that we want to delay by.

No, but that’s not what we are doing. We are comparing two dates (now & last_changed) that have been converted to timestamps.

They might. That’s why I used last_changed since that will always be correct as far as I know.

last_changed isn’t an attribute. It’s available in the state machine tho. As far as I know you can’t see it unless you call it out in a template.

What do you mean by that? What is weird about it?

the only other thing that I forgot until today is that you have to now put in the initial state into the automation or it might be created in the disabled state. So if you use this complete automation then it should work as long as the sensors are all correct:

- alias: "Bathroom lights off when no motion"
  initial_state: 'on'
  trigger:
  - platform: template
    value_template: "{{ (states.sensor.bathroom_motion_sensor.state == 'off' and states.sensor.zha_01ddaf89_1_1029.state | int < 50 and ((as_timestamp(now()) - as_timestamp(states.sensor.bathroom_motion_sensor.last_changed)) / 60 )| int > 5) or (states.sensor.bathroom_motion_sensor.state == 'off' and states.sensor.zha_01ddaf89_1_1029.state | int >= 50 and ((as_timestamp(now()) - as_timestamp(states.sensor.bathroom_motion_sensor.last_changed)) / 60 )| int > 15) }}"
  condition:
    condition: time
    after: '07:00'
    before: '00:00'
  action:
    service: homeassistant.turn_off
    entity_id: light.bathroom

Not to confuse the issue any more it already seems to be for some reason but you could simplify the template a little by moving the motion sensor being off from the template to the condition section.

this really sounds so overly complicated. It might do the job in the end, but reads as turning 3 times left while turning right immediately would be so much cleaner.

if anything, you could write the template like below, to have more overview and structure, which aids in debugging and error-prevention:

  - platform: template
    value_template: >
      {{ (is_state('sensor.bathroom_motion_sensor','off') and 
          states('sensor.zha_01ddaf89_1_1029') | int < 50 and 
          now() - states.sensor.bathroom_motion_sensor.last_changed.total_seconds() > 5) or 

         (is_state('sensor.bathroom_motion_sensor','off') and 
          states('sensor.zha_01ddaf89_1_1029') | int >= 50 and 
          now() - (states.sensor.bathroom_motion_sensor.last_changed.total_seconds() > 15) }}

or even:

  - platform: template
    value_template: >
      {{ is_state('sensor.bathroom_motion_sensor','off') and 

         (states('sensor.zha_01ddaf89_1_1029') | int < 50 and 
          now() - states.sensor.bathroom_motion_sensor.last_changed.total_seconds() > 5) or 

          (states('sensor.zha_01ddaf89_1_1029') | int >= 50 and 
          now() - states.sensor.bathroom_motion_sensor.last_changed.total_seconds() > 15) }}

I didn’t know about this method. thanks for the information.

But when I stuck it in the template editor it wouldn’t render. I had to rewrite it like this to get it to work:

(now() - states.sensor.bathroom_motion_sensor.last_changed).total_seconds()

And the total seconds result still needs to be converted to minutes or the “>5” or “>15” doesn’t make any sense.

so the resulting time calculation really needs to be:

((now() - states.sensor.bathroom_motion_sensor.last_changed).total_seconds()) / 60)

And I honestly think that most templates sound overly complicated when you write them out in words.

Writing out the template your way still ends up sounding just as complicated:

So what we are doing in comparing the two times is we are taking two date-time strings and subtracting the two numbers from each other which results in the hours and minutes between the two. we then convert the result to seconds using the total_seconds method. we then convert the total seconds result to minutes and then compare that result to the desired delay time.

How many lefts is that? :wink:

indeed, a small typo, caused by the out-of-my-head write of this morning. Hardly a rewrite you had to do :wink:

tbh, I didn’t look at the logic, sorry for that. But if you want 15 minutes, you should multiply by 15, and not divide by 60… No idea what you are trying to do with your amended template, but if you want the passed time to be > 15 minutes you don’t want to divide the total seconds (which is what the template gives you) by 60.

try:
{{ (now() - states.sensor.bathroom_motion_sensor.last_changed).total_seconds() > 15*60 }}

tested just now:

my point in the first place…
that’s why Id prefer an intermediary binary_sensor as I suggested a few posts above.

but if you insist writing the template as you did, I’d really advise doing it in a manner you have all code in view, and don’t have to scroll along. And have some order in which you can find the correct comma’s, curly brackets and parentheses… That way even a complicated template can be read nicely, and make it more understandable

You do realize that you just did exactly the same thing I did but just changed where you did the math, right? In the end you still either have to convert seconds to minutes or minutes to seconds. I did the former, you did the latter.

But you still need a template either way. I just did it here where its actually being used instead of creating a sensor template in another location that you have to keep track of. Then if i dont need the automation any more i delete one thing and dont have to also remember to delete the then useless sensor.

I don’t see it as necessarily easier either way I just prefer it all in one location.

no I don’t, and its not true:

is not equal to:

the former divides the total_seconds by 60, the latter checks if the total_seconds is >15 minutes…

i was merely suggesting, please do as you please of course, by all means. What eer makes your clock tick.

Update, here is my whole code now

- alias: "Bathroom lights off when no motion"
  initial_state: 'on'
  trigger:
  - platform: template
    value_template: "{{ (states.sensor.bathroom_motion_sensor.state == 'off' and states.sensor.zha_01ddaf89_1_1029.state | int < 50 and (now() - states.sensor.bathroom_motion_sensor.last_changed).total_seconds() > 1*60) or (states.sensor.bathroom_motion_sensor.state == 'off' and states.sensor.zha_01ddaf89_1_1029.state | int >= 50 and (now() - states.sensor.bathroom_motion_sensor.last_changed).total_seconds() > 2*60) }}"
  action:
    service: homeassistant.turn_off
    entity_id: light.bathroom

But still the automation won’t trigger when time has passed (1 or 2 minutes in testing). Left side of the OR-statement is true and right side false when tested separately. The whole thing is true also.

Edit: Everything should be ok but the light won’t go off :frowning:
Edit2: Now the light did turn off, after 4 minutes and over 30 seconds…

Maybe it would be easier to put two different automations, other triggers when humidity > 50 and second one when humidity < 50