Vollständige Next.js 15 Portfolio-Website mit: - Blog-System mit 100+ Artikeln - Supabase-Integration - Responsive Design mit Tailwind CSS - TypeScript-Konfiguration - Testing-Setup mit Vitest und Playwright Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
729 lines
20 KiB
Markdown
729 lines
20 KiB
Markdown
# 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
|
||
|
||
```bash
|
||
# 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
|
||
```
|
||
|
||
```yaml
|
||
# 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
|
||
|
||
```typescript
|
||
// 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
|
||
|
||
```typescript
|
||
// 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
|
||
|
||
```typescript
|
||
// 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
|
||
|
||
```typescript
|
||
// 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
|
||
|
||
```yaml
|
||
# 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
|
||
```
|
||
|
||
```typescript
|
||
// Gruppen steuern
|
||
z2m.setState('living_room', { state: 'ON', brightness: 200 });
|
||
|
||
// Alle Lichter aus
|
||
z2m.turnOff('all_lights');
|
||
```
|
||
|
||
---
|
||
|
||
## Scenes & Automations
|
||
|
||
```yaml
|
||
# 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
|
||
```
|
||
|
||
```typescript
|
||
// 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
|
||
|
||
```typescript
|
||
// 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
|
||
|
||
```typescript
|
||
// 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
|
||
|
||
- [Zigbee2MQTT Documentation](https://www.zigbee2mqtt.io/)
|
||
- [zigbee-herdsman GitHub](https://github.com/Koenkk/zigbee-herdsman)
|
||
- [Supported Devices](https://www.zigbee2mqtt.io/supported-devices/)
|
||
- [Zigbee Technology 2026](https://zigbeehubs.com/zigbee-technology-2026/)
|