Menu
Zurück zum Blog
1 min read
IoT

Zigbee & Zigbee2MQTT Integration

Zigbee Smart Home mit Zigbee2MQTT. Lokale Kontrolle, MQTT Bridge, Device Pairing und Home Assistant Integration.

ZigbeeZigbee2MQTTSmart HomeMQTTHome AssistantLocal Control
Zigbee & Zigbee2MQTT Integration

Zigbee & Zigbee2MQTT Integration

Meta-Description: Zigbee Smart Home mit Zigbee2MQTT. Lokale Kontrolle, MQTT Bridge, Device Pairing und Home Assistant Integration.

Keywords: Zigbee, Zigbee2MQTT, Smart Home, MQTT, Home Assistant, Local Control, Mesh Network


Einführung

Zigbee ist das bewährte Protokoll für batteriebetriebene Smart Home Geräte. Zigbee2MQTT macht es unabhängig von proprietären Bridges – lokale Kontrolle via MQTT mit über 4.000 unterstützten Geräten.


Zigbee Architecture

┌─────────────────────────────────────────────────────────────┐
│              ZIGBEE2MQTT ARCHITECTURE                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                   Applications                       │   │
│  │  ┌───────────┐  ┌───────────┐  ┌───────────┐       │   │
│  │  │   Home    │  │   Node    │  │   Custom  │       │   │
│  │  │ Assistant │  │   RED     │  │    App    │       │   │
│  │  └─────┬─────┘  └─────┬─────┘  └─────┬─────┘       │   │
│  └────────┼──────────────┼──────────────┼─────────────┘   │
│           │              │              │                  │
│  ┌────────┴──────────────┴──────────────┴─────────────┐   │
│  │                  MQTT Broker                        │   │
│  │              (Mosquitto / EMQX)                     │   │
│  └────────────────────────┬───────────────────────────┘   │
│                           │                                │
│  ┌────────────────────────┴───────────────────────────┐   │
│  │                 Zigbee2MQTT                         │   │
│  │  ┌─────────────┐  ┌─────────────┐  ┌───────────┐  │   │
│  │  │  Frontend   │  │   Bridge    │  │  Converters│  │   │
│  │  │   (Web UI)  │  │   (Core)    │  │  (4000+)  │  │   │
│  │  └─────────────┘  └─────────────┘  └───────────┘  │   │
│  └────────────────────────┬───────────────────────────┘   │
│                           │                                │
│  ┌────────────────────────┴───────────────────────────┐   │
│  │              zigbee-herdsman                        │   │
│  │         (Zigbee Protocol Stack)                     │   │
│  └────────────────────────┬───────────────────────────┘   │
│                           │                                │
│  ┌────────────────────────┴───────────────────────────┐   │
│  │              USB Adapter (Coordinator)              │   │
│  │   Sonoff ZBDongle-P / ConBee II / CC2652P          │   │
│  └────────────────────────┬───────────────────────────┘   │
│                           │                                │
│  ┌────────────────────────┴───────────────────────────┐   │
│  │                 Zigbee Mesh Network                 │   │
│  │   ┌──────┐   ┌──────┐   ┌──────┐   ┌──────┐       │   │
│  │   │Router│───│Router│───│Router│───│ End  │       │   │
│  │   │(Plug)│   │(Bulb)│   │(Plug)│   │Device│       │   │
│  │   └──────┘   └──────┘   └──────┘   └──────┘       │   │
│  └────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Installation

# Docker Installation (empfohlen)
mkdir zigbee2mqtt-data
cd zigbee2mqtt-data

# configuration.yaml erstellen
cat > configuration.yaml << 'EOF'
homeassistant: true
permit_join: false
mqtt:
  base_topic: zigbee2mqtt
  server: mqtt://localhost:1883
  user: mqtt_user
  password: mqtt_password
serial:
  port: /dev/ttyUSB0
  adapter: zstack
