Skip to content

Runbook: ZBT-1 multiprotocol → Thread-only (OpenThread RCP)

Context: the Pi's ZBT-1 ran the deprecated Silicon Labs Multiprotocol add-on, serving Zigbee (ZHA) and Thread (OTBR) from one radio. After the Zigbee network moves to the ZBT-2 on av, the ZBT-1's only remaining job is Thread border router for the Matter devices (Entry Door Lock, Bedroom C Door). Multiprotocol is EOL upstream; the end state is plain OpenThread RCP firmware + the standalone OTBR add-on.

Thread dataset(s) live in HA's .storage/thread.datasets (2 datasets recorded) — the border router can be rebuilt around the same dataset so Matter devices stay commissioned.

Ordering note (per migration-critic review): this conversion happens EARLY in the Zigbee cutover — immediately after the multiprotocol add-on is UNINSTALLED and before Z2M ever starts — because a merely-stopped multiprotocol add-on (boot: auto) resurrects zigbeed with the old coordinator identity on any Pi reboot, and because doing it early keeps Thread up during the whole Zigbee soak (lock downtime shrinks to ~30–60 min).

Steps

  1. Preserve state (already done pre-cutover, verify):
  2. Active dataset recorded: channel 15, extended PAN f3b647a7fe582081 (in .storage/thread.datasets; TLV export saved to ~/homelab-private/).
  3. Uninstall the Silicon Labs Multiprotocol add-on (part of the Zigbee runbook step 1).
  4. Install the official OpenThread Border Router add-on (Pi): device = ZBT-1 serial by-id, autoflash_firmware: true — the add-on flashes the OpenThread RCP firmware onto the ZBT-1 itself; no manual flashing, no unplugging. (Manual fallback if autoflash misbehaves: universal-silabs-flasher with the skyconnect_openthread_rcp_*.gbl build from nabucasa/silabs-firmware-builder — the tooling is proven on av via the phase-3 probe.)
  5. Rejoin the existing Thread network: in Settings → Devices → Thread, the new OTBR appears with a fresh (wrong) dataset — use "⋮ → make preferred / use existing network" to push the preserved dataset onto it (or WS otbr/set_network). The TBR advertises the original network; Matter devices reattach without re-commissioning.
  6. Verify: OTBR logs show xpan f3b647a7fe582081; Thread panel shows one preferred network; Entry Door Lock + Bedroom C Door reachable. The lock is a sleepy end device — allow 30–60 min to reattach before declaring failure (have the physical key regardless).

Failure fallback

Worst case (dataset refused / devices won't reattach): re-commission the two Matter devices — annoying but small blast radius. The door lock keeps working mechanically/HomeKey throughout.

Operational notes (standalone OTBR on av, 2026-08-01)

The border router on av is the standalone otbr container (not the HA add-on), wired to HA's OTBR integration at http://127.0.0.1:8081. Three hard-won gotchas:

  • Dataset persistence. otbr-agent stores the active dataset under /var/lib/thread (0_<extaddr>.data), NOT /var/lib/otbr. The compose volume must map ./otbr/data:/var/lib/thread — mapping the wrong path silently wipes the network (and orphans the commissioned Matter door devices) on every container recreate. The .data file holds the network key and is gitignored. Reload manually with docker exec otbr ot-ctl dataset set active <tlv>ifconfig upthread start; the preserved home-assistant dataset (ch 15, xpan f3b647a7fe582081) is in HA's .storage/thread.datasets.
  • SkyConnect placement. The ZBT-1 OT-RCP link is marginal behind an unpowered USB hub — otbr reaches leader then drops (RadioSpinelNoResponse) and cycles. It's power-starvation on the shared rail (worse with a hungry neighbour like the KVM at ~72 s intervals, but it still drops periodically with the radios alone). Direct into a rear USB-A port = rock-solid (extension for RF is fine); a powered hub is the expected but untested fix for hub operation. ZBT-2 (native-USB) is fine on the hub. See docs/av-usb-port-map.md.
  • Restart pacing. tools/otbr-run.sh wraps otbr-agent so a flapping RCP can't wedge the stick: it sleeps ~20 s before the container exits, keeping Docker's restarts spaced (a rapid reopen loop toggles the CP210x reset line and wedges the EFR32 → D-state, only a physical power-cycle recovers).
  • Self-heal. stacks/ha/{otbr,zigbee2mqtt}-hotplug.{service,rules} recreate the container when the stick re-enumerates (move/replug). Install the host units via a /tmp script (see AGENTS.md "Privileged host-level installs").