What is the purpose of the conditions block in automations?

The crux of my question is why does HA split automation into three sections:

  • Triggers
  • Conditions
  • Automations

Here is a snippet from a test automation:

conditions:
  - condition: state
    entity_id: binary_sensor.living_main_ir_occupancy
    state:
      - "on"
actions:
  - condition: state
    entity_id: binary_sensor.living_main_ir_occupancy
    state:
      - "on"
  - action: light.toggle
    target:
      device_id: 1804a38d2c38e193812a609371fa9717

As you can see I am repeating the same condition in both the conditions section and the actions section.

As far as I can tell the only difference to placing the condition in the conditions section vs putting it as the first action is that when you run the automation manually - using the menu option “Run Actions” any conditions in the conditions block is skipped/not checked, where as “action” conditions are always checked.

Is there any other scenario where the location of the check has an effect?

The condition section houses conditions that will let stop the automation from doing anything.
This is the preferred method of preventing the automation from acting rather that turning the automation on and off which is not recommended.

The conditions in the action section are to control specific actions., as opposed to controlling all at once.

But putting a condition as the first action does the same thing…


However in doing some experimentation, I have found a better answer:

The conditions block is evaluated at a different time to a condition set as the first action.
The easiest way to see this is if your automation is in queued mode.

For the first execution of your script, you probably won’t notice a difference
however if the first execution takes some time and a second trigger happens:

  • Any conditions in the conditions block are evaluated immediately after the trigger fires.
    • If the condition passes the action is queued.
    • If the condition fails the entire execution is dropped.
  • In contrast if the condition is the first one in the actions block the execution is always queued.
    • The condition is only evaluated once the script starts executing.

Therefore if your script checks that value of something that it also modifies, it is better to place your condition in the action block, so that you see the results of the first execution.

Edit:
Any variables you declare in the top level variables section are also evaluated/captured at the time the trigger fires - so if you want to memorize / use the state as of the time the trigger fires, you can do something like:

  test_state: "{{ states('input_boolean.mytest') }}"

No. That only controls the first action.

Here is my test case, it is similar to the first case I posted:

alias: TestB
mode: single
description: ""
triggers: []
conditions: []
actions:
  - condition: template
    value_template: "{{ false }}"
  - action: light.toggle
    target:
      entity_id: light.living_side
  - action: light.toggle
    target:
      entity_id:
        - light.dining_ceiling_1

The condition prevents all subsequent actions from executing, not just the first one.

I use conditions in the action part as a show stopper if I manually trigger an automation.
The condition is if this automation is enabled.

It might seem odd but these automations are part of my ESL tags. And sometimes the hub stops working and all of the tags go out of sync.
So I have an automation that triggers when the hub stops working and reboots it, then it triggers all ESL automations manually.
And if I have one automation that is disabled it will still run so using a condition to know if I want it to run or not is quite handy

1 Like

There is (was) one typical use-case, it is that you cannot AND triggers, unless you use templates. Triggers are implicitly OR.

So if you want to execute an automation if entities A and B are both true, you would have to trigger on both A and B states, then do an AND condition.

1 Like

You can think of the first conditions section as global conditions to check to control whether the automations run the actions AT ALL.

If the conditions fail (return all false) then the actions section of the automation won’t even be run. And I also believe that the trace will show the automation as not being run but I could be wrong on that as things have changed and I honestly don’t remember which is which now.

conditions in the actions section control the actions sequencing.

in your very simplistic example I agree that there is no technical difference in the result of the automation. But that’s not the intended use case for allowing conditions in the actions section.

Let’s look at a very slightly more complex example.

conditions:
  - condition: state
    entity_id: binary_sensor.living_main_ir_occupancy
    state:
      - "on"
actions:
  - action: light.toggle
    target:
      device_id: 1804a38d2c38e193812a609371fa9717
  - condition: state
    entity_id: binary_sensor.some_other_sensor
    state:
      - "off"
  - action: light.toggle
    target:
      device_id: 123456

in this case there is a functional difference between the three automations.

in your automation with the condition in the conditions section if the condition fails no actions will run. the automation never got started.

in your automation with the condition in the actions section the actions will run but the first action is to check the condition. if it fails no further actions are run. the automation stops.

in my automation the condition in the conditions section will act exactly as your first one.