frontend:
  port: 8080
advanced:
  network_key: GENERATE
  pan_id: GENERATE
  channel: 15
  log_level: info
  last_seen: 'ISO_8601'
EOF
# docker-compose.yml
version: '3.8'
services:
  zigbee2mqtt:
    container_name: zigbee2mqtt
    image: koenkk/zigbee2mqtt:latest
    restart: unless-stopped
    volumes:
      - ./zigbee2mqtt-data:/app/data
      - /run/udev:/run/udev:ro
    ports:
      - 8080:8080
    environment:
      - TZ=Europe/Berlin
    devices:
      - /dev/ttyUSB0:/dev/ttyUSB0
    depends_on:
      - mosquitto

  mosquitto:
    container_name: mosquitto
    image: eclipse-mosquitto:2
    restart: unless-stopped
    volumes:
      - ./mosquitto/config:/mosquitto/config
      - ./mosquitto/data:/mosquitto/data
      - ./mosquitto/log:/mosquitto/log
    ports:
      - 1883:1883
      - 9001:9001

MQTT Topics

// Zigbee2MQTT MQTT Topic Structure

// Base Topic: zigbee2mqtt

// Device State (Subscribe)
// zigbee2mqtt/[friendly_name]
{
  "state": "ON",
  "brightness": 254,
  "color_temp": 370,
  "linkquality": 156,
  "last_seen": "2026-01-18T10:30:00.000Z"
}

// Device Control (Publish)
// zigbee2mqtt/[friendly_name]/set
{
  "state": "ON",
  "brightness": 200,
  "color_temp": 300,
  "transition": 2
}

// Device Availability
// zigbee2mqtt/[friendly_name]/availability
"online" | "offline"

// Bridge Status
// zigbee2mqtt/bridge/state
"online" | "offline"

// Bridge Info
// zigbee2mqtt/bridge/info
{
  "version": "1.40.0",
  "commit": "abc123",
  "coordinator": {
    "ieee_address": "0x00124b001cda7a45",
    "type": "zStack3x0"
  },
  "network": {
    "channel": 15,
    "pan_id": 6754,
    "extended_pan_id": "0xdddddddddddddddd"
  }
}

// Device List
// zigbee2mqtt/bridge/devices
[
  {
    "ieee_address": "0x00158d00012345678",
    "friendly_name": "living_room_light",
    "type": "Router",
    "model": "LED1545G12",
    "vendor": "IKEA"
  }
]

Node.js Integration

// lib/zigbee2mqtt-client.ts
import mqtt, { MqttClient } from 'mqtt';

interface Z2MDevice {
  ieee_address: string;
  friendly_name: string;
  type: 'Coordinator' | 'Router' | 'EndDevice';
  model?: string;
  vendor?: string;
  supported: boolean;
  definition?: {
    description: string;
    supports: string;
    exposes: Z2MExpose[];
  };
}

interface Z2MExpose {
  type: string;
  name: string;
  property: string;
  access: number;
  values?: string[];
  value_min?: number;
  value_max?: number;
}

interface DeviceState {
  state?: 'ON' | 'OFF';
  brightness?: number;
  color_temp?: number;
  color?: { x: number; y: number };
  temperature?: number;
  humidity?: number;
  battery?: number;
  occupancy?: boolean;
  contact?: boolean;
  linkquality?: number;
  last_seen?: string;
}

class Zigbee2MQTTClient {
  private client: MqttClient;
  private baseTopic: string;
  private devices: Map<string, Z2MDevice> = new Map();
  private states: Map<string, DeviceState> = new Map();
  private listeners: Map<string, Set<(state: DeviceState) => void>> = new Map();

  constructor(
    brokerUrl: string,
    baseTopic: string = 'zigbee2mqtt'
  ) {
    this.baseTopic = baseTopic;
    this.client = mqtt.connect(brokerUrl);

    this.client.on('connect', () => this.onConnect());
    this.client.on('message', (topic, payload) =>
      this.onMessage(topic, payload.toString())
    );
  }

