Aller au contenu

Zigbee2MQTT Cheat Sheet

Overview

Zigbee2MQTT is an open-source bridge that connects Zigbee devices to an MQTT broker, eliminating the need for proprietary Zigbee hubs from vendors like Philips Hue, IKEA, Aqara, or Samsung SmartThings. It supports over 3,000 devices from 400+ vendors, making it the most comprehensive Zigbee integration available. By translating Zigbee protocol messages to standard MQTT topics, Zigbee2MQTT integrates seamlessly with home automation platforms like Home Assistant, Node-RED, OpenHAB, and any MQTT-capable system.

Zigbee2MQTT requires a supported Zigbee coordinator USB adapter (such as the Sonoff Zigbee 3.0 USB Dongle Plus, SLZB-06, or ConBee II) connected to a Linux machine, Raspberry Pi, or Docker container. It provides a web-based frontend for device management, OTA firmware updates, network visualization, and device configuration. The project handles Zigbee network formation, device pairing, group management, binding, and scenes, all exposed through a clean MQTT API that publishes device states and accepts commands as JSON payloads.

Installation

# Create data directory
mkdir -p /opt/zigbee2mqtt/data

# Create initial configuration
cat > /opt/zigbee2mqtt/data/configuration.yaml << 'EOF'
homeassistant: true
permit_join: false
mqtt:
  base_topic: zigbee2mqtt
  server: mqtt://localhost:1883
serial:
  port: /dev/ttyUSB0
frontend:
  port: 8080
advanced:
  network_key: GENERATE
  pan_id: GENERATE
  log_level: info
EOF

# Run with Docker
docker run -d \
  --name zigbee2mqtt \
  --restart=unless-stopped \
  -v /opt/zigbee2mqtt/data:/app/data \
  --device /dev/ttyUSB0:/dev/ttyUSB0 \
  -p 8080:8080 \
  -e TZ=America/New_York \
  koenkk/zigbee2mqtt

Docker Compose

# docker-compose.yml
version: '3.8'
services:
  mqtt:
    image: eclipse-mosquitto:2
    ports:
      - "1883:1883"
    volumes:
      - mosquitto-data:/mosquitto/data
      - mosquitto-config:/mosquitto/config

  zigbee2mqtt:
    image: koenkk/zigbee2mqtt
    restart: unless-stopped
    volumes:
      - ./zigbee2mqtt-data:/app/data
    ports:
      - "8080:8080"
    devices:
      - /dev/ttyUSB0:/dev/ttyUSB0
    environment:
      - TZ=America/New_York
    depends_on:
      - mqtt

volumes:
  mosquitto-data:
  mosquitto-config:

Native Installation

# Install Node.js 18+
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs

# Clone and build
sudo git clone --depth 1 https://github.com/Koenkk/zigbee2mqtt.git /opt/zigbee2mqtt
cd /opt/zigbee2mqtt
npm ci

# Create systemd service
sudo cat > /etc/systemd/system/zigbee2mqtt.service << 'EOF'
[Unit]
Description=Zigbee2MQTT
After=network.target

[Service]
Environment=NODE_ENV=production
ExecStart=/usr/bin/node /opt/zigbee2mqtt/index.js
WorkingDirectory=/opt/zigbee2mqtt
Restart=on-failure
RestartSec=10s
User=root

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable zigbee2mqtt
sudo systemctl start zigbee2mqtt

Configuration

Main Configuration File

# /opt/zigbee2mqtt/data/configuration.yaml

homeassistant: true
permit_join: false

mqtt:
  base_topic: zigbee2mqtt
  server: mqtt://localhost:1883
  user: mqtt_user
  password: mqtt_password
  keepalive: 60
  version: 5

serial:
  port: /dev/ttyUSB0
  # adapter: zstack    # auto-detected usually
  # baudrate: 115200

frontend:
  port: 8080
  host: 0.0.0.0
  auth_token: my-secret-token

