Comparison of an input_datetime and now() - can this be achieved simply?

Hey everyone,
I do have a question of this kind.

So I have a templated sensor that takes the value of a temp sensor from, say, 8am to 8pm, and then the value of another the rest of the time. Actually, the 8am and 8pm values are taken from 2 time inputs.

Until today, I was doing something like this (with added “debugging” code):

{% set n = now().time() %}
{% set b = now().fromtimestamp(state_attr('input_datetime.heater_target_rooms', 'timestamp')).time() %}
{% set e = now().fromtimestamp(state_attr('input_datetime.heater_target_living', 'timestamp')).time() %}
{% if (b >= e and (n >= b or n < e)) or (b < e and (n >= b and n < e)) %}
  {{ states.sensor.temperature_1.state }}
{% else %}
  {{ states.sensor.temperature_2.state }}
{% endif %}

{{ n }}
{{ states('input_datetime.heater_target_rooms') }} -> {{ b }}
{{ states('input_datetime.heater_target_living') }} -> {{ e }}

Which outputs the following:

20.9

23:32:00.002545
21:00:00 -> 22:00:00
06:45:00 -> 07:45:00

We can see I had a problem with an offset hour being added to my target times. Now, I do understand this is due to not passing the timezone argument when calling “fromtimestamp” (I’m ashamed to say I did not get how to do that from a template). Reading this thread, I convinced myself this was unnecessarily complex and tried to replace now() with the time sensor I’m already using for automations.

This leads to this code, which is indeed simpler and actually gives the correct result:

{% set n = states('sensor.time') %}
{% set b = states('input_datetime.heater_target_rooms') %}
{% set e = states('input_datetime.heater_target_living') %}
{% if (b >= e and (n >= b or n < e)) or (b < e and (n >= b and n < e)) %}
  {{ states.sensor.temperature_1.state }}
{% else %}
  {{ states.sensor.temperature_2.state }}
{% endif %}

All should be well, but I’m a bit uneasy, because I’ve got the feeling I’m now comparing strings instead of comparing times. I know said strings include leading zeros so string comparison should work fine, but it still feels a bit off to me. Should I indeed worry or should I just stop obsessing with this and happily go back to trying to understand how the whole media_player thing is supposed to let me play something to a local bluetooth sink ?

Anyways, thanks a lot for HA, which is indeed an amazing piece of software, particularly combined with the myriad other open source software that it plays nice with. The HA crowd is amazing :heart_eyes:

hi,
is this correct please?

{{ states('input_datetime.irepair_opened_at') < today_at("09:00").strftime('%Y-%m-%d') }}

Without an explanation of what you are trying to do, it is hard to say if your template is correct. However, the way you have constructed it ( with the strftime() ), your template will not use the time information at all, it will only use the date.

If your goal is to test if the Input Datetime is before 9AM today use:

{{ states('input_datetime.irepair_opened_at') | as_datetime | as_local < today_at("09:00") }}
1 Like

thank you very much for your response!
I wanted to create an automation that will test if the input datetime is after 9AM …

The code bellow didn’t return an error when tested it at /config/templates

{{ states('input_datetime.irepair_opened_at') > today_at("09:00").strftime('%Y-%m-%d') }}

It returned false (which is correct) as my input_datetime the time I was testing was:

εικόνα

However your suggestion gives an error when test it

{{ states('input_datetime.irepair_opened_at') | as_datetime | as_local < today_at("09:00") }}

gives

AttributeError: 'NoneType' object has no attribute 'tzinfo'

So what do you think?

From your screenshot, input_datetime.irepair_opened_at contains time but no date. Therefore this should work:

{{ today_at(states('input_datetime.irepair_opened_at')) < today_at('09:00') }} 
1 Like

The error is due to the fact that your Input Datetime is time-only, and I assumed a time & date Input Datetime…

Even though your template doesn’t throw an error, it will never work for what you are trying to do.

{{ states('input_datetime.irepair_opened_at')  >  today_at("09:00").strftime('%Y-%m-%d') }}