  private onConnect(): void {
    console.log('Connected to MQTT broker');

    // Subscribe to all Z2M topics
    this.client.subscribe(`${this.baseTopic}/#`);

    // Request device list
    this.publish('bridge/request/devices', '');
  }

  private onMessage(topic: string, payload: string): void {
    const relativeTopic = topic.replace(`${this.baseTopic}/`, '');

    // Bridge Events
    if (relativeTopic === 'bridge/devices') {
      const devices = JSON.parse(payload) as Z2MDevice[];
      this.updateDeviceList(devices);
      return;
    }

    if (relativeTopic === 'bridge/state') {
      console.log(`Bridge state: ${payload}`);
      return;
    }

    // Device State Updates
    if (!relativeTopic.includes('/') || relativeTopic.endsWith('/availability')) {
      return;
    }

    // State Update für Gerät
    const deviceName = relativeTopic.split('/')[0];

    try {
      const state = JSON.parse(payload) as DeviceState;
      this.states.set(deviceName, { ...this.states.get(deviceName), ...state });

      // Listener benachrichtigen
      this.listeners.get(deviceName)?.forEach(cb => cb(state));
      this.listeners.get('*')?.forEach(cb => cb(state));
    } catch {
      // Nicht-JSON Payload ignorieren
    }
  }

  private updateDeviceList(devices: Z2MDevice[]): void {
    devices.forEach(device => {
      this.devices.set(device.friendly_name, device);
    });
    console.log(`Loaded ${devices.length} devices`);
  }

  private publish(topic: string, payload: any): void {
    const fullTopic = `${this.baseTopic}/${topic}`;
    const message = typeof payload === 'string'
      ? payload
      : JSON.stringify(payload);

    this.client.publish(fullTopic, message);
  }

  // Public API

  getDevices(): Z2MDevice[] {
    return Array.from(this.devices.values());
  }

  getDevice(name: string): Z2MDevice | undefined {
    return this.devices.get(name);
  }

  getState(deviceName: string): DeviceState | undefined {
    return this.states.get(deviceName);
  }

  // Device Control
  setState(deviceName: string, state: Partial<DeviceState>): void {
    this.publish(`${deviceName}/set`, state);
  }

  turnOn(deviceName: string): void {
    this.setState(deviceName, { state: 'ON' });
  }

  turnOff(deviceName: string): void {
    this.setState(deviceName, { state: 'OFF' });
  }

  toggle(deviceName: string): void {
    this.publish(`${deviceName}/set`, { state: 'TOGGLE' });
  }

  setBrightness(deviceName: string, brightness: number, transition?: number): void {
    this.setState(deviceName, {
      brightness: Math.min(254, Math.max(0, brightness)),
      ...(transition && { transition })
    });
  }

  setColorTemp(deviceName: string, colorTemp: number): void {
    // Mired: 153 (cool) - 500 (warm)
    this.setState(deviceName, { color_temp: colorTemp });
  }

  setColor(deviceName: string, x: number, y: number): void {
    this.setState(deviceName, { color: { x, y } });
  }

  // Event Listener
  onStateChange(
    deviceName: string,
    callback: (state: DeviceState) => void
  ): () => void {
    if (!this.listeners.has(deviceName)) {
      this.listeners.set(deviceName, new Set());
    }

    this.listeners.get(deviceName)!.add(callback);

    return () => {
      this.listeners.get(deviceName)?.delete(callback);
    };
  }

  // Bridge Control
  permitJoin(permit: boolean, duration?: number): void {
    this.publish('bridge/request/permit_join', {
      value: permit,
      time: duration || 120
    });
  }

  renameDevice(oldName: string, newName: string): void {
    this.publish('bridge/request/device/rename', {
      from: oldName,
      to: newName
    });
  }

