Is Zigbee2MQTT Down? Real-Time Status & Outage Checker
Is Zigbee2MQTT Down? Real-Time Status & Outage Checker
Zigbee2MQTT is an open-source Zigbee-to-MQTT bridge with over 12,000 GitHub stars, allowing you to control Zigbee smart home devices — Philips Hue, IKEA Tradfri, Aqara temperature sensors, Sonoff switches, and thousands more — entirely via MQTT without vendor-specific hubs or cloud dependencies. It supports over 3,000 devices and integrates natively with Home Assistant via MQTT discovery, making it the de-facto standard for DIY Zigbee home automation. Zigbee2MQTT runs on a Raspberry Pi, a home server, or in Docker, paired with a Zigbee USB adapter such as the CC2531, Sonoff Zigbee Dongle Plus, or HUSBZB-1. The bridge translates Zigbee's proprietary protocol into plain JSON messages published over MQTT, exposing every sensor reading, switch state, and command as a readable topic any automation system can consume.
When Zigbee2MQTT goes offline, the consequences cascade immediately: lights cannot be toggled, motion sensors stop triggering automations, door/window sensors go silent, and temperature data vanishes. Because Zigbee devices communicate through the bridge — not directly to Home Assistant — a single bridge failure takes down the entire Zigbee mesh from the automation layer's perspective, even if the devices themselves are still powered and paired.
Quick Status Check
#!/bin/bash
# Zigbee2MQTT health check
# Checks MQTT bridge state, process, USB adapter, and broker connectivity
MQTT_HOST="${MQTT_HOST:-localhost}"
MQTT_PORT="${MQTT_PORT:-1883}"
ZIGBEE_ADAPTER="${ZIGBEE_ADAPTER:-/dev/ttyACM0}"
FAIL=0
echo "=== Zigbee2MQTT Status Check ==="
echo "MQTT broker: ${MQTT_HOST}:${MQTT_PORT}"
echo "Zigbee adapter: ${ZIGBEE_ADAPTER}"
echo ""
# Check MQTT broker port reachable
if nc -z -w 3 "${MQTT_HOST}" "${MQTT_PORT}" 2>/dev/null; then
echo "[OK] MQTT broker port ${MQTT_PORT} is reachable"
else
echo "[FAIL] MQTT broker port ${MQTT_PORT} not reachable"
FAIL=1
fi
# Check Zigbee USB adapter exists
if [ -e "${ZIGBEE_ADAPTER}" ]; then
echo "[OK] Zigbee adapter found at ${ZIGBEE_ADAPTER}"
else
# Try alternative paths
ALT=$(ls /dev/ttyACM* /dev/ttyUSB* /dev/serial/by-id/*zigbee* 2>/dev/null | head -1)
if [ -n "$ALT" ]; then
echo "[WARN] Adapter not at ${ZIGBEE_ADAPTER} but found at ${ALT}"
else
echo "[FAIL] Zigbee USB adapter not found — check udev rules or USB connection"
FAIL=1
fi
fi
# Check Zigbee2MQTT process or Docker container
if pgrep -f "zigbee2mqtt" > /dev/null 2>&1; then
echo "[OK] Zigbee2MQTT process is running"
elif docker inspect zigbee2mqtt > /dev/null 2>&1; then
STATE=$(docker inspect -f '{{.State.Status}}' zigbee2mqtt 2>/dev/null)
if [ "$STATE" = "running" ]; then
echo "[OK] Zigbee2MQTT Docker container is running"
else
echo "[FAIL] Zigbee2MQTT Docker container state: ${STATE}"
FAIL=1
fi
else
echo "[WARN] Zigbee2MQTT process/container not detected"
fi
# Subscribe to bridge state topic briefly (requires mosquitto_sub)
if command -v mosquitto_sub > /dev/null 2>&1; then
BRIDGE_STATE=$(timeout 5 mosquitto_sub -h "${MQTT_HOST}" -p "${MQTT_PORT}" \
-t "zigbee2mqtt/bridge/state" -C 1 2>/dev/null)
if echo "${BRIDGE_STATE}" | grep -q "online"; then
echo "[OK] Bridge state topic reports: online"
elif [ -n "${BRIDGE_STATE}" ]; then
echo "[WARN] Bridge state topic reports: ${BRIDGE_STATE}"
FAIL=1
else
echo "[WARN] No message received on zigbee2mqtt/bridge/state within 5s"
fi
else
echo "[INFO] mosquitto_sub not installed — skipping bridge state MQTT check"
fi
echo ""
if [ "$FAIL" -eq 0 ]; then
echo "Result: Zigbee2MQTT appears healthy"
else
echo "Result: Zigbee2MQTT has failures — review output above"
exit 1
fi
Python Health Check
#!/usr/bin/env python3
"""
Zigbee2MQTT health check
Connects via MQTT to verify bridge state, device count, and last-seen timestamps
"""
import json
import os
import sys
import threading
import time
from datetime import datetime, timezone
try:
import paho.mqtt.client as mqtt
except ImportError:
print("ERROR: paho-mqtt not installed. Run: pip install paho-mqtt")
sys.exit(1)
MQTT_HOST = os.environ.get("MQTT_HOST", "localhost")
MQTT_PORT = int(os.environ.get("MQTT_PORT", "1883"))
MQTT_USER = os.environ.get("MQTT_USER", "")
MQTT_PASS = os.environ.get("MQTT_PASS", "")
STALE_DEVICE_HOURS = float(os.environ.get("STALE_DEVICE_HOURS", "24"))
SUBSCRIBE_TIMEOUT = 8
results = {}
received = threading.Event()
def on_connect(client, userdata, flags, rc):
if rc == 0:
client.subscribe("zigbee2mqtt/bridge/state")
client.subscribe("zigbee2mqtt/bridge/info")
client.subscribe("zigbee2mqtt/bridge/devices")
else:
results["mqtt_connect"] = (False, f"MQTT connect failed rc={rc}")
received.set()
def on_message(client, userdata, msg):
topic = msg.topic
try:
payload = msg.payload.decode("utf-8")
except Exception:
return
if topic == "zigbee2mqtt/bridge/state":
try:
data = json.loads(payload)
state = data.get("state", payload)
except json.JSONDecodeError:
state = payload
results["bridge_state"] = (state == "online", f"state={state}")
elif topic == "zigbee2mqtt/bridge/info":
try:
data = json.loads(payload)
coord = data.get("coordinator", {})
fw = coord.get("meta", {}).get("revision", "unknown")
version = data.get("version", "unknown")
results["bridge_info"] = (True, f"z2m={version} coordinator_fw={fw}")
except Exception as e:
results["bridge_info"] = (False, f"parse error: {e}")
elif topic == "zigbee2mqtt/bridge/devices":
try:
devices = json.loads(payload)
total = len(devices)
now = datetime.now(timezone.utc)
stale = []
for dev in devices:
ls = dev.get("last_seen")
name = dev.get("friendly_name", dev.get("ieee_address", "unknown"))
dev_type = dev.get("type", "")
if dev_type == "Coordinator":
continue
if ls:
try:
last = datetime.fromisoformat(ls.replace("Z", "+00:00"))
hours_ago = (now - last).total_seconds() / 3600
if hours_ago > STALE_DEVICE_HOURS:
stale.append(f"{name} ({hours_ago:.1f}h ago)")
except Exception:
pass
results["device_count"] = (total > 0, f"{total} device(s) in network")
if stale:
results["stale_devices"] = (
False,
f"{len(stale)} device(s) not seen in >{STALE_DEVICE_HOURS}h: "
+ ", ".join(stale[:3]) + ("..." if len(stale) > 3 else "")
)
else:
results["stale_devices"] = (True, f"All devices seen within {STALE_DEVICE_HOURS}h")
except Exception as e:
results["device_count"] = (False, f"parse error: {e}")
finally:
received.set()
print(f"=== Zigbee2MQTT Python Health Check ===")
print(f"MQTT broker: {MQTT_HOST}:{MQTT_PORT}")
print()
client = mqtt.Client(client_id="z2m-health-check")
if MQTT_USER:
client.username_pw_set(MQTT_USER, MQTT_PASS)
client.on_connect = on_connect
client.on_message = on_message
try:
client.connect(MQTT_HOST, MQTT_PORT, keepalive=10)
results["mqtt_connect"] = (True, f"Connected to {MQTT_HOST}:{MQTT_PORT}")
except Exception as e:
print(f"[FAIL] MQTT connect — {e}")
sys.exit(1)
client.loop_start()
received.wait(timeout=SUBSCRIBE_TIMEOUT)
client.loop_stop()
client.disconnect()
# If bridge state not received, mark as failed
if "bridge_state" not in results:
results["bridge_state"] = (False, "No message on zigbee2mqtt/bridge/state within timeout")
label_map = {
"mqtt_connect": "MQTT connection",
"bridge_state": "Bridge state",
"bridge_info": "Bridge info (version/firmware)",
"device_count": "Device count",
"stale_devices": "Device last-seen",
}
failures = 0
for key, (ok, detail) in results.items():
label = label_map.get(key, key)
status = "OK" if ok else "FAIL"
print(f"[{status}] {label} — {detail}")
if not ok:
failures += 1
print()
if failures == 0:
print("Result: Zigbee2MQTT appears healthy")
sys.exit(0)
else:
print(f"Result: {failures} check(s) failed — review output above")
sys.exit(1)
Common Zigbee2MQTT Outage Causes
| Symptom | Likely Cause | Resolution |
|---|---|---|
| Bridge offline after reboot, devices unresponsive | Zigbee USB adapter not detected — udev rules missing or device path changed | Add a persistent udev rule by-id; set serial.port in configuration.yaml to /dev/serial/by-id/... path instead of /dev/ttyACM0 |
| All home automation stops responding | MQTT broker disconnected or crashed, severing the bridge from all consumers | Check Mosquitto/EMQX service status; verify server, user, and password in Zigbee2MQTT MQTT config; restart broker then bridge |
| Devices randomly dropping offline | Zigbee mesh degraded — too few router devices or interference on channel 11/15/20/25 | Add Zigbee router devices (IKEA plugs work well); change Zigbee channel in configuration.yaml; check for 2.4 GHz Wi-Fi channel overlap |
| New devices fail to pair | Coordinator firmware outdated — older CC2531/CC2652 firmware has pairing limitations | Flash latest coordinator firmware; use a supported adapter like Sonoff Zigbee Dongle Plus with CC2652P; check supported adapters list in z2m docs |
| Device history lost, entities reset in Home Assistant | database.db corruption — typically caused by power loss during a write or disk full | Restore from backup; set advanced.last_seen: ISO_8601 and enable regular DB backups; move DB to a non-SD-card path |
| Unknown devices joining the network | permit_join accidentally left enabled — security risk in multi-unit buildings |
Set permit_join: false in configuration.yaml; use time-limited join via the frontend or API rather than leaving it open permanently |
Architecture Overview
| Component | Function | Failure Impact |
|---|---|---|
| Zigbee USB Coordinator | Hardware radio managing the Zigbee PAN (personal area network) and mesh routing | Entire Zigbee network goes offline; no device communication possible |
| Zigbee2MQTT Bridge Process | Translates Zigbee frames to/from JSON MQTT messages; manages device registry and database | All device state updates and commands stop; MQTT topics go stale |
| MQTT Broker (Mosquitto/EMQX) | Message bus for distributing device state to Home Assistant, Node-RED, and other consumers | Bridge disconnects; all subscribers lose real-time device data |
| database.db (SQLite) | Stores device network parameters, last-seen timestamps, and scene data | Device history unavailable; devices may need re-pairing after corruption |
| Zigbee Mesh (Router Devices) | End devices relay traffic through mains-powered router nodes to the coordinator | End devices out of coordinator range go offline without sufficient router coverage |
| Zigbee2MQTT Frontend (port 8080) | Optional web dashboard for device management, OTA updates, and network map | Cannot manage devices via UI; bridge continues operating normally |
Uptime History
| Date | Incident Type | Duration | Impact |
|---|---|---|---|
| 2026-02 | Breaking configuration schema change in Zigbee2MQTT 2.x migration | Several hours per affected user | Bridge refused to start after upgrade until configuration.yaml was migrated to new format |
| 2025-10 | Coordinator firmware regression with CC2652-based adapters | ~12 hours | New device pairing failed; existing paired devices continued operating normally |
| 2025-08 | Node.js 20 deprecation crash on older Raspberry Pi OS installs | Variable (user-managed) | Bridge crashed on startup after system Node.js upgrade; required manual version pin |
| 2025-07 | Widespread SD card corruption causing database.db loss | Variable (user-managed) | Device registry lost; all devices required re-pairing and re-configuration in Home Assistant |
Monitor Zigbee2MQTT Automatically
Zigbee2MQTT failures are invisible until a light won't turn on or a motion sensor fails to trigger an alarm — by then, automation has already been broken for hours. ezmon.com monitors your Zigbee2MQTT endpoints from multiple external probes and alerts your team via Slack, PagerDuty, or SMS the moment the bridge state topic stops reporting "online" or the MQTT broker becomes unreachable.