advanced:
  network_key: GENERATE
  pan_id: GENERATE
  ext_pan_id: GENERATE
  channel: 15
  log_level: info
  log_output:
    - console
    - file
  homeassistant_discovery_topic: homeassistant
  homeassistant_status_topic: homeassistant/status
  last_seen: ISO_8601
  elapsed: true
  transmit_power: 20

availability:
  active:
    timeout: 10
  passive:
    timeout: 1500

ota:
  update_check_interval: 1440
  disable_automatic_update_check: false

device_options:
  legacy: false

Device-Specific Configuration

# In configuration.yaml
devices:
  '0x00158d0001234567':
    friendly_name: living_room_motion
    retain: true
    availability: true
  '0x00158d0009876543':
    friendly_name: kitchen_temperature
    debounce: 1
    debounce_ignore:
      - action

groups:
  '1':
    friendly_name: living_room_lights
    retain: false
    devices:
      - '0x00158d0001111111/1'
      - '0x00158d0002222222/1'
  '2':
    friendly_name: all_lights

MQTT Commands

Device Control

# Publish to control a device
# Turn on a light
mosquitto_pub -t "zigbee2mqtt/living_room_light/set" -m '{"state": "ON"}'

# Set brightness and color
mosquitto_pub -t "zigbee2mqtt/living_room_light/set" -m \
  '{"state": "ON", "brightness": 200, "color_temp": 350}'

# Set color by name or hex
mosquitto_pub -t "zigbee2mqtt/living_room_light/set" -m \
  '{"color": {"hex": "#FF5733"}}'

# Set HSV color
mosquitto_pub -t "zigbee2mqtt/living_room_light/set" -m \
  '{"color": {"hue": 120, "saturation": 100}}'

# Toggle device
mosquitto_pub -t "zigbee2mqtt/living_room_light/set" -m '{"state": "TOGGLE"}'

# Set thermostat
mosquitto_pub -t "zigbee2mqtt/thermostat/set" -m \
  '{"occupied_heating_setpoint": 22, "system_mode": "heat"}'

# Lock/unlock
mosquitto_pub -t "zigbee2mqtt/front_door_lock/set" -m '{"state": "LOCK"}'

Subscribe to Device State

# Subscribe to all device updates
mosquitto_sub -t "zigbee2mqtt/#" -v

# Subscribe to specific device
mosquitto_sub -t "zigbee2mqtt/living_room_motion" -v

# Subscribe to bridge events
mosquitto_sub -t "zigbee2mqtt/bridge/#" -v

# Subscribe to device availability
mosquitto_sub -t "zigbee2mqtt/+/availability" -v

Bridge Management via MQTT

# Permit joining (60 seconds)
mosquitto_pub -t "zigbee2mqtt/bridge/request/permit_join" -m '{"value": true, "time": 60}'

# Disable joining
mosquitto_pub -t "zigbee2mqtt/bridge/request/permit_join" -m '{"value": false}'

# Rename device
mosquitto_pub -t "zigbee2mqtt/bridge/request/device/rename" -m \
  '{"from": "0x00158d0001234567", "to": "kitchen_sensor"}'

# Remove device
mosquitto_pub -t "zigbee2mqtt/bridge/request/device/remove" -m '{"id": "kitchen_sensor"}'

# Force remove (unresponsive device)
mosquitto_pub -t "zigbee2mqtt/bridge/request/device/remove" -m \
  '{"id": "old_sensor", "force": true}'

# Get network map
mosquitto_pub -t "zigbee2mqtt/bridge/request/networkmap" -m '{"type": "graphviz", "routes": true}'

# Restart Zigbee2MQTT
mosquitto_pub -t "zigbee2mqtt/bridge/request/restart" -m ""

# Check for OTA updates
mosquitto_pub -t "zigbee2mqtt/bridge/request/device/ota_update/check" -m '{"id": "living_room_light"}'

