Troubleshooting Home Automation
Symptom first: no devices, a trigger that will not fire, a door sensor that reads backwards, an empty chart. Most of these look identical from the outside, so each entry starts with the check that tells them apart.
A home-automation stack is four processes, and three of them are not RUAL. That is why the first move is almost always to find out which layer is quiet, rather than to change a setting and see.
The Three Checks That Localise Almost Everything
Run these in order. Each one clears a layer.
mosquitto_sub -h 127.0.0.1 -t '#' -v
# Expect zigbee2mqtt/bridge/state -> online
# and zigbee2mqtt/bridge/devices -> a JSON arraycurl -s https://<node>/_system/homeassist/status \
-H 'Authorization: Bearer <token>'curl -s 'https://<node>/_system/homeassist/devices?include_unadopted=1' \
-H 'Authorization: Bearer <token>'| Where it stops | The problem is |
|---|---|
| Check 1 shows nothing | The radio daemon, or the broker. Not RUAL. |
Check 1 works, check 2 says connected: false | The node's broker settings, or credentials. |
| Check 2 is fine, check 3 is empty | Almost always base_topic. |
Check 3 lists the device with "adopted": false | Adoption. This is the most common answer on the whole page. |
No Devices Appear
In order of how often it is each one:
base_topicdoes not match. The node subscribes under the base topic in[homeassist.zigbee]; Zigbee2MQTT publishes undermqtt.base_topicin its own configuration. Both default tozigbee2mqtt, so a mismatch only happens when somebody changed one. The failure is silent and total: the node connects to the broker happily and sees nothing. Compare the two files; do not trust memory.- The adapter was never constructed. An adapter with no configuration section, or with an empty
broker, is not created at all. Check the boot line:HomeAssist: enabled for entity 1, adapters: [zigbee]lists exactly what exists. If it saysadapters: [], you also had the warning[homeassist] enabled but no adapter is configured. - Home automation is off. Blocks report
HOMEASSIST_NOT_AVAILABLEand the API answers 503 with a hint. Set[homeassist] enabled = true. - The coordinator has no devices. A fresh stick genuinely has an empty mesh, and
zigbee2mqtt/bridge/devicesis an empty array. Pair something. - Broker credentials. If the broker requires a username and password, both the node and each daemon need it. One side unconfigured looks exactly like a broken radio.
Note that the Zigbee coordinator itself is deliberately excluded from the device list: it is not something an automation addresses, and letting it through would put a permanently unadoptable "Coordinator" entry at the top of every picker. Z-Wave node 1 is skipped for the same reason, it is the controller stick.
Bridge Offline Is Not the Same as Broker Unreachable
Both present as "no devices", and the fix is completely different, so the node logs them separately.
| Log line | Means | Fix |
|---|---|---|
zigbee2mqtt: bridge OFFLINE |
The broker is reachable and RUAL is connected to it. Zigbee2MQTT is not running, or it is running and has lost the coordinator. | Restart Zigbee2MQTT. If it starts and immediately reports offline again, the dongle is the problem: check the serial port, the adapter setting and the cable. |
| MQTT connect failures with backoff | The broker itself is unreachable, or refusing the connection. | Check Mosquitto is running, check the host and port, check credentials. |
connected: true in status, no device traffic |
Both are fine and the topic is wrong. | base_topic. |
The adapter status pin tracks the broker connection, not the bridge. get status reporting connected: true means RUAL has an MQTT session, which is a weaker statement than "the radio works". That is why check 1 exists.
Two Nodes, One Broker
If a second RUAL node shares the broker, give each one its own client_id. Two MQTT connections with the same client identifier kick each other off, forever, and the resulting symptom is a device list that appears and disappears every few seconds on both nodes.
A Trigger Does Not Fire
The device is not adopted. That is the answer most of the time. An unadopted device is tracked and visible, its state is followed, and it fires nothing. The gate is enforced centrally, so no trigger is exempt except device discovered.
After that, in order:
- The blueprint is not saved, or the block is not active. The trigger index only contains active trigger blocks from saved blueprints. It rebuilds on save, so no restart is needed, but an unsaved edit does not exist as far as the dispatcher is concerned.
- The device pin does not resolve. It accepts the platform id, the native id or the exact name, case-insensitively. A near-miss on the name silently matches nothing. Paste the id from
get all deviceswhen in doubt. - The room does not match. Room comparison is case-insensitive but not fuzzy: "Hall" and "Hallway" are different rooms, and a device with no room set matches no room filter.
- The device is the wrong type.
light turned onfires only for devices classified as lights andswitch turned ononly for switches. A metering plug is a switch even if the vendor calls it a light. Check thetypefield inget device. - The value did not change. Transition triggers require a change. If a sensor is already true and re-reports true, nothing fires, by design.
- The threshold was not crossed. "Above 25" fires when the reading passes 25 from at or below it. If the room has been at 27 all day, it fired once this morning and will not fire again until it drops below 25 and comes back up.
- The capability is not the one you think.
sensor activatedwith a sensor type set fires only for that capability. Read the device's actual capability list withget device; a sensor that reportsoccupancywill not fire a trigger narrowed tocontact. - The concurrency cap. At most 16 device flows run at once, and beyond that events are dropped with a warning naming the trigger type and device. If you see that line, something in your flows is slow: a
delayinside a device flow holds a slot for its whole duration.
A quick way to isolate steps 3 to 7: temporarily put a device state changed trigger with no device and no room in front of a debug block. If that fires and your specific trigger does not, the problem is a filter, not the plumbing.
A Contact Sensor Reads Backwards
It should not, and if it does, look at your flow before you look at the sensor.
Zigbee's contact attribute is true when the magnet is present, which is when the door is closed. Matter's BooleanState is the same way round. The platform inverts both at the adapter, so in RUAL contact = true means OPEN, everywhere, on every protocol. This is the single most common source of backwards door automation in every home automation system there is, which is exactly why it is corrected once, at the boundary, rather than left to each flow.
So if a door flow behaves backwards, the usual causes are:
- The flow uses
contact closedwhere it meantcontact opened, on the assumption that it had to compensate. - The flow reads
contactthroughget device stateand inverts it by hand, double-correcting. - The magnet is mounted the wrong way round, or on the frame rather than the door, so the sensor genuinely reports the opposite of what the door is doing. Open the door and watch the value with
get deviceor in Zigbee2MQTT: the sensor is the authority on its own wiring.
Colour temperature has the same shape of correction: RUAL speaks kelvin, the wire speaks mireds. If a lamp goes cold when you asked for warm, check you passed 2700 and not 370.
Z-Wave Devices Never Appear
The gateway type must be "ValueID topics". Z-Wave JS UI can publish under two topic layouts and only one of them is machine-parseable.
"Named topics" interpolates your own location and node names into the path, so zwave/kitchen/ceiling/switch_binary/... cannot be told apart from a node whose name contains a slash. ValueID topics are zwave/<nodeId>/<commandClass>/<endpoint>/<property>, which always parses. The node cannot work with the other layout and does not try.
Set it in Z-Wave JS UI under Settings, Gateway, Gateway type. Then check the topics directly:
mosquitto_sub -h 127.0.0.1 -t 'zwave/#' -v
# Right: zwave/12/49/0/Air_temperature {"time":...,"value":21.5}
# Wrong: zwave/kitchen/ceiling/49/0/Air_temperature {...}Also check gateway_name. It is the ZWAVE_GATEWAY-<name> suffix on the API topics and must match the MQTT name configured in Z-Wave JS UI, default zwave-js-ui. With it wrong, values still arrive and get parsed, but the node inventory request goes nowhere: devices appear with names like "Z-Wave node 12" instead of the names you gave them, and with fewer capabilities than they should have.
A Daikin Unit Reports Nothing, or Reports Odd Temperatures
Both firmware generations are supported and the node picks the dialect itself: the legacy BRP069 and BRP072 units speak a flat k=v API at /aircon/*, and the newer BRP069C4x "Onecta" firmware speaks a nested JSON tree at /dsiot/*. A unit that answers 404 on the legacy endpoint is detected as Onecta on the first poll, and the node remembers which dialect each host speaks.
So a Daikin unit that reports nothing at all is usually a network problem rather than a firmware one: wrong IP, the unit on a different VLAN, or a DHCP lease that moved. Confirm with curl http://<ip>/common/basic_info.
Odd temperatures on an Onecta unit are a known limitation, not a fault. Power on and off is reliable; the temperature and mode decoding follows community reverse engineering, the hex encoding varies by model, and it has not been verified against a live unit. If a reading looks wrong, read the untranslated tree instead: the full /dsiot response is on the device's raw map under dsiot, reachable through get device. A report of which model produced which wrong value is the thing that fixes this properly.
Other Daikin specifics worth knowing:
- It is polled, not pushed. The default 30 second
poll_intervalis the resolution of every climate trigger on that unit, and the state you read can be up to one interval old. - Hosts are configured, not discovered. Daikin adapters do not advertise reliably over mDNS. Put the IP in
[homeassist.daikin] hosts, and give it a DHCP reservation so it does not move. - Pairing does not apply.
pairing openon the Daikin adapter returnsHOMEASSIST_PAIRING_UNSUPPORTED. The unit is on the WiFi or it is not. - A unit that is off reports mode
off, not its last mode. The unit itself remembers "heat" all summer; reporting that verbatim would makehvac_mode == "heat"true for a switched-off air conditioner.
UniFi Connects but Reports Nothing
Almost always the keys. Access and Protect are separate apps on the same console, with separate integration APIs and separate keys. Enabling one does not enable the other, and a key issued by one does not authenticate to the other.
- No doors, NFC or entry events: the Access half. Enable the API in Access, then Security, then Advanced, then API, and put that key in
access_key. - No cameras, doorbells or motion: the Protect half. Enable it in Protect, then Settings, then Control Plane, then Integrations, and put that key in
protect_key. - Neither: check the host. Consoles do not have stable hostnames, so use the IP with a DHCP reservation.
A field decodes to the wrong thing. Possible, and expected to be found this way: the adapter follows Ubiquiti's documented APIs but its field decoding has not been confirmed end to end against a live console. Every payload is also carried untranslated on the device's raw map, so read that through get device and use the raw value while the mapping is corrected. A report naming the console version and the raw payload is what fixes it.
RUAL does not appear inside the UniFi app, and cannot. UniFi has no third-party integrator surface that renders in its own UI. The integration APIs are read and command only, so the node is a client of the console rather than a plugin to it.
An NFC trigger fires once and then stops for the same card. The NFC value is the card token, so presenting the same card twice in a row with nothing in between is not a change and does not re-fire. Use access granted or access denied when you need every attempt, since those carry the full event.
Hue and Matter Specifics
| Symptom | Cause and fix |
|---|---|
| Hue adapter starts and does nothing, with a warning about no app key | The bridge has not authorised this node. Press the link button and call POST /_system/homeassist/pair with {"adapter":"hue"} within thirty seconds. The key is printed to the log; paste it into [homeassist.hue] app_key so it survives a restart. It is deliberately not persisted automatically, because a bridge credential does not belong somewhere a blueprint can read it. |
| A Hue motion sensor appears once, not four times | Correct. One Hue device carries motion, temperature, light level and battery as separate services; surfacing those as four devices would put four entries in the picker for something the household calls "the hall sensor". |
| Hue bulbs are missing after moving them to your own coordinator | A bulb is paired to the bridge or to your stick, never both. Re-paired to your coordinator it arrives through the Zigbee adapter with a new device id, and the old Hue-adapter entry stops reporting. |
| A two-gang Matter switch appears as one device | A known limitation. Matter endpoints are collapsed onto one device, whose power is whichever endpoint reported last. Modelling endpoints properly needs a sub-device concept the platform does not have yet. |
| Matter pairing does not find a new device | Matter commissioning is not "open a window and wait". It needs the 11-digit setup code or the MT: string printed on the device. pairing open on the Matter adapter opens the fabric so another controller can join, which is a different operation. |
A Command Does Nothing
| Error pin | What to do |
|---|---|
HOMEASSIST_DEVICE_NOT_ADOPTED | Adopt it. Commands to unadopted devices are refused by design. |
HOMEASSIST_DEVICE_NOT_FOUND | The reference did not resolve. The error carries what you passed; compare it against get all devices. |
HOMEASSIST_CAPABILITY_UNSUPPORTED | The device never announced that capability, and nothing was sent to the radio. Check the device's capability list; capabilities accumulate, so a device may reveal one only after it first changes. |
HOMEASSIST_DEVICE_REQUIRED | No device pin and no picked device. |
ok = false, empty error | The adapter refused or the send failed. Check the node log for the adapter's own message. |
ok = true, nothing happens | The command reached the daemon. Watch the broker: a zigbee2mqtt/<name>/set publish that produces no state message back means the device did not act, which is a mesh or device problem rather than a RUAL one. |
Do not diagnose by comparing the returned device state. Radio adapters return the pre-command snapshot: the bulb has been told, and its own report arrives a moment later. Only Hue and Daikin confirm synchronously. ok is the pin that answers "did this land".
What a Restart Does and Does Not Restore
Last-known device state is persisted to Redis and restored before the adapters connect, so get lights that are on and get active sensors answer sensibly immediately after a restart rather than returning nothing.
What is not restored is reachability. A restored value is a belief about what the device was doing, not evidence that it is still there, so available stays false and last_seen is untouched until the device actually reports. A device list read immediately after a restart therefore looks like a house of unreachable hardware with plausible values, which is exactly what it is. It fills in as devices report, within a polling interval for mains-powered devices and considerably longer for sleepy battery ones.
| Symptom after a restart | Cause |
|---|---|
Values look right, everything reports available: false | Normal. Nothing has reported yet. |
| All state is empty, not just availability | Redis was flushed, this is a fresh install, or the entries expired. State is kept for 30 days, refreshed on each write. |
| A sensor reads occupied or open when it is not | A restored belief that has not been corrected yet. It stands until the device next reports, which for a battery sensor that only speaks on change can be a while. |
| Every trigger fired at once on boot | Should not happen: an unknown previous value seeds silently, and restored state gives the first report something to compare against. If you see it, check that Redis is reachable from the node, because without it the seeding rule is the only protection and a first-ever active reading of smoke, gas or water leak still fires by design. |
A flow that runs at boot and depends on knowing a device is really there should read available rather than inferring it from the presence of a value.
A History Chart Is Empty
Read the recording pin first. It separates two very different situations that look identical:
recording = false: this node stores no history for that capability. Either[homeassist.analytics] enabled = false, or the capability is one that is not recorded at all (color_hexis the notable one).recording = truewith no readings: nothing was stored in that window. For an alarm capability that genuinely means the door never moved. For a measurement it can also mean the value never moved beyond its deadband, in which case you should still see heartbeat rows.
Then check the obvious ones: the device was not adopted during that window (unadopted devices record nothing, including retrospectively), the window is longer than the 90 day retention, or the capability name is not the canonical one.
Gaps in a chart are real. Empty buckets are omitted rather than zero-filled, because drawing a missing temperature reading as 0 °C invents a cold snap. A gap means the device did not report.
A single missing bar on an energy chart is usually a meter reset: a device replaced or firmware reflashed makes the counter go backwards, and that bucket reports no delta rather than a large negative one that would poison every total above it.
Devices Drop Out Intermittently
This is nearly always physical, and nearly always the same two causes.
- The coordinator is plugged straight into the machine. USB 3 ports and NVMe drives emit broadband noise across 2.4 GHz. Use the extension cable that came with the dongle, half a metre, away from the case. On a hub the same rule applies: dongles on their cables, not packed into adjacent ports.
- The mesh has no routers where it needs them. Battery devices do not route. A mains-powered plug or bulb in the middle of a long run does more for reliability than anything in software.
link_quality is recorded for exactly this: "this sensor stopped working" is nearly always a mesh problem, and the shape of the decline is the evidence. Chart it over 7 days for the device that drops, with get device history and capability link_quality.
A device that leaves the mesh is marked unavailable and kept registered, with its name, room and adoption intact. It is not forgotten, because a dead battery is not you saying "remove this".
A Button Trigger Fires but the Branch Never Matches
The strings on the value pin are the device's own, not a RUAL vocabulary. Aqara sends single, double, hold; a Hue dimmer sends initial_press, short_release, long_release; multi-button remotes prefix the button number.
Press the button once and read state.action through get device, or watch the broker, and branch on what it actually sends. Note that Zigbee2MQTT clears the action by publishing an empty string immediately after; that empty value is dropped rather than reported, so it does not double every press.
Next Steps
Setting Up a RUAL Nano The runbook, including the verification steps these checks refer back to. Discovering and Adopting Devices Adoption, which is the answer to most trigger problems. Debugging Blueprints The console, simulation and error pins, for the flow half of the problem.Frequently asked
Why does RUAL show no home automation devices at all?
Most often the base_topic in the node's config does not match the one Zigbee2MQTT publishes under, which fails silently and completely. Check in order: does mosquitto_sub show traffic on the broker, does GET /_system/homeassist/status report the adapter connected, and does the device list include unadopted devices.
What is the difference between a Zigbee bridge being offline and the broker being unreachable in RUAL?
A bridge offline message means the broker is reachable and RUAL is connected to it, but Zigbee2MQTT is not running or has lost the coordinator. Broker connect failures mean the broker itself is unreachable. Both present as no devices, so the node logs them separately.
Why do my Z-Wave devices not appear in RUAL?
Z-Wave JS UI must have its MQTT gateway type set to ValueID topics. The named-topics layout interpolates your own location and node names into the topic path, which cannot be parsed back into a node id, so RUAL cannot use it. ValueID topics look like zwave/12/49/0/Air_temperature.
