Skip to content

Actions Over Time: Delays, Waits, and Automation Modes

09/10/2026
Delays, waits, and automation modes in Home Assistant

This article may contain affiliate links. If you buy through these links, the price is the same for you and the store pays me a small commission that helps keep Tecnoyfoto running.

← Chapter 5 · Course index

Until now, our actions have finished almost immediately. Home Assistant turned on a light, created a notification, or turned off a switch, and the run ended. Once we add a delay or wait for a sensor to change, an automation can remain active for seconds or minutes.

That raises a new question: what should happen if motion is detected again while the previous run is still waiting?

In this chapter, we will build a hallway light. It will turn on when motion is detected, wait for the sensor to stop detecting motion, remain on for another thirty seconds, and then turn off. We will compare this solution with a fixed delay and study the four run modes: single, restart, queued, and parallel.

The requirement before YAML

The first version could be expressed as follows:

When there is motion, turn on the light, wait sixty seconds and turn it off.

It is easy to implement:

alias: "YAML Course - hallway light with fixed delay"
triggers:
  - trigger: state
    entity_id: binary_sensor.course_hallway_motion
    from: "off"
    to: "on"
conditions: []
actions:
  - action: light.turn_on
    target:
      entity_id: light.course_living_room
  - delay:
      seconds: 60
  - action: light.turn_off
    target:
      entity_id: light.course_living_room
mode: single

The actions run in order. Home Assistant does not start the delay and turn off the light at the same time:

Turn on light → wait 60 seconds → turn off light

The run remains active during the delay even though it is not sending commands.

DEALS · OFFICIAL STORE

Discounts on SONOFF smart home

Wi-Fi switches, relays, sensors, LED strips and more. Deals change frequently in the official store.

Coupon: TECNOYFOTO (10% off at checkout)

View official deals Affiliate link Affiliate link · SONOFF store

What does delay really mean?

delay pauses the sequence for a specified duration. It does not observe the sensor or check whether motion is still present. When the duration ends, the sequence continues with the next action.

We can express the same duration in different ways:

- delay: 5
- delay: "00:00:05"
- delay:
    seconds: 5

The form with named units is especially clear when combining durations:

- delay:
    minutes: 1
    seconds: 30

A delay is appropriate when the requirement depends on a known duration: waiting two seconds between commands, keeping a signal active for half a minute, or giving a device time to complete a transition.

It is not the best representation when the real phrase is “wait until there is no motion.” In that case we do not know beforehand the duration; we know the event that allows us to continue.

The problem with a fixed delay in a motion-controlled area

Imagine someone enters the hallway, stops to look for something, and is still there after sixty seconds. The automation with delay turns off the light because the time expired. It never checked whether the sensor was still active.

We could increase the delay to five minutes. That would prevent some premature shutoffs, but the light would also remain on for five minutes after a ten-second visit. Changing the number does not solve the mismatch between elapsed time and sensor state.

The most faithful requirement would be:

When motion is detected, turn on the light. Wait until motion ends. Keep the light on for another thirty seconds, then turn it off.

Now we have two separate waits:

  • An event wait: the sensor switches to off.
  • A timed wait: 30 seconds elapse.

Wait for another trigger with wait_for_trigger

The sequence can be written as follows:

alias: "YAML Course - Hallway light with motion"
description: >-
  Turn on the light when motion starts, wait for motion to end, and keep it on
  for another 30 seconds.
triggers:
  - trigger: state
    entity_id: binary_sensor.course_hallway_motion
    from: "off"
    to: "on"
conditions: []
actions:
  - action: light.turn_on
    target:
      entity_id: light.course_living_room

  - wait_for_trigger:
      - trigger: state
        entity_id: binary_sensor.course_hallway_motion
        from: "on"
        to: "off"
    timeout:
      minutes: 10
    continue_on_timeout: false

  - delay:
      seconds: 30

  - action: light.turn_off
    target:
      entity_id: light.course_living_room
mode: restart