# Apply OTA update
mosquitto_pub -t "zigbee2mqtt/bridge/request/device/ota_update/update" -m '{"id": "living_room_light"}'

Group Management

# Create group
mosquitto_pub -t "zigbee2mqtt/bridge/request/group/add" -m '{"friendly_name": "bedroom_lights"}'

# Add device to group
mosquitto_pub -t "zigbee2mqtt/bridge/request/group/members/add" -m \
  '{"group": "bedroom_lights", "device": "bed_light_left"}'

# Remove device from group
mosquitto_pub -t "zigbee2mqtt/bridge/request/group/members/remove" -m \
  '{"group": "bedroom_lights", "device": "bed_light_left"}'

# Control group
mosquitto_pub -t "zigbee2mqtt/bedroom_lights/set" -m '{"state": "ON", "brightness": 150}'

Advanced Usage

Binding (Direct Device-to-Device)

# Bind switch to light (commands go directly, no coordinator needed)
mosquitto_pub -t "zigbee2mqtt/bridge/request/device/bind" -m \
  '{"from": "wall_switch", "to": "ceiling_light", "clusters": ["genOnOff", "genLevelCtrl"]}'

# Unbind
mosquitto_pub -t "zigbee2mqtt/bridge/request/device/unbind" -m \
  '{"from": "wall_switch", "to": "ceiling_light"}'

# Bind to group
mosquitto_pub -t "zigbee2mqtt/bridge/request/device/bind" -m \
  '{"from": "wall_switch", "to": "living_room_lights"}'

Scenes

# Store scene
mosquitto_pub -t "zigbee2mqtt/living_room_lights/set" -m \
  '{"scene_store": {"ID": 1, "name": "movie_mode"}}'

# Recall scene
mosquitto_pub -t "zigbee2mqtt/living_room_lights/set" -m \
  '{"scene_recall": {"ID": 1}}'

External Converters

// /opt/zigbee2mqtt/data/my_converter.js
const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');

const definition = {
    zigbeeModel: ['CustomDevice123'],
    model: 'CustomDevice123',
    vendor: 'MyVendor',
    description: 'Custom Zigbee sensor',
    fromZigbee: [fz.temperature, fz.humidity],
    toZigbee: [],
    exposes: [e.temperature(), e.humidity()],
};

module.exports = definition;
# Add to configuration.yaml
external_converters:
  - my_converter.js

Troubleshooting

IssueSolution
Coordinator not foundCheck USB device path, try different port
Device won’t pairEnable permit_join, bring device close to coord
Intermittent connectivityAdd Zigbee routers (always-on devices) to mesh
Device shows unavailableCheck battery, verify availability timeout
MQTT connection refusedVerify broker address, credentials, port
High latencyReduce network size per channel, avoid WiFi ch
OTA update failsKeep device close to coordinator during update
Frontend not loadingCheck port 8080 not blocked, verify config

Diagnostic Commands

# Check Zigbee2MQTT logs
docker logs -f zigbee2mqtt
# or
journalctl -u zigbee2mqtt -f

# Check coordinator firmware
mosquitto_sub -t "zigbee2mqtt/bridge/info" -C 1 | python3 -m json.tool

# List all devices
mosquitto_sub -t "zigbee2mqtt/bridge/devices" -C 1 | python3 -m json.tool

# Check device state
mosquitto_sub -t "zigbee2mqtt/my_device" -C 1

# Debug logging
# Set in configuration.yaml: advanced.log_level: debug
# Restart Zigbee2MQTT

# Find USB device path
ls -la /dev/ttyUSB* /dev/ttyACM*
dmesg | grep -i "usb\|tty"

WiFi Channel Interference

# Zigbee channels vs WiFi channels (2.4GHz overlap)
# Zigbee 15 -> WiFi 1 (least overlap)
# Zigbee 20 -> WiFi 6 (least overlap)
# Zigbee 25 -> WiFi 11 (least overlap)
# Best practice: Use Zigbee channel 15, 20, or 25