But in my automation if the first condition in the conditions section passes then the actions will run. it tyhen performs the first action (toggle your light). then it checks the condition. if it fails then the next action isn’t run. the automation stops. If the condition passes then the automation continues running actions.

you can also use conditions to conditionally control the execution of different sets of actions.

It’s all about how you want to control the execution and flow of the automation.

2 Likes

One other difference is that an automation that stops at the Conditions block will not update its last_triggered attribute. For most cases this will have no consequence, but it’s useful if you want to throttle an automation. In order for the following to work, it and any other general conditions need to be in the Conditions block:

alias: Don't execute action more often than once every 5 minutes
condition: template
value_template: |
 {{ now() >= this.attributes.last_triggered | default(as_datetime(0),1)
 + timedelta(minutes = 5) }}
1 Like

That is useful to know - thank you.

I believe the conditions in the block are “anded” together, so it’s sufficient for any of them to return false.

The combination of these two points is that there is extra processing that HA does after (at least) one trigger fires and all the conditions are met.

If any of the conditions are not met - it’s almost as if the trigger didn’t happen at all - at least that’s what it looks like, thank you.

I believe you would get the same result if you just placed your conditions (A and B) as the first two actions, at least from a functional standpoint.

As the other responses have identified the metadata effects (such as last_triggered) are controlled by the conditions block (so that would be different).

Yeah I wrote that kind of clumsily.

First I’m not you if you realize it but not all conditions in the conditions section are ‘and’. There are also ‘or’ conditions that can be used there too.

So yes, any false condition in an ‘and’ condition block or all conditions in an ‘or’ condition block (or any combination of those) will cause the conditions section to fail. Which is what I was trying to say but failed.

yes, the extra processing is that it will execute the actions section.

That goes back to the difference between the global conditions section and the local condition used in the actions section for executing action flow.

I don’t see any point in debating the distinction if there is no difference unless you care about those other limited use aspects. (sorry not trying to argumentative so I apologize if comes across that way)

Most people just use the conditions section as intended. I don’t see any benefit to not using it like that except again under very limited use cases that very few people need.

If the automation’s triggers section fires but does not pass conditions then it will stop and record a trace but not update its last_triggered value because it is not deemed to have been “officially triggered”.

It’s unfortunate that the word “triggered” is used in last_triggered because it implies it only takes one of the automation’s triggers to fire in order to update last_triggered … but that’s not the case. That distinction is held exclusively by an automation that reaches the stage of executing its actions section.

So where you choose to locate your conditions depends on your application. Generally speaking, if I want to allow execution of actions only under the right circumstances then I use the conditions section.

Yeah, IMO there should a separate last_triggered and last_completed (or processed or similar worded).

Frankly that was the purpose of the thread - if there genuinely wasn’t a difference my follow up would have been - why not remove the conditions block. When I was first learning HA it did confuse me why they were necessary.

Each of the distinctions has added an extra tool to the toolbox - there may be cases where they are needed.

Edit: Removed block about traces - I didn’t verify that myself.

An automation that triggers but stops in the Conditions block still produces a Trace. Here’s one from an automation that stopped upon failing the first condition in the Conditions block:

You know, I always felt like that was kind of a bug. I know it’s documented but just feels wrong outside of a conditional block.

Plus, there’s also the stop action to make it explicit inside a control block, although I’m not sure of a case where that is needed vs just using a block.

As in your example, the second action doesn’t run:

actions:
  - condition: template
    value_template: "{{ false }}"
  - action: system_log.write
    data:
      message: Ran second action

But, this one does:

actions:
  - parallel:
      - condition: template
        value_template: "{{ false }}"
      - action: system_log.write
        data:
          message: Ran second action

I guess it’s syntactic sugar, but makes the behavior a bit unclear without a block for grouping the actions.

I have been burnt by the behavior of conditions in the past too.
My current mental model for them is that they are roughly similar to a:

if (...) {
    // No-op
} else
    continue;

Block in other programming languages, except that you don’t have to be inside a looping construct to use them (any sequence block will do). In that:

  • If the condition is true they do nothing.
  • If the condition is false they skip over the remainder of the containing sequence (note: the docs specifically mention sequences - I haven’t tested parallel blocks).

I see the stop as being analogous to a return in other programming languages, I see the value in being able to completely stop an automation when heavily nested, however in practice I haven’t had a need for the stop action, as I try to avoid heavy nesting.