1. Introduction
Lorsqu’un réseau domotique dépasse la quarantaine d’équipements, le dongle USB Zigbee branché au dos d’un serveur finit souvent par montrer ses limites. Entre interférences radio et contraintes de passthrough matériel lors des redémarrages de conteneurs ou de machines virtuelles, passer à un coordinateur Zigbee PoE (Power over Ethernet) déporté est une bonne évolution matérielle possible.
Quand basculer vers un SMLIGHT SLZB-MR4U ? Faut-il tenter une migration transparente (sauvegarde/restauration NVRAM et clonage de l’adresse IEEE) pour éviter de toucher aux capteurs ? Faut-il prendre son courage à deux mains et assumer un réappairage total sur un réseau neuf ?
La migration transparente fonctionne très bien chez certains utilisateurs ayant un parc homogène ou des puces strictement identiques. Mais concernant mon retour d’expérience, la migration « transparente » a été compliquée, ayant subi pendant plusieurs jours des effets de bord qui m’ont coûté des heures de diagnostic. Si vous avez la possibilité de réappairer, faites-le : ce sera sans problème.
Pourquoi un coordinateur Zigbee PoE ?
Un coordinateur réseau alimenté en PoE (802.3af, puces TI CC2652P / CC2674P10 ou Silicon Labs EFR32MG24, gamme SMLIGHT SLZB-06 / SLZB-MR) résout trois problèmes :
- Positionnement radio optimal : placé au centre du logement via un câble RJ45 PoE plutôt qu’enfermé dans une baie ou au sous-sol ;
- Découplage système complet : Zigbee2MQTT communique via un socket TCP (
tcp://coordinateur-zigbee.lan:6638). Les conteneurs Docker peuvent redémarrer sans perdre le périphérique série (/dev/ttyUSB0) ; - Bilan de liaison supérieur : l’antenne externe (
+5 dBi) éloignée des ports USB 3.0 apporte un gain immédiat de15 à 30 points de LQIsur la première couronne de routeurs.
2. « Migration transparente » :
Sur le papier, la documentation de Zigbee2MQTT indique que l’on peut passer d’un coordinateur à un autre sans réappairer ses équipements grâce au fichier coordinator_backup.json et à la réécriture de l’adresse MAC IEEE du nouveau coordinateur.
Dans mon cas personnel : avec un maillage comportant plusieurs marques très différentes (Aqara, Sonoff, Ikea, Tuya, Legrand) et un changement de génération de puce radio : cette migration transparente a déclenché chez moi trois types de dysfonctionnements silencieux pénibles à débugger :
- Tables de routage fantômes (Stale Source Routes) : les routeurs secteur gardent en mémoire flash la topologie de l’ancien coordinateur. Même avec la même adresse
0x0000et la même adresse IEEE, l’emplacement physique et les caractéristiques RF changent. Plusieurs routeurs continuent d’acheminer les trames vers d’anciens voisins hors de portée. - Désalignement des tables internes NVRAM (
BindingTable&AssocTable) : lors de la restauration d’une NVRAM sur un firmware plus récent, certains index d’association sautent. L’équipement apparaît « en ligne », mais ses rapports d’état (attribute reporting) ou ses clics d’interrupteur n’arrivent plus à Zigbee2MQTT. - Erreurs
MAC_NO_ACK (233)et décrochages à retardement : la migration semble fonctionner pendant les deux premières heures. Puis, 24 h à 48 h plus tard, les capteurs sur batterie (End-Devices) échouent à renouveler leur parent, vident leur pile CR2032 et tombent hors ligne les uns après les autres.
| Critère | Migration Transparente (Clonage NVRAM) | Réappairage Total (Maillage Neuf) |
|---|---|---|
| Temps initial | ~15 minutes | 1 h 30 à 2 h (50 à 70 équipements) |
| Temps de debug Day-2 | Plusieurs jours (capteurs fantômes) | 0 minute après l’opération |
| Tables NVRAM & Bindings | Anciens nœuds et routes obsolètes | 100 % vierge et déterministe |
| Qualité du maillage (LQI) | Dégradée (routes sous-optimales conservées) | Optimale (calculée depuis la position PoE) |
| Changement de canal RF | Non (bloqué sur l’ancien canal) | Oui (passage propre sur le canal 25) |
| Impact Home Assistant | Aucun si tout marche | Aucun en conservant les friendly_name |
3. Réappairage total sans casser Home Assistant
Home Assistant identifie les entités MQTT par leur friendly_name, et non par le pan_id du réseau Zigbee. Conserver les mêmes friendly_name suffit à préserver tous les tableaux de bord et automatisations.
Étape 1 : Exporter les friendly_name avant la bascule
Sauvegarder configuration.yaml de Zigbee2MQTT pour conserver la correspondance entre chaque adresse IEEE (0x00158d...) et son nom logique :
# Extraire la liste des adresses IEEE et leurs friendly_name
grep -E "^ '0x|friendly_name:" domotique/zigbee2mqtt/data/configuration.yaml
Étape 2 : Initialiser un réseau vierge sur le canal 25
Configurer Zigbee2MQTT avec pan_id: GENERATE, ext_pan_id: GENERATE, network_key: GENERATE et fixer le canal 25 (2475 MHz, hors du canal Wi-Fi 11) :
# domotique/zigbee2mqtt/data/configuration.yaml
homeassistant: true
permit_join: false
mqtt:
base_topic: zigbee2mqtt
server: mqtt://broker-mqtt.local:1883
serial:
port: tcp://coordinateur-zigbee.local:6638
adapter: zstack
baudrate: 115200
rtscts: false
advanced:
channel: 25
pan_id: GENERATE
ext_pan_id: GENERATE
network_key: GENERATE
transmit_power: 20
last_seen: ISO_8601
Étape 3 : Ordre d’appairage (Routeurs d’abord, End-Devices sur site ensuite)
- Phase 1 : Routeurs secteur uniquement : intégrer les prises connectées et micromodules 230 V, du plus proche du coordinateur au plus éloigné, en réappliquant immédiatement chaque
friendly_name. Ne pas compter sur des ampoules reliées à un interrupteur mural classique coupé physiquement (ou utiliser le mode découplé). - Phase 2 : Stabilisation topologique (30 minutes) : couper l’appairage pour laisser les routeurs échanger leurs tables de voisinage (Link Status).
- Phase 3 : Capteurs sur batterie (End-Devices) à leur emplacement final : ne pas appairer les capteurs sur un bureau près du coordinateur avant de les déplacer (les capteurs Aqara 1re génération ne changent pas de routeur parent). Ouvrir l’appairage sur le routeur secteur de la pièce cible (
Permit join (Router)).
4. SMLIGHT SLZB-MR4U : activer Thread (OTBR) sur la Radio 1
Le SMLIGHT SLZB-MR4U embarque deux puces radio indépendantes dans le même boîtier PoE :
- Radio 1 (
Silicon Labs EFR32MG26, port TCP6638,460800bauds) : dédiée à Thread RCP (Radio Co-Processor) pour Matter-over-Thread via OpenThread Border Router (OTBR) ; - Radio 2 (
Texas Instruments CC2674P10, port TCP7638,115200bauds) : dédiée à Zigbee2MQTT, sans coupure lors du flashage de la Radio 1.
Selon la documentation officielle SMLIGHT (Thread setup : network and USB connection (#otbr)), deux modes sont disponibles :
- Option A :
"Thread to remote OTBR"(Recommandée) : la Radio 1 est flashée en Thread RCP (slzb06Mg26_openthread_rcp_2.7.2.0_460800.gbl, exposée sur<ip-slzb>:6638à460800bauds,Hardware flow control: No). Le serviceotbr-agenttourne sous Docker / Home Assistant et se connecte au port TCP6638. - Option B :
"Thread + OTBR running on device"(Bêta) : le service OTBR tourne directement sur leSLZB-MR4U(http://<ip-slzb>:8080, IPv6 LAN).
👉 Guide dédié : Matter via Wi-Fi vs Matter via Thread (OTBR) avec SMLIGHT SLZB-MR4U.
5. Vérification du maillage
Une fois le réappairage terminé, verrouiller permit_join: false et vérifier l’état du pont MQTT :
# État du pont Zigbee2MQTT et firmware du coordinateur PoE
mosquitto_sub -h broker-mqtt.local -t "zigbee2mqtt/bridge/info" -C 1 | jq '{version, coordinator, network}'
# Équipements hors coordinateur et statut d'interview
mosquitto_sub -h broker-mqtt.local -t "zigbee2mqtt/bridge/devices" -C 1 | jq '.[] | select(.type != "Coordinator") | {friendly_name, type, interview_completed}'
| Commande | Description |
|---|---|
`channel: 25`
| Privilégier le canal Zigbee 25 (2475 MHz) pour éviter le recouvrement avec les canaux Wi-Fi 1 et 6. |
`pan_id: GENERATE`
| Forcer la création d'un réseau vierge sur un nouveau coordinateur plutôt que de cloner une ancienne NVRAM. |
`Permit join (Router)`
| Toujours appairer les capteurs à pile sur leur lieu définitif en ciblant le routeur secteur de la pièce. |
`permit_join: false`
| Désactiver systématiquement l'appairage permanent en production (sécurité et économie de bande passante radio). |
`transition: 0`
| Sur les éclairages pilotés par bouton sans fil, utiliser une transition instantanée pour éviter la saturation de file. |
6. Post-Mortem : deux bugs techniques qui ont bloqué la migration et les réappairages
Au-delà des limites inhérentes au clonage de NVRAM entre générations de puces, l’audit des scripts exécutés lors de la bascule a mis en évidence deux erreurs techniques qui ont aggravé les échecs :
Erreur n°1 : Restauration NVRAM Z-Stack tronquée (maintenue sur le canal 11)
- Sous-identifiants NVRAM incomplets : Les tables d’adressage (
ZCD_NV_NWK_SEC_MATERIAL_TABLE_START,APS_KEY_DATA_TABLE,ADDRMGR) avaient été écrites avec un nombre limité de sub-IDs au lieu de restaurer toute la pile TI Z-Stack (zigpy-znp). Le coordinateur rejetait alors les trames chiffrées d’une partie des nœuds. - Maintien sur le canal 11 : La configuration restaurée a conservé le
channel: 11(pan_id: 8459), en recouvrement direct avec le canal Wi-Fi 1.
Erreur n°2 : Bug d’ordonnancement dans controller.js (deviceJoined émis avant networkAddress)
Lorsqu’un capteur déjà présent dans database.db (conservé pour son friendly_name) est réinitialisé par un appui de 5 secondes, il rejoint le réseau avec la même adresse IEEE (0x00158d...), mais reçoit une nouvelle adresse courte 16-bit (networkAddress).
Or, un premier patch monté sur zigbee-herdsman émettait this.selfAndDeviceEmit(device, "deviceJoined", { device }) avant l’instruction device.networkAddress = payload.networkAddress :
- Zigbee2MQTT envoyait l’interview et la Transport Key vers l’ancienne
networkAddressmorte encore en cache ; - Le capteur tombait en timeout au bout de 10 secondes et quittait le réseau.
Correctif appliqué dans controller.js
Mettre à jour et sauvegarder device.networkAddress en tête de onDeviceJoined, onDeviceAnnounce et onNetworkAddress avant tout événement :
// Mettre à jour la nouvelle adresse 16-bit AVANT d'émettre deviceJoined ou de lancer device.interview()
if (device.networkAddress !== payload.networkAddress) {
logger.debug(`Updating networkAddress of '${device.ieeeAddr}' from '${device.networkAddress}' to '${payload.networkAddress}'`);
device.networkAddress = payload.networkAddress;
device.save();
}
L’appairage passe alors en moins de 4 secondes par capteur, sans perte de friendly_name.
💡 Prompt garde-fou pour auditer ou migrer Zigbee2MQTT avec une IA
Tu agis comme expert Zigbee2MQTT, zigbee-herdsman et firmware Texas Instruments Z-Stack (SLZB-06 / SLZB-MR4U).
Avant toute modification sur la configuration Zigbee2MQTT, respecte ces 6 garde-fous :
1. AUDIT NON DESTRUCTIF :
- Inspecte `configuration.yaml`, `coordinator_backup.json` et `database.db` en lecture seule.
- Ne supprime JAMAIS d'entrées dans `database.db` (préservation des `friendly_name` Home Assistant).
- Crée un backup horodaté (`database.db.bak`, `configuration.yaml.bak`) avant toute écriture.
2. CANAL ET PAN ID :
- Vérifie `channel`, `pan_id` et `ext_pan_id` via `zigbee2mqtt/bridge/info`.
- Ne modifie jamais `channel` ou `pan_id` sur un réseau existant sans validation explicite.
3. INTÉGRITÉ NVRAM Z-STACK :
- Utilise uniquement le mécanisme natif complet de `zigbee-herdsman` / `zigpy-znp` sans tronquer les sub-IDs (`ADDRMGR`, `NWK_SEC_MATERIAL`, `APS_KEY_DATA`).
4. RÉAPPAIRAGE D'APPAREILS EXISTANTS (`networkAddress` 16-bit) :
- Dans `onDeviceJoined` et `onDeviceAnnounce`, mets à jour `device.networkAddress = payload.networkAddress; device.save();` AVANT d'émettre `deviceJoined` ou `device.interview()`.
5. CAPTEURS ANCIENS (Xiaomi / Aqara 1re gén & Tuya TS0601) :
- Si un EndDevice connu dans `database.db` échoue son interview à cause de la mise en veille rapide, conserve son profil existant au lieu d'appeler `device.leave()`.
6. VALIDATION :
- Vérifie dans les logs que `zigbee-herdsman` démarre sans erreur, que `bridge/state` est `online`, et compare le nombre d'équipements actifs.
Conclusion
Passer sur un coordinateur PoE Ethernet comme le SMLIGHT SLZB-MR4U améliore nettement la portée et la stabilité du réseau Zigbee. Si des capteurs décrochent après une migration NVRAM, inutile de perdre des heures à débugger des tables fantômes : 1 h 30 de réappairage méthodique (routeurs secteur d’abord, capteurs sur site ensuite, en gardant les friendly_name) suffit pour repartir sur une base propre.
Plus d’informations sur le dépôt : https://github.com/ravindrajob/InfraAtHome
