Follow-up to #364 (merged). Corrects the cutover mechanism before it is ever run.
Why
#364 hand-set advanced.pan_id/ext_pan_id/network_key in the Z2M config from the live ZHA values. That is the risky path: the extended PAN ID byte order is reversed between ZHA (zigpy) and Z2M (zigbee-herdsman). Loading the ZHA value as-is makes herdsman see a mismatch and form a new network → every device drops → mass re-pair (the explicit non-goal). Mixing a manual ext_pan_id with a backup also conflicts.
What
Restore an Open Coordinator Backup (the shared zigpy↔herdsman interchange format) instead — it pins byte orders in-spec and handles the reversal on import.
New zigbee2mqtt-coordinator-backup ExternalSecret (OpenBao) holds the full backup JSON (network key → OpenBao only, never git); replaces zigbee2mqtt-network-externalsecret.yaml.
Init container seeds /app/data/coordinator_backup.json on first run only (also guarded on database.db so Z2M state is never overwritten); renders only the MQTT password into secret.yaml.
Runbook: Step 1 now exports the backup from zigbee.db via zigpy (non-disruptive — no second radio session); added an explicit device re-interview step (devices stay joined, but Z2M's DB starts empty).
Verified live (read-only)
Coordinator: pan 0x8b2b, ext_pan_id fc7a735d08c15476, channel 25; zigpy emits a valid open-coordinator-backup with 15 device records embedded. Kustomize builds; --dry-run=server clean.
Follow-up to #364 (merged). Corrects the cutover **mechanism** before it is ever run.
## Why
#364 hand-set `advanced.pan_id`/`ext_pan_id`/`network_key` in the Z2M config from the live ZHA values. That is the **risky** path: the extended PAN ID byte order is **reversed** between ZHA (zigpy) and Z2M (zigbee-herdsman). Loading the ZHA value as-is makes herdsman see a mismatch and **form a new network → every device drops → mass re-pair** (the explicit non-goal). Mixing a manual `ext_pan_id` with a backup also conflicts.
## What
Restore an **Open Coordinator Backup** (the shared zigpy↔herdsman interchange format) instead — it pins byte orders in-spec and handles the reversal on import.
- New `zigbee2mqtt-coordinator-backup` ExternalSecret (OpenBao) holds the full backup JSON (network key → OpenBao only, never git); replaces `zigbee2mqtt-network-externalsecret.yaml`.
- Init container seeds `/app/data/coordinator_backup.json` on first run only (also guarded on `database.db` so Z2M state is never overwritten); renders only the MQTT password into `secret.yaml`.
- Config drops `advanced.pan_id/ext_pan_id/network_key/channel`.
- Runbook: Step 1 now exports the backup from `zigbee.db` via zigpy (non-disruptive — no second radio session); added an explicit **device re-interview** step (devices stay joined, but Z2M's DB starts empty).
## Verified live (read-only)
Coordinator: pan `0x8b2b`, ext_pan_id `fc7a735d08c15476`, channel `25`; zigpy emits a valid `open-coordinator-backup` with 15 device records embedded. Kustomize builds; `--dry-run=server` clean.
**Sources:** [ZHA→Z2M no-re-pair discussion](https://github.com/Koenkk/zigbee2mqtt/discussions/24478) · [Z2M FAQ (backup/restore)](https://www.zigbee2mqtt.io/guide/faq/)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Correct the cutover mechanism before it ever runs. The prior approach hand-set
advanced.pan_id/ext_pan_id/network_key in the Z2M config from the ZHA values.
That is the risky path: the extended PAN ID byte order is REVERSED between ZHA
(zigpy) and Z2M (zigbee-herdsman), so loading the ZHA value as-is makes herdsman
see a mismatch and FORM A NEW network — dropping every device (the explicit
non-goal). Mixing a manual ext_pan_id with a backup also conflicts (Z2M ignores
the reversed value).
Instead restore an Open Coordinator Backup, the shared zigpy<->herdsman
interchange format, which pins byte orders in-spec and handles the reversal on
import:
- New zigbee2mqtt-coordinator-backup ExternalSecret (OpenBao) holds the full
backup JSON (contains the network key -> OpenBao only, never git), replacing
zigbee2mqtt-network-externalsecret.yaml.
- Init container seeds /app/data/coordinator_backup.json on first run only
(guarded on database.db too, so Z2M's own state is never overwritten), and
now renders only the mqtt password into secret.yaml.
- Config drops advanced.pan_id/ext_pan_id/network_key/channel entirely; the
network comes from the restored backup.
Runbook updated: Step 1 exports the Open Coordinator Backup from zigbee.db via
zigpy (non-disruptive — no second radio session); Step 2 stores it in OpenBao;
added an explicit device re-interview step (devices stay joined but Z2M's
database starts empty), and a PVC-clear note in rollback.
Verified live: coordinator has pan 0x8b2b, ext_pan_id fc7a735d08c15476,
channel 25; zigpy emits a valid open-coordinator-backup for it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Follow-up to #364 (merged). Corrects the cutover mechanism before it is ever run.
Why
#364 hand-set
advanced.pan_id/ext_pan_id/network_keyin the Z2M config from the live ZHA values. That is the risky path: the extended PAN ID byte order is reversed between ZHA (zigpy) and Z2M (zigbee-herdsman). Loading the ZHA value as-is makes herdsman see a mismatch and form a new network → every device drops → mass re-pair (the explicit non-goal). Mixing a manualext_pan_idwith a backup also conflicts.What
Restore an Open Coordinator Backup (the shared zigpy↔herdsman interchange format) instead — it pins byte orders in-spec and handles the reversal on import.
zigbee2mqtt-coordinator-backupExternalSecret (OpenBao) holds the full backup JSON (network key → OpenBao only, never git); replaceszigbee2mqtt-network-externalsecret.yaml./app/data/coordinator_backup.jsonon first run only (also guarded ondatabase.dbso Z2M state is never overwritten); renders only the MQTT password intosecret.yaml.advanced.pan_id/ext_pan_id/network_key/channel.zigbee.dbvia zigpy (non-disruptive — no second radio session); added an explicit device re-interview step (devices stay joined, but Z2M's DB starts empty).Verified live (read-only)
Coordinator: pan
0x8b2b, ext_pan_idfc7a735d08c15476, channel25; zigpy emits a validopen-coordinator-backupwith 15 device records embedded. Kustomize builds;--dry-run=serverclean.Sources: ZHA→Z2M no-re-pair discussion · Z2M FAQ (backup/restore)
🤖 Generated with Claude Code
Correct the cutover mechanism before it ever runs. The prior approach hand-set advanced.pan_id/ext_pan_id/network_key in the Z2M config from the ZHA values. That is the risky path: the extended PAN ID byte order is REVERSED between ZHA (zigpy) and Z2M (zigbee-herdsman), so loading the ZHA value as-is makes herdsman see a mismatch and FORM A NEW network — dropping every device (the explicit non-goal). Mixing a manual ext_pan_id with a backup also conflicts (Z2M ignores the reversed value). Instead restore an Open Coordinator Backup, the shared zigpy<->herdsman interchange format, which pins byte orders in-spec and handles the reversal on import: - New zigbee2mqtt-coordinator-backup ExternalSecret (OpenBao) holds the full backup JSON (contains the network key -> OpenBao only, never git), replacing zigbee2mqtt-network-externalsecret.yaml. - Init container seeds /app/data/coordinator_backup.json on first run only (guarded on database.db too, so Z2M's own state is never overwritten), and now renders only the mqtt password into secret.yaml. - Config drops advanced.pan_id/ext_pan_id/network_key/channel entirely; the network comes from the restored backup. Runbook updated: Step 1 exports the Open Coordinator Backup from zigbee.db via zigpy (non-disruptive — no second radio session); Step 2 stores it in OpenBao; added an explicit device re-interview step (devices stay joined but Z2M's database starts empty), and a PVC-clear note in rollback. Verified live: coordinator has pan 0x8b2b, ext_pan_id fc7a735d08c15476, channel 25; zigpy emits a valid open-coordinator-backup for it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>