  removeDevice(deviceName: string, force?: boolean): void {
    this.publish('bridge/request/device/remove', {
      id: deviceName,
      force: force || false
    });
  }

  // OTA Updates
  checkOTAUpdates(): void {
    this.publish('bridge/request/device/ota_update/check', '');
  }

  updateDevice(deviceName: string): void {
    this.publish('bridge/request/device/ota_update/update', {
      id: deviceName
    });
  }

  disconnect(): void {
    this.client.end();
  }
}

export { Zigbee2MQTTClient };

Express API Wrapper

// api/zigbee-api.ts
import express from 'express';
import { Zigbee2MQTTClient } from '../lib/zigbee2mqtt-client';

const router = express.Router();
const z2m = new Zigbee2MQTTClient(process.env.MQTT_URL || 'mqtt://localhost:1883');

// GET /api/devices
router.get('/devices', (req, res) => {
  const devices = z2m.getDevices()
    .filter(d => d.type !== 'Coordinator')
    .map(d => ({
      name: d.friendly_name,
      ieee: d.ieee_address,
      type: d.type,
      model: d.model,
      vendor: d.vendor,
      state: z2m.getState(d.friendly_name)
    }));

  res.json(devices);
});

// GET /api/devices/:name
router.get('/devices/:name', (req, res) => {
  const device = z2m.getDevice(req.params.name);

  if (!device) {
    return res.status(404).json({ error: 'Device not found' });
  }

  res.json({
    ...device,
    state: z2m.getState(req.params.name)
  });
});

// POST /api/devices/:name/set
router.post('/devices/:name/set', (req, res) => {
  const device = z2m.getDevice(req.params.name);

  if (!device) {
    return res.status(404).json({ error: 'Device not found' });
  }

  z2m.setState(req.params.name, req.body);
  res.json({ success: true });
});

// POST /api/devices/:name/toggle
router.post('/devices/:name/toggle', (req, res) => {
  z2m.toggle(req.params.name);
  res.json({ success: true });
});

// Groups
// POST /api/groups/:name/set
router.post('/groups/:name/set', (req, res) => {
  z2m.setState(req.params.name, req.body);
  res.json({ success: true });
});

// Bridge Control
// POST /api/bridge/permit_join
router.post('/bridge/permit_join', (req, res) => {
  const { permit, duration } = req.body;
  z2m.permitJoin(permit, duration);
  res.json({ success: true });
});

export default router;

Home Assistant Auto-Discovery

// Zigbee2MQTT sendet automatisch Home Assistant Discovery Messages

// homeassistant/light/0x00158d00012345678/light/config
{
  "name": "Living Room Light",
  "unique_id": "0x00158d00012345678_light_zigbee2mqtt",
  "state_topic": "zigbee2mqtt/living_room_light",
  "command_topic": "zigbee2mqtt/living_room_light/set",
  "availability": [
    { "topic": "zigbee2mqtt/bridge/state" },
    { "topic": "zigbee2mqtt/living_room_light/availability" }
  ],
  "brightness": true,
  "brightness_scale": 254,
  "color_mode": true,
  "supported_color_modes": ["color_temp", "xy"],
  "min_mireds": 153,
  "max_mireds": 500,
  "schema": "json",
  "device": {
    "identifiers": ["zigbee2mqtt_0x00158d00012345678"],
    "name": "Living Room Light",
    "model": "LED1545G12",
    "manufacturer": "IKEA",
    "via_device": "zigbee2mqtt_bridge"
  }
}

Device Groups

# configuration.yaml

groups:
  living_room:
    friendly_name: Wohnzimmer
    devices:
      - living_room_light_1
      - living_room_light_2
      - living_room_light_3

  all_lights:
    friendly_name: Alle Lichter
    devices:
      - living_room_light_1
      - living_room_light_2
      - bedroom_light
      - kitchen_light