The top-level trigger starts the automation when motion begins. wait_for_trigger, by contrast, is an action inside the sequence. It suspends this run until one of its own triggers fires.

In our case, we expect the opposite transition:

binary_sensor.course_hallway_motion: on → off

When it happens, the sequence advances to a delay of thirty seconds.

The main trigger and the wait trigger serve different roles

The two structures are similar:

triggers:
  - trigger: state
- wait_for_trigger:
    - trigger: state

But they are not interchangeable.

The main trigger creates a new automation run. The trigger inside wait_for_trigger allows an existing run to continue.

If motion ending were a second main trigger, either transition would start the actions from the beginning. When motion ended, the automation would turn the light on again before waiting. That is not the flow we want.

Why we add a timeout

A sensor can remain on, become unavailable, or fail to publish the expected transition. Without a limit, the automation could wait indefinitely.

timeout:
  minutes: 10
continue_on_timeout: false

The timeout limits the wait to ten minutes. If the transition occurs earlier, the wait ends normally. Otherwise, continue_on_timeout: false stops the sequence before it reaches the later turn-off action.

This is deliberate. If the sensor has not confirmed that motion ended within ten minutes, we do not want to pretend that it did. Ending the run without turning off the light leaves a visible situation we can diagnose.

Another automation might use continue_on_timeout: true, the default, and continue after the limit. The correct choice depends on the safest outcome when the expected event never arrives.

A timeout does not turn a wait into a delay. It continues to wait for the event first; time only defines when to stop waiting for it.

The final thirty seconds serve a different purpose

After receiving off, we add:

- delay:
    seconds: 30

This grace period prevents the light from turning off the instant the sensor stops detecting motion. Some sensors clear their state quickly or report brief gaps. Keeping the light on for a short period produces a better experience.

But motion may return during those thirty seconds. The automation is still active in the delay when its main trigger fires again. This is where mode matters.

mode decides what to do with overlapping runs

Modes do not change what triggers an automation. They determine how it handles a new trigger while an earlier run is still active.

single: ignore the new run

mode: single

Only one run is allowed. If motion returns during the final thirty seconds, the new trigger is rejected. The original run finishes its delay and turns off the light even though motion is present again.

single is the default mode. It works well when a second run adds no value or an operation must not overlap. For this hallway light, however, it does not match what a person would expect.

restart: cancel the previous run and start over

mode: restart

A new trigger stops the current run and starts another from the beginning, provided the top-level conditions allow it.

If motion returns during the grace period:

  1. The pending delay is canceled.
  2. The new run turns the light back on.
  3. Wait again for the motion to end.
  4. Start a new 30-second grace period.

For our hallway light, restart captures the idea that new activity restarts the cycle.

queued: place runs in a queue

mode: queued
max: 10

A new run waits for earlier runs to finish. The queue preserves order, which is useful when every event must be processed and the work cannot run in parallel.

For a hallway light, this can create unnecessary sequences: after the first run finishes waiting, another starts from the beginning even if the motion that caused it has already ended. This is not a useful model for keeping a light synchronized with the latest activity.

parallel: start independent runs

mode: parallel
max: 10

Each trigger starts an independent run. An older run could reach light.turn_off while a newer one is still waiting for motion to end. The result is a race condition: several sequences control the same light without coordination.

Parallel mode makes sense when runs are independent—for example, when processing separate notifications whose results cannot conflict. Do not choose it merely because it sounds faster.

Comparing the modes in our hallway

ModeNew motion during the grace period
singleIgnores the new trigger; the old run may turn off the light
restartCancels the pending shutdown and starts a new cycle
queuedPlaces the new cycle behind the previous one
parallelBoth cycles continue and can contradict each other

Mode is not a global user preference. Choose it separately for each automation based on how overlapping runs should relate.

Test without waiting ten minutes

During development, we do not need to wait the full production durations. We can temporarily shorten the timeout and grace period:

