DEV Community

Charles Hartmann
Charles Hartmann

Posted on

Why your Home Assistant motion light turns off on you (and the Zigbee fix that comes first)

The first Home Assistant motion light is a ten-minute job and it feels like magic. Six months later it is the automation the family complains about most: it goes off while someone is standing in the hallway, it fires at noon, and the bedroom motion sensor it depends on drops off the Zigbee network every other week.

None of that is bad luck. It is three specific mistakes, and each one is a two-line fix. Here is the version I run at home, with the YAML, plus the Zigbee change that most people need before any of it matters. Entity names are examples; use your own.

Mistake 1: a fixed delay with the default mode

The textbook motion light looks like this: motion turns on, delay: 00:02:00, turn off. The problem is not the delay, it is the automation's mode. The default, single, means that if the motion sensor fires again while the first run is still sleeping through its delay, the second trigger is dropped. There is a warning in the log that nobody reads. The two-minute countdown that started on the first movement keeps running, and the light goes off two minutes after the first motion, not the last. That is why it turns off on people who are still there.

The fix is two changes. Set mode: restart, so every new motion cancels the running countdown and starts again. And instead of sleeping for two minutes, wait for the sensor to be off for two minutes:

alias: Hallway light on motion
description: On with motion after dark; off 2 min after motion stops
mode: restart
triggers:
  - trigger: state
    entity_id: binary_sensor.hallway_motion
    to: "on"
conditions:
  - condition: state
    entity_id: sun.sun
    state: below_horizon
actions:
  - action: light.turn_on
    target:
      entity_id: light.hallway_ceiling
    data:
      brightness_pct: 60
  - wait_for_trigger:
      - trigger: state
        entity_id: binary_sensor.hallway_motion
        to: "off"
        for: "00:02:00"
  - action: light.turn_off
    target:
      entity_id: light.hallway_ceiling
Enter fullscreen mode Exit fullscreen mode

Walk through a real evening: motion at 0:00 starts the run and the light comes on. Motion again at 1:12 restarts the run (the first one is cancelled). Motion at 2:18 restarts it again. The sensor finally reports off at 2:40 and stays off; the wait_for_trigger fires at 4:40 and the light goes out. Two minutes after the last movement, every time.

If you want the version that never fights a person who turned the light on at the switch, use a timer helper instead: motion starts (or restarts) timer.hallway_light and turns the light on; a second automation triggered by timer.finished turns it off. A manual switch press never starts the timer, so the automation never turns off a light a human turned on.

The four modes, for reference:

Mode On a second trigger while running Use for
single Dropped (warning in the log) One-shot notifications, things that must not overlap
restart The running one is cancelled and it starts over Motion lights
queued Runs after the first finishes Sequences that must each complete
parallel Both run at once Independent targets

Mistake 2: a state trigger with no to:

This one is responsible for "my automation runs twenty times an hour and I don't know why". A state trigger with no to: and no from: fires on any change of the entity, including attribute changes such as a light's brightness or a sensor's last_seen. Always give it a to:.

The companion fix is for: on flappy sensors. A door contact that reports open, closed, open within a second triggers three times without it and zero times with for: "00:00:05". The for: on the wait in the automation above does the same job for the motion sensor.

You want to react to Trigger Key detail
An entity reaching a state state Set to:; without it, every attribute change fires too
A state held for a while state with for: Fires only after the state has lasted that long
A number crossing a line numeric_state above: / below:; fires once when crossed
Sunset or sunrise sun offset: "-00:30:00" for half an hour before

(Home Assistant 2024.10 moved to the plural triggers: / conditions: / actions: keys with trigger: state and action: light.turn_on inside them. The older platform: and service: spellings still work; the editor saves in the new form.)

Mistake 3: changing the automation before reading the trace

Every run of every automation keeps a trace: which trigger fired, what each condition evaluated to, what each action did, and the state of the entities it touched at that instant. Settings, then Automations & Scenes, open the automation, three-dot menu, Traces.

When an automation "didn't work", the trace tells you which of four things happened: it never triggered (look at the trigger entity's history), a condition was false (the trace shows the exact value it saw), an action errored (the error is right there), or it was triggered while already running in single mode and dropped. Guessing is slower than reading. Five traces are kept by default; raise it on a troublesome one:

alias: Hallway light on motion
trace:
  stored_traces: 25
Enter fullscreen mode Exit fullscreen mode

The Zigbee fix that comes before all of it

A motion light is only as reliable as the sensor, and most "the bedroom sensor dropped again" problems are not the sensor. They are the coordinator stick plugged straight into a USB 3.0 port on the server, inside a metal case, next to the Wi-Fi access point.

USB 3.0 ports and the cables plugged into them radiate noise in the 2.4 GHz band, which is exactly where Zigbee lives. Put the coordinator on a USB 2.0 extension cable, one to two metres long, a metre away from any SSD, hub or access point, and into a USB 2.0 port if the machine has one. This single change fixes more Zigbee networks than any setting.

Three more, in order of effort:

  • Pick the channel on purpose, before pairing anything. Zigbee channels 11 to 26 share 2.4 GHz with Wi-Fi channels 1, 6 and 11. Zigbee 15, 20 and 25 sit in the gaps. Zigbee2MQTT's default of 11 sits squarely under Wi-Fi channel 1. In Zigbee2MQTT it is advanced.channel; in ZHA it is a choice during setup. Changing it later makes every device re-join.
  • Routers are the mesh. Only mains-powered devices route. Battery sensors and remotes are end devices that talk to one parent and never forward anything. A house with twenty battery sensors and one coordinator is a star, not a mesh. Put a smart plug or a mains bulb in each room between the coordinator and the far sensors, pair the routers first, from the room they will live in, then the sensors, also in place.
  • permit_join: false except while you are actually pairing. Leave it on and a neighbour's new bulb joins your network, or a device you reset by accident re-joins with a fresh identity and all its automations break.

A Zigbee2MQTT configuration with the choices made on purpose:

serial:
  port: /dev/serial/by-id/usb-...   # or tcp://192.168.30.40:6638 for a network coordinator
  adapter: zstack                   # ember for EFR32 sticks
mqtt:
  server: mqtt://core-mosquitto:1883
advanced:
  channel: 20
  network_key: GENERATE             # written once on first start; back it up
  pan_id: GENERATE
  ext_pan_id: GENERATE
  last_seen: ISO_8601
permit_join: false
frontend:
  enabled: true
  port: 8099
homeassistant:
  enabled: true
Enter fullscreen mode Exit fullscreen mode

Checklist

  • Motion lights on mode: restart with a wait_for_trigger, not a fixed delay (or a timer helper)
  • Every state trigger has a to:; flappy sensors have for:
  • Read the trace before changing an automation that misbehaved
  • Coordinator on a USB 2.0 extension cable, away from the case and the access point
  • Zigbee on 15, 20 or 25, chosen before pairing; Wi-Fi on 1, 6 or 11
  • A mains-powered router in each room between the coordinator and the far sensors, paired in place
  • permit_join off except while pairing

Drafted with AI help. I edited it, and it's based on my own setup.

This post is adapted from Chapters 2 and 4 of my ebook Automations That Just Work: Reliable Home Assistant with Zigbee, Z-Wave, Dashboards and Updates That Don't Break Things. The free sample is the whole automations chapter; the full book adds Z-Wave JS done right, helpers and blueprints, phone and wall-tablet dashboards, notifications that don't cry wolf, and a monthly update routine with three ways back.

Top comments (0)