So after fully following the path of code for a trigger, I agree with @123’s findings. I’m not sure how @jwelter’s scenario even occurred.
Anyways if you are interested in the code behind this:
This trigger method
async def async_trigger(self, variables, skip_condition=False,
context=None):
"""Trigger automation.
This method is a coroutine.
"""
if not skip_condition and not self._cond_func(variables):
return
# Create a new context referring to the old context.
parent_id = None if context is None else context.id
trigger_context = Context(parent_id=parent_id)
self.async_set_context(trigger_context)
self.hass.bus.async_fire(EVENT_AUTOMATION_TRIGGERED, {
ATTR_NAME: self._name,
ATTR_ENTITY_ID: self.entity_id,
}, context=trigger_context)
await self._async_action(self.entity_id, variables, trigger_context)
self._last_triggered = utcnow()
await self.async_update_ha_state()
gets passed into this method (Through a series of other methods that I’m going to skip)
def async_track_point_in_utc_time(hass, action, point_in_time):
"""Add a listener that fires once after a specific point in UTC time."""
# Ensure point_in_time is UTC
point_in_time = dt_util.as_utc(point_in_time)
@callback
def point_in_time_listener(event):
"""Listen for matching time_changed events."""
now = event.data[ATTR_NOW]
if now < point_in_time or hasattr(point_in_time_listener, 'run'):
return
# Set variable so that we will never run twice.
# Because the event bus might have to wait till a thread comes
# available to execute this listener it might occur that the
# listener gets lined up twice to be executed. This will make
# sure the second time it does nothing.
point_in_time_listener.run = True
async_unsub()
hass.async_run_job(action, now)
async_unsub = hass.bus.async_listen(EVENT_TIME_CHANGED,
point_in_time_listener)
return async_unsub
What should be noted in async_track_point_in_utc_time is that the action
in hass.async_run_job(action, now)
is executed after the point in time listener. And the action contains the conditional check.
Basically, this callback doesn’t return until the time limit has finished. When that occurs the top method is executed and the first thing checked is the condition.
So all in all, my post is not correct. The correct flow is:
non-for trigger flow:
trigger -> condition -> action
for trigger flow:
trigger -> wait for duration -> condition -> action