// Gruppen steuern
z2m.setState('living_room', { state: 'ON', brightness: 200 });

// Alle Lichter aus
z2m.turnOff('all_lights');

Scenes & Automations

# configuration.yaml

# Szenen definieren
scenes:
  - name: movie_mode
    data:
      living_room:
        state: 'ON'
        brightness: 50
        color_temp: 500
      kitchen:
        state: 'OFF'

  - name: morning_routine
    data:
      bedroom_light:
        state: 'ON'
        brightness: 100
        color_temp: 250
// Szene aktivieren
z2m.publish('scene/recall', { scene: 'movie_mode' });

// Custom Automation
z2m.onStateChange('motion_sensor', (state) => {
  if (state.occupancy) {
    z2m.turnOn('hallway_light');

    // Auto-Off nach 5 Minuten
    setTimeout(() => {
      const currentState = z2m.getState('motion_sensor');
      if (!currentState?.occupancy) {
        z2m.turnOff('hallway_light');
      }
    }, 5 * 60 * 1000);
  }
});

// Temperature-basierte Automation
z2m.onStateChange('temperature_sensor', (state) => {
  if (state.temperature && state.temperature > 25) {
    z2m.publish('fan/set', { state: 'ON', preset_mode: 'high' });
  }
});

Zigbee Network Map

// Network Topology abrufen

// zigbee2mqtt/bridge/request/networkmap
z2m.publish('bridge/request/networkmap', { type: 'raw' });

// Antwort auf zigbee2mqtt/bridge/response/networkmap
interface NetworkMap {
  nodes: Array<{
    ieee_address: string;
    friendly_name: string;
    type: 'Coordinator' | 'Router' | 'EndDevice';
    network_address: number;
    lqi: number;  // Link Quality Indicator
    relationship: number;
  }>;
  links: Array<{
    source: { ieee_address: string };
    target: { ieee_address: string };
    lqi: number;
    depth: number;
  }>;
}

Troubleshooting

// Häufige Probleme und Lösungen

// 1. Gerät reagiert nicht
async function troubleshootDevice(deviceName: string) {
  const state = z2m.getState(deviceName);

  // Linkqualität prüfen
  if (state?.linkquality && state.linkquality < 50) {
    console.log('Low link quality - consider adding router devices');
  }

  // Availability prüfen
  // Subscribe to zigbee2mqtt/[device]/availability

  // Device Interview neu durchführen
  z2m.publish('bridge/request/device/interview', { id: deviceName });
}

// 2. Pairing funktioniert nicht
function resetAndPair() {
  // Permit Join aktivieren
  z2m.permitJoin(true, 180);  // 3 Minuten

  // Gerät in Pairing-Modus versetzen (geräteabhängig)
  console.log('Reset device and put in pairing mode');
}

// 3. Mesh-Netzwerk optimieren
function optimizeMesh() {
  // Router-Geräte (Steckdosen, Lampen) sind wichtig für Mesh
  // Mindestens ein Router pro Raum

  const devices = z2m.getDevices();
  const routers = devices.filter(d => d.type === 'Router');
  const endDevices = devices.filter(d => d.type === 'EndDevice');

  console.log(`Routers: ${routers.length}, End Devices: ${endDevices.length}`);

  if (endDevices.length / routers.length > 6) {
    console.log('Warning: Not enough routers for stable mesh');
  }
}

Fazit

Zigbee2MQTT bietet:

  1. Lokale Kontrolle: Keine Cloud-Abhängigkeit
  2. 4000+ Geräte: Herstellerunabhängig
  3. MQTT Integration: Einfache Anbindung
  4. Open Source: Aktive Community

Der beste Weg für ein offenes Smart Home.


Bildprompts

  1. "Zigbee mesh network diagram with router and end devices connected"
  2. "Zigbee2MQTT web interface showing device list and controls"
  3. "USB Zigbee coordinator stick connected to Raspberry Pi, smart home hub"

Quellen