Taking the value from your screen shot, the above resolves to: 08:59:00 > 2022-02-09 which will be False. It will give seemingly inconsistent results because it is actually only comparing the leading digit of each side of the comparison (in this example: 0 > 2).
It will be False if the Input Datetime is set anywhere between 00:00-19:59 and True for 20:00-23:59.

To test if your Input Datetime is after 9AM, you need to tell it what day you’re interested in… since you are only interested in the time value, this is a same-day comparison and you can use:

{{ today_at(states('input_datetime.irepair_opened_at')) > today_at('09:00') }} 
1 Like

thank you both!
Because I wanted to test if input Datetime is after 9AM I used

{{ today_at(states('input_datetime.irepair_opened_at')) > today_at('09:00') }} 

It works!

OK. That was unclear because in your first post the template tested if it was before 9AM.

1 Like

thank you @123
I really apreciate it as you helped me out several times at the past. (with various templates)

Would you please take the time and tell me why this is not working. It is related to time comparison ??

{{ as_timestamp(now()) - as_timestamp(states.binary_sensor.happy.last_changed) < 15 }}

What I want to achieve is to return true (ON) when previous state was OFF for 15 seconds

To be more specific I have a binary sensor which turns ON several times.
I am using a helper and I count the ON states with a number increment.

I don’t want to count if the button is pressed again too fast (less than 15 seconds)
Here is my automation

alias: TEST FEEDBACK SENSOR
description: ''
trigger:
  - platform: state
    entity_id: binary_sensor.happy
    to: 'on'
condition:
  - condition: template
    value_template: >-
      {{ as_timestamp(now()) -
      as_timestamp(states.binary_sensor.happy.last_changed) < 15 }}
action:
  - service: input_number.increment
    data: {}
    target:
      entity_id: input_number.feedback_sensor
mode: single

This will report true if the binary_sensor’s last_changed value is less than 15 seconds ago.

{{ now() - states.binary_sensor.happy.last_changed < timedelta(seconds=15) }}

I think I want the opposite…
count +1 if state is ON and condition is previous state off for more than 15 seconds

if I press button once, count and then count again only if it has stay off for more than 15 seconds (and pressed again ofcourse)

does it make sense?

So not like the template you posted?

I want the condition to come true if it has been OFF for +15 seconds.

@123 , I had a second thought and maybe I wasn’t clear enough …again :slight_smile:
So here is the full story so you can understand better

I’ve created a binary sensor at esphome

binary_sensor:
  - platform: gpio
    pin:
      number: D1
      mode: INPUT_PULLUP
      inverted: True
    name: "happy"

Each time I press a button, it goes to ON.
What I want is to count the ON states.

Which I have achieved with the following automation :

alias: TEST FEEDBACK SENSOR
description: ''
trigger:
  - platform: state
    entity_id: binary_sensor.happy
    to: 'on'
condition:
action:
  - service: input_number.increment
    data: {}
    target:
      entity_id: input_number.feedback_sensor_happy
mode: single

It works fine.
My problem is that you can press the button constantly and this will add +1 as the state keeps changing to ON which is something I want to avoid.

So the condition I want is :
You press the button once then add increment +1
If you press again or constantly add nothing .

if you press again after 15 seconds add another +1 and wait again for 15 ‘’

Your example is not wrong but when I press the button it returns to false so the automation can’t proceed.

hope it makes sense now

after a bit of search I found what I was looking for my condition

 {{ (now() | as_timestamp) - (trigger.from_state.last_changed | as_timestamp) > 15 }}

It works exactly like I wanted it!
thank you for your time

{{ now() - trigger.from_state.last_changed > timedelta(seconds=15) }}

FWIW, it’s the same suggestion I offered here (to check if the binary_sensor’s state changed more than X seconds ago):

1 Like

@123 Do you know if I can achieve the same directly to esphome ??

I have a button attached to a binary sensor and I was thinking of creating a template switch that will check if the binary_Sensor’s state changed more than X seconds to report ON.

I am unfamiliar with ESPHome. Hopefully someone else can assist you.

In ESPHome you can have your button run a script with a 15 second delay after it increments the couter. (Automations and Templates — ESPHome)

thank you very much for your response. Would you please provide an example of how to use it ?
What the script will do? Why do I have to create a counter?