Mastering Advanced Automation Flow Control: Building State-Aware Automations in Home Assistant

0
0
  • #Home_Assistant
  • #Automation
  • #Smart_Home
  • #Advanced
  • #Flow_Control
  • #YAML
Represent Mastering Advanced Automation Flow Control: Building State-Aware Automations in Home Assistant article
6m read

Mastering Advanced Automation Flow Control: Building State-Aware Automations in Home Assistant

Home Assistant excels at making your home smarter, but often, the automations we craft start simple: "If X happens, then do Y." While effective for basic tasks, real-world scenarios in a smart home are rarely linear. What if you need your automation to wait for a specific condition before proceeding? Or execute different actions based on a multitude of factors? What if you need to repeat a sequence until a certain state is achieved? This is where mastering Home Assistant's advanced automation flow control comes into play.

By leveraging powerful built-in actions like wait_for_trigger, choose, and repeat, alongside the judicious use of variables, you can move beyond rigid "if-then" statements to create truly intelligent, state-aware automations that adapt dynamically to your home's conditions. This guide will equip you with the knowledge to build resilient and sophisticated smart home logic.

The Power of wait_for_trigger: Pausing for Purpose

Imagine turning on a light when motion is detected, but you only want it to turn off once there's been no motion for five minutes AND a door is closed. A simple "turn off after 5 minutes of no motion" automation might leave the light on if the door is still open. wait_for_trigger solves this by pausing the automation's execution until a specified trigger fires.

How it works: When an automation reaches a wait_for_trigger step, it halts. It will only resume when one of its defined triggers occurs, or if a specified timeout is reached.

Use Cases:

  • Waiting for a door to close before locking it.
  • Pausing a routine until a device reports a specific state (e.g., "washing machine finished").
  • Waiting for a user to confirm an action via a notification.

Example: Turn off light only when no motion AND door closed

automation:
  - alias: "Turn on light with motion"
    trigger:
      - platform: state
        entity_id: binary_sensor.motion_sensor
        to: "on"
    action:
      - service: light.turn_on
        target:
          entity_id: light.hallway_light
      - wait_for_trigger:
          - platform: state
            entity_id: binary_sensor.motion_sensor
            to: "off"
            for: "00:05:00"
          - platform: state
            entity_id: binary_sensor.hallway_door
            to: "off" # Assuming 'off' means closed
        timeout: "00:10:00" # Max wait time before continuing
        continue_on_timeout: false # If timeout, do NOT proceed
      - service: light.turn_off
        target:
          entity_id: light.hallway_light

Best Practices for wait_for_trigger:

  • Always use timeout: Prevents automations from getting stuck indefinitely if the trigger never fires.
  • Consider continue_on_timeout: Set to false if the subsequent actions should only occur if the trigger was successful. Set to true if you want the automation to proceed even if it timed out (e.g., to log an error).
  • Multiple Triggers: You can define multiple triggers within wait_for_trigger; the automation will resume when *any* of them fire.

Conditional Logic with choose: Dynamic Branching

Traditional automations often rely on multiple separate automations or complex if/else conditions within templates. The choose action provides a clean, structured way to execute different sequences of actions based on a set of conditions, similar to a switch-case statement in programming.

How it works:

The choose action evaluates a list of conditions sequentially. When the first condition that evaluates to true is found, its associated actions are executed, and the choose block completes. If no conditions are met, the optional default actions are executed.

Use Cases:

  • Executing different lighting scenes based on the time of day.
  • Responding differently to a button press depending on the current state of another device.
  • Sending specific notifications based on who is home.

Example: Different light actions based on time

action:
  - choose:
      - conditions:
          - condition: time
            after: "06:00:00"
            before: "12:00:00"
        sequence:
          - service: light.turn_on
            target:
              entity_id: light.living_room
            data:
              brightness_pct: 70
              color_temp: 4000 # Cool white
      - conditions:
          - condition: time
            after: "18:00:00"
            before: "23:00:00"
        sequence:
          - service: light.turn_on
            target:
              entity_id: light.living_room
            data:
              brightness_pct: 50
              color_temp: 2700 # Warm white
    default:
      - service: light.turn_off
        target:
          entity_id: light.living_room

Best Practices for choose:

  • Order Matters: Conditions are evaluated top-down. Ensure more specific conditions appear before more general ones if there's overlap.
  • Utilize default: Always consider a default block for fallback actions or error handling if none of your specific conditions are met.
  • Clarity: choose makes complex conditional logic much more readable than nested if statements within templates.

Iterative Actions with repeat: Looping for Efficiency

Sometimes you need to perform an action multiple times, or until a certain condition is met. The repeat action allows you to loop through a sequence of actions.

How it works:

repeat offers four types of looping:

  • count: Repeat a fixed number of times.
  • while: Repeat as long as a condition remains true.
  • until: Repeat until a condition becomes true.
  • for_each: Repeat for each item in a list (e.g., a list of entities).

Use Cases:

  • Flashing a light multiple times for an alert.
  • Gradually dimming lights until they are off.
  • Checking the status of several devices in sequence.

Example: Flash a light 5 times

action:
  - repeat:
      count: 5
      sequence:
        - service: light.turn_on
          target:
            entity_id: light.warning_light
        - delay: "00:00:00.500" # 500 milliseconds
        - service: light.turn_off
          target:
            entity_id: light.warning_light
        - delay: "00:00:00.500"