actions:
  # Earlier actions omitted so we can focus on test durations.
  - wait_for_trigger:
      - trigger: state
        entity_id: binary_sensor.course_hallway_motion
        from: "on"
        to: "off"
    timeout:
      seconds: 20
    continue_on_timeout: false

  - delay:
      seconds: 5

A complete test would be:

  1. Turn off the motion control and the light.
  2. Activate input_boolean.course_hallway_motion_detected.
  3. Check that the light goes on.
  4. Leave motion active for several seconds and verify that the light does not turn off.
  5. Turn off the motion control.
  6. Confirm in the trace that wait_for_trigger ends and the delay begins.
  7. Reactivate motion within five seconds.
  8. Check that restart cancels the previous run and the light remains on.

After confirming the behavior, restore the 30-second grace period and intended timeout.

Read the timeline of the trace

In this chapter, the trace adds something that was barely visible before: duration.

The normal route shows:

Motion on
  → light.turn_on
  → wait_for_trigger waiting
Motion off
  → wait_for_trigger completed
  → delay of 30 seconds
  → light.turn_off

If the timeout expires with continue_on_timeout: false, the run ends at the wait. If motion returns during the delay with mode: restart, a new run appears and the previous one is canceled.

Do not automatically treat a run canceled by restart as a failure. Cancellation may be exactly the policy we configured.

An important limit: waits are not a permanent state

An active run exists inside the automation engine. If Home Assistant restarts or the automation is reloaded, the run and its wait may be lost. After startup, Home Assistant does not automatically resume the point where the sequence was waiting.

That is usually acceptable for simple courtesy lights. Critical deadlines or events that must survive restarts require a design based on stored timestamps, timers, or other persistent entities. That is a more advanced topic, but remember that delay is not a durable scheduler.

Exercise: choose between time and event

For each need, first decide whether you would use delay, wait_for_trigger or a different design. Justify the decision before writing YAML.

  1. Turn on a light for exactly three seconds as a visual signal.
  2. Wait until a door closes, with a two-minute time limit.
  3. Turn off the light five minutes after the last motion detected.
  4. Run an action tomorrow at 08:00, even if Home Assistant is restarted tonight.

Then complete the automation of the hallway with these requirements:

  • Trigger when motion begins.
  • Turn on the light.
  • Wait for motion to end, with a maximum of ten minutes.
  • If the limit is reached, stop the sequence.
  • Wait for 30 additional seconds.
  • Turn off the light.
  • Restart the cycle if new motion appears.
See the explained solution

The complete automation is:

alias: "YAML Course - Hallway light with motion"
description: >-
  Turn on the light when motion starts, wait for motion to end, and keep it on
  for another 30 seconds.
triggers:
  - trigger: state
    entity_id: binary_sensor.course_hallway_motion
    from: "off"
    to: "on"
conditions: []
actions:
  - action: light.turn_on
    target:
      entity_id: light.course_living_room
  - wait_for_trigger:
      - trigger: state
        entity_id: binary_sensor.course_hallway_motion
        from: "on"
        to: "off"
    timeout:
      minutes: 10
    continue_on_timeout: false
  - delay:
      seconds: 30
  - action: light.turn_off
    target:
      entity_id: light.course_living_room
mode: restart

The exact three-second visual signal uses delay because duration is the requirement. Waiting for a door to close uses wait_for_trigger with a timeout. “Five minutes after the last motion” can use a restartable sequence in which each new motion event resets the grace period. An action scheduled for tomorrow at a specific time should not depend on a delay that a restart can erase; it needs a time trigger or a persistent design.

What should you take from this chapter?

delay waits for a duration without observing the environment. wait_for_trigger expects an event within an active run. A timeout limits how long the wait can last, and continue_on_timeout decides whether the sequence should continue when the event does not arrive.

Once sequences include waits, runs can overlap. single, restart, queued, and parallel represent different policies; no mode is universally correct. For the hallway light, restart lets new motion cancel a pending shutoff and restart the cycle.

In the next chapter we will separate the reusable sequences from the events that activate them. We will create our first script and call it from an automation and from the interface without duplicating the same actions.