Skip to content
Hack Your WorldSoftware · Infrastructure · Home automation

Analysis

Home Assistant Automations Should Fail Gracefully

Physical lighting, cove lights, and a docked robot vacuum continuing to work around a controller fault
AI image: Hack Your World

A Home Assistant service call succeeding does not prove the physical result occurred. Design separately for unpowered receivers, devices that are still booting, one-way protocols, silent failures, and the controls that must work without Home Assistant.

A command is not a result

Home Assistant can report that an automation ran perfectly even when the room is still dark. The service call was valid. The target existed in the entity registry. No YAML failed. The physical device simply was not ready—or could not answer at all.

That gap matters more than the number of integrations in a dashboard. A useful automation has to account for power, startup time, communication direction, and the ordinary physical controls people will use without thinking about the software.

Failure one: I sent RF commands to an unpowered fan

My living-room ceiling fan has its own RF receiver. A Bond Bridge sends the same one-way commands as the handheld remote. A Lutron Caséta switch was wired upstream and could cut power to the receiver.

My first design tracked the fan and light with helpers, then tried to restore those states whenever the Lutron switch brought the circuit back. The second design was cleaner: wait a few seconds after power-on, then send default light and fan commands.

Both designs were built around the wrong assumption. The wall switch was being treated as a daily control for a device that needed continuous power. A quick off/on cycle could restart the receiver while Bond was transmitting. The automation completed; the command vanished.

The fix was architectural, not a longer delay. I leave the Caséta switch on, expose the Bond fan and light as the controls people should use, and keep one guard automation that restores the upstream switch if it is accidentally turned off.

The full failure sequence and final YAML are in Why I stopped power-cycling my Bond ceiling fan.

Failure two: the WLED controller really can be powered off

The bedroom cove looks similar on a diagram: a Caséta switch can remove power from a WLED controller. The intended behavior is different. Ordinary “off” is sent to WLED, leaving the controller awake. “Full off” is a separate command that deliberately cuts power.

Because that hard-off state is intentional, every scene has to recover from it. A shared script turns on the Caséta switch, waits up to 25 seconds for light.bedroom_cove to stop being unavailable, waits another two seconds, and then sends the requested RGBW state to WLED’s JSON API.

- action: switch.turn_on
  target:
    entity_id: switch.master_bedroom_cove_lights
- wait_template: >-
    {{ not is_state('light.bedroom_cove', 'unavailable') }}
  timeout: "00:00:25"
  continue_on_timeout: true
- delay: "00:00:02"
- action: rest_command.wled_set

This time a readiness wait belongs in the design because power cycling is an explicit mode, not an accidental side effect of the main control. The helper uses restart mode, so the newest scene request wins instead of waiting behind a stale one.

The complete helper, REST command, live scene values, and two off scripts are in How I made a power-switched WLED cove behave.

Failure three: the vacuum needs to admit defeat

The Eufy Omni S2 is not an essential control, and I do not need to make its cleaning command complicated. Matter gives Home Assistant room cleaning, operating state, error state, and charging data. The useful failure behavior is a notification when the machine stops doing its job quietly.

My rescue alert watches two conditions:

  • The operational-error sensor changes away from no_error.
  • The S2 spends eight minutes in the state used while it is looking for the charger.

That second condition catches the ordinary failures that do not deserve an elaborate recovery routine: a closed door, a physical trap, or a path back to the dock that no longer exists. Home Assistant tells me. It does not invent a way out for the robot.

I also leave the battery percentage out of decisions because it is not trustworthy in this setup yet. An entity existing does not make its value useful.

The room, zone, whole-floor, and rescue-alert scripts are in How I added the Eufy Omni S2 through Matter.

The three boundaries
System What failed What I changed What remains imperfect
Bond ceiling fan RF commands reached an unpowered receiver Keep the receiver powered and hide the upstream switch from normal control RF state is still assumed, not measured
WLED cove A command could arrive during controller startup Restore power, wait for availability, then send the scene The current command is best effort and has no retry
Eufy Omni S2 A cleaning run could fail quietly Notify on reported errors or an extended charger search Battery percentage is not reliable enough for automation

The physical control gets first claim

I use Lutron Caséta for permanent lighting because the wall control should remain obvious when Home Assistant is restarting. An automation can coordinate scenes and schedules, but I do not want it to impersonate the basic light switch.

This is also why I use Matter bulbs only in places that are not already served by Lutron: a bedside lamp, a movable lamp, or another fixture that can remain powered. Putting a smart bulb behind a switch people naturally turn off recreates the same class of failure.

I wrote the decision rule out in Why I use Caséta for the house and Matter bulbs only in lamps.

My review before I call an automation finished

  1. What still works if Home Assistant is down? Essential physical controls should remain understandable.
  2. Can the target lose power? If so, the automation needs either permanent power or an explicit readiness path.
  3. Does the protocol report state? A one-way RF command cannot prove what the device did.
  4. Can an entity be present but useless? Unavailable and stale values need to be treated as data, not surprises.
  5. What happens when the action fails? Some failures need a retry. Some need a notification. Some should not stop the rest of a scene.
  6. Can two requests overlap? For scene selection I usually want the newest request to replace the old one.
  7. Is the recovery more complicated than the device? If so, the power or control boundary may be wrong.

I do not try to erase every failure

The Bond receiver still cannot report its physical state. The WLED cove can still miss a best-effort HTTP command. The S2 can still trap itself. Graceful failure does not mean pretending those limitations disappeared.

It means the house remains understandable. A switch works. A startup path waits. A silent failure becomes visible. The automation does the part the hardware can support and stops short of claiming certainty it does not have.

Disclosure: These examples come from the Home Assistant configuration running in my house.