Best Practices for repeat:

  • Prevent Infinite Loops: For while and until loops, ensure there's a mechanism for the condition to eventually change, or include a timeout.
  • Variables with for_each: When using for_each, you can access the current item in the loop using repeat.item, which is incredibly powerful for dynamic entity manipulation.
  • Delay within loops: Be mindful of rapid-fire service calls. Add small delays if interacting with physical devices to avoid overwhelming them or the network.

Managing Delays and Timeouts

While delay is straightforward, wait_for_trigger with its timeout parameter offers a more robust way to manage time in automations. A simple delay pauses execution for a fixed duration, regardless of external events. A wait_for_trigger with a timeout, however, is responsive: it waits *up to* the timeout, but proceeds immediately if the trigger fires sooner.

# Simple delay
action:
  - service: light.turn_on
    target:
      entity_id: light.porch_light
  - delay: "00:05:00" # Wait 5 minutes, then turn off
  - service: light.turn_off
    target:
      entity_id: light.porch_light

Dynamic State Management with Variables

Home Assistant automations allow you to define variables within a sequence or script. These variables are local to that specific automation run and can be used to store temporary values, making your logic more dynamic and cleaner than relying solely on helper entities.

action:
  - variables:
      current_brightness: "{{ state_attr('light.living_room', 'brightness') | default(0) }}"
      desired_brightness: "{{ iif(now().hour < 12, 180, 255) }}" # Different brightness based on time
  - service: light.turn_on
    target:
      entity_id: light.living_room
    data:
      brightness: "{{ desired_brightness }}"

Putting It All Together: A Complex "Good Night" Routine

Let's craft a more intelligent "Good Night" automation that ensures doors are closed, turns off lights, and arms the alarm, with user interaction.

alias: "Intelligent Good Night Routine"
description: "Ensures home is secure before arming alarm and turning off lights."
trigger:
  - platform: state
    entity_id: input_boolean.good_night_button
    to: "on"
action:
  - variables:
      open_doors_windows: "{{ expand('group.all_doors_windows') | selectattr('state', 'eq', 'on') | map(attribute='name') | list }}"
  - choose:
      - conditions: "{{ open_doors_windows | length > 0 }}"
        sequence:
          - service: notify.mobile_app_your_phone
            data:
              message: "Warning: {{ open_doors_windows | join(', ') }} still open. Close to continue."
              data:
                actions:
                  - action: "good_night_override"
                    title: "Override & Continue"
          - wait_for_trigger:
              - platform: event
                event_type: mobile_app_notification_action
                event_data:
                  action: "good_night_override"
              - platform: template
                value_template: "{{ expand('group.all_doors_windows') | selectattr('state', 'eq', 'off') | list | length == expand('group.all_doors_windows') | list | length }}"
            timeout: "00:05:00"
            continue_on_timeout: false
          - choose:
              - conditions:
                  - condition: template
                    value_template: "{{ wait.trigger.platform == 'event' and wait.trigger.event.event_data.action == 'good_night_override' }}"
                sequence:
                  - service: persistent_notification.create
                    data:
                      message: "Good Night: Override activated. Proceeding with open sensors."
              - conditions:
                  - condition: template
                    value_template: "{{ wait.completed == false }}" # If timeout occurred
                sequence:
                  - service: persistent_notification.create
                    data:
                      message: "Good Night: Timeout. Action cancelled due to open sensors."
                  - stop: "Automation cancelled."
    default:
      - service: persistent_notification.create
        data:
          message: "All clear. Proceeding with Good Night routine."
  - service: light.turn_off
    target:
      area_id: "all"
  - service: media_player.turn_off
    target:
      area_id: "all"
  - service: alarm_control_panel.alarm_arm_home
    target:
      entity_id: alarm_control_panel.home_alarm
mode: single

This example demonstrates:

  • Using a variable to store open doors/windows.
  • choose for conditional branching based on the state of doors/windows.
  • wait_for_trigger to wait for either all sensors to close OR a user to override via a notification action.
  • Another choose to react differently based on how the wait_for_trigger concluded.
  • stop action to halt the automation if conditions aren't met or timeout occurs without override.

Best Practices for Reliable Advanced Automations

  • Test Incrementally: Build complex automations piece by piece. Test each wait_for_trigger, choose, or repeat block in isolation before combining them.
  • Use Scripts for Reusability: If a sequence of actions is used in multiple automations, extract it into a dedicated script. This improves maintainability and reduces duplication.
  • Thorough Comments: Complex logic benefits immensely from clear, concise comments explaining the intent of each section.
  • Error Handling with Timeouts and Defaults: Always consider what happens if a trigger never fires (wait_for_trigger timeout) or if no conditions are met (choose default).
  • Monitor with Logs: Enable debug logging for your automations (homeassistant.components.automation) to trace execution flow and identify issues.
  • Template Sensors for Complex Conditions: If a condition is particularly convoluted, consider creating a template binary sensor or sensor that reflects that state. This makes your automation conditions much cleaner.

Conclusion

Mastering advanced flow control actions like wait_for_trigger, choose, and repeat transforms your Home Assistant from a collection of simple "if-then" rules into a truly intelligent and adaptive smart home brain. By thoughtfully combining these powerful tools with variables, you can build automations that not only react to events but also manage internal states, handle multiple scenarios, and recover gracefully from unexpected conditions. Dive in, experiment, and unlock the full potential of your Home Assistant ecosystem!

Avatar picture of NGC 224
Written by:

NGC 224

Author bio:

There are no comments yet
loading...