Post the full automation, please, or at least the full trigger and condition blocks.
Is there a way to tell? The trace merely shows that the condition failed, not every step in the calculation.
Sure:
alias: Dehumidifier Pump Control
trigger:
- at: input_datetime.dehumidifier_pump_pause_until
platform: time
- entity_id: sensor.151732605496952_tank
platform: state
- entity_id: input_number.dehumidifier_pump_threshold
platform: state
- event: start
platform: homeassistant
condition:
- condition: template
value_template: |-
{{now().timestamp() >=
state_attr('input_datetime.dehumidifier_pump_pause_until', 'timestamp')}}
action:
- if:
- above: input_number.dehumidifier_pump_threshold
condition: numeric_state
entity_id: sensor.151732605496952_tank
then:
if:
condition: state
entity_id: switch.dehumidifier_pump_switch
state: "off"
then:
- service: switch.turn_on
target:
entity_id: switch.dehumidifier_pump_switch
- data:
timestamp: "{{ now().timestamp() + 1*60 }}"
service: input_datetime.set_datetime
target:
entity_id: input_datetime.dehumidifier_pump_pause_until
else:
if:
condition: state
entity_id: switch.dehumidifier_pump_switch
state: "on"
then:
- service: switch.turn_off
target:
entity_id: switch.dehumidifier_pump_switch
- data:
timestamp: "{{ now().timestamp() + 10*60 }}"
service: input_datetime.set_datetime
target:
entity_id: input_datetime.dehumidifier_pump_pause_until
(I have reformatted this slightly. For some reason the YAML copy/paste had the if/then/else out of order. I know it doesn’t technically make a difference, but it certainly makes it more difficult to read…)
Note that this is not the only automation that adjusts input_datetime.dehumidifier_pump_pause_until
.
Add variables before the condition and store the two sides in separate variables. This should show in traces.
I changed the automation to:
condition:
- condition: or
conditions:
- condition: template
value_template: >-
{{now().timestamp() >=
state_attr('input_datetime.dehumidifier_pump_pause_until',
'timestamp')}}
- condition: template
value_template: "{{trigger.platform == 'time'}}"
…which works around the behavior while making it visible in the trace if this behavior occurs.
By manually doing a service call to update pause_until I’m able to trigger this behavior:
service: input_datetime.set_datetime
data:
timestamp: "{{ now().timestamp() + 1 }}"
target:
entity_id: input_datetime.dehumidifier_pump_pause_until
Will do…
I changed it to the following:
condition:
- condition: or
conditions:
- condition: template
value_template: >-
{% set now_timestamp = now().timestamp() %}
{% set pause_timestamp =
state_attr('input_datetime.dehumidifier_pump_pause_time', 'timestamp')
%}
{{ now_timestamp >= pause_timestamp }}
- condition: template
value_template: "{{trigger.platform == 'time'}}"
…but the trace does not have any information about the variables.
Am I misunderstanding something?
If you mean adding a variables action - I can’t do so because this is the condition block of the automation, which is run before any actions.
I can try turning the condition block into an initial if/else return
in the automation actions I suppose, though that’s a more major change to the automation.
Suggestions?
No, not the action, just add variables before condition section. You have to do it through YAML, there’s no UI for it.
I think what Artur meant was something like:
condition:
- variables:
now_timestamp: "{{ now().timestamp() }}"
pause_timestamp: "{{state_attr('input_datetime.dehumidifier_pump_pause_time', 'timestamp') }}"
- condition: or
conditions:
- condition: template
value_template: >-
{{ now_timestamp >= pause_timestamp }}
- condition: template
value_template: "{{trigger.platform == 'time'}}"
Oh interesting!
I didn’t know you could define variables in the condition block. This appears to be undocumented. Is it something I can rely on, or should I just use it for debugging?
I’ll try that and get back to you.
I’m not sure this is correct. I meant like that:
variables:
now_timestamp: "{{ now().timestamp() }}"
pause_timestamp: "{{state_attr('input_datetime.dehumidifier_pump_pause_time', 'timestamp') }}"
condition:
- condition: or
conditions:
- condition: template
value_template: >-
{{ now_timestamp >= pause_timestamp }}
- condition: template
value_template: "{{trigger.platform == 'time'}}"
Please make sure to read a thread you’re commenting before saying something unneeded. Thank you.
I ran a few tests. What I’ve found is that the time trigger ignores fractions of seconds and will fire as soon as possible after the integer seconds are matching now()
Here’s an example where I set the trigger time to a timestamp that ended in 0.99 seconds. In my automation I rendered now()
and set it to a variable to see what it displayed. Here are the results:
input_timestamp: 1724988756.99
now_timestamp: 1724988756.001632
I tried this many times. The value for now()
was always between 0.001 to 0.002 seconds after the integer value of the desired time trigger. It made no difference what fractions of seconds were specified on the entity I used to trigger. I’d expect the 0.001-0.002 delay to change depending on how fast your host hardware is and what other processes are currently running. But the takeaway is that the trigger clearly ignores fractions of a second.
Therefore, comparing now()
to the desired trigger time is a bad idea unless you remove the fractions of seconds from the trigger time.
It only reports the result of the entire template. Converting the Jinja2 variables to script variables will report their values in the trace, if you want more visibility, but it won’t ultimately change the template’s result.
The issue you’re occasionally encountering might be due to how fractional seconds are handled (or not handled) by the trigger. What is the smallest unit of time in the input_datetime’s value? Minutes or seconds?
EDIT
Ninja’d by meckaneck
Agreed about the unexpected place. That being said, that seems to be documenting script-global variables, not condition-scope variables. I can’t find any documentation on condition-scope variables…
Actually now that I try it it condition-scope variables don’t appear to work (or I have the syntax wrong):
condition:
- variables:
now_timestamp: "{{now().timestamp()}}"
pause_timestamp: >-
{{state_attr('input_datetime.dehumidifier_pump_pause_time',
'timestamp')}}
- condition: or
conditions:
- condition: template
value_template: "{{now_timestamp >= pause_timestamp}}"
- condition: template
value_template: "{{trigger.platform == 'time'}}"
Automation is unavailable: Unexpected value for condition: ‘None’. Expected and, device, not, numeric_state, or, state, sun, template, time, trigger, zone @ data[0]
Moving the variables to script-global does work:
variables:
now_timestamp: "{{now().timestamp()}}"
pause_timestamp: >-
{{state_attr('input_datetime.dehumidifier_pump_pause_time',
'timestamp')}}
condition:
- condition: or
conditions:
- condition: template
value_template: "{{now_timestamp >= pause_timestamp}}"
- condition: template
value_template: "{{trigger.platform == 'time'}}"
…however I’d be concerned with this approach with ordering (are variables defined in this manner guaranteed to be evaluated after the trigger?)
Aha: great insight! That matches what I’m seeing with script-global variables:
now_timestamp: 1725023103.001939
pause_timestamp: 1725023103.99
And that explains the observed bug quite nicely; thank you for chasing that down. Anything that was setting the input_datetime to an integer # of seconds would end up “fine” - which some but not all changes to the pause time were doing. Hence the inconsistency. Time for a bug report on github?
The more I look at it, the more I think this is a bug. Looking at the code here, there is a separate logic chain depending on if the time trigger is an input_datetime
compared to a sensor
with a timestamp
device class.
When the trigger is a sensor
, the listener is set to the sensor’s state, which carries along with it fractions of a second, if any.
When the trigger is an input_datetime
only the hours, minutes, and seconds are used to set the listener. My guess is that was done because, via the UI, an input_datetime
can only be set to 1-second resolution. I think it was overlooked that an input_datetime
can contain microsecond accuracy if set by an action call.
Maybe I have misread the code but the smallest unit of time monitored by a Time Trigger appears to be seconds (i.e. the clock’s minimum resolution is 1 second). Fractional seconds are ignored.
Sort of like if its resolution was hours, then fractional hours would be ignored so it would trigger at 13:00 for a sensor or input_datetime’s value of 13:15.
In other words, it’s working exactly as designed. To accommodate fractional seconds, it would need to reduce the clock resolution to milliseconds or microseconds … and I doubt that would fly in an Architecture discussion.
You don’t need infinite precision for this.
Just do what most other timers do when presented with a target time, and wake up a) no earlier than the target time and b) as close to the target as possible.
a) is the key here.
This matches e.g. what Linux does (interrupted syscalls notwithstanding).
If you’re planning to make that change, to the Time Trigger’s underlying code, you can propose it in the Architecture repository. If accepted, you can proceed to submit a Pull Request in the Core repository.
Otherwise you can create a Feature Request in the community forum and hope a volunteer developer agrees with your proposal and takes on the project.
I’m seeing that fractional seconds work in a time trigger already, as long as it uses a sensor entity (must be sensor
domain with timestamp
device class) that has a state that does include fractional seconds. Which, they normally don’t, because the code for a sensor with device class timestamp
strips fractional seconds also.
But if you override a sensor
entity’s state (via developer tools → states), it IS possible to get fractional seconds in a sensor entity. So I did that, using a datetime that ends with 0.9 seconds and when the automation fired, now()
rendered 0.0033 seconds after the intended trigger:
now_timestamp: 1725041230.903311
trigger_timestamp: 1725041230.9
So, it DID trigger at 0.9 seconds, it was just fairly difficult to get all the right things in place.
I agree this warrants an architecture discussion. There are a few things that should change if fractional seconds are desired to be supported:
- Update time triggers to use microseconds when an
input_datetime
entity is selected - Update
input_datetime
helpers to allow specifying them with microseconds, both in yaml and the UI. They can already hold microseconds if set via an action, although it is only in the timestamp attribute. The state itself does not reflect microseconds. - Update
timestamp
sensors to not strip off microseconds
If the decision is made to not support microseconds in sensors or triggers, then the following should change:
- update
input_datetime
helpers so that microseconds are stripped off when using theinput_datetime.set_datetime
action - Update the state of buttons and events so that they don’t use microseconds (for consistency across all entities)
I’ll probably make a post later if nobody else gets to it before then.