[SOLVED] Recovering from a failed ZBT-2 flash

Hi! I’m new here, so sorry if this is in the wrong place etc. I recently bought a ZBT-2 for my Home Assistant Green and the initial flash failed. I tried the web flasher and the reset button to no avail. I did a lot of searching and couldn’t find anything that worked for me. I thought my device was truly bricked and almost returned it. What finally worked was using the universal-silabs-flasher CLI tool. I’m posting here and including all the gory details in the hopes that it will help others searching for a similar issue, or at least offer some things to try.

I am running the latest (as of this posting) Home Assistant (updating was a common suggestion that didn’t apply to me):

Core 2026.1.2
Supervisor 2026.01.1
Operating System 17.0
Frontend 20260107.2

When I plugged the ZBT-2 into the HA Green, the device was auto-detected by Home Assistant. It prompted me to set it up as Zigbee or Thread. I chose Thread and it began to update the firmware. The update failed (can’t remember the exact message). The logs show this:

Logger: homeassistant.components.homeassistant_hardware.firmware_config_flow
Source: components/homeassistant_hardware/firmware_config_flow.py:203
integration: Home Assistant Hardware (documentation, issues)
First occurred: 7:19:18 PM (1 occurrence)
Last logged: 7:19:18 PM

Failed to flash firmware
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/homeassistant_hardware/firmware_config_flow.py", line 203, in _install_firmware_step
    await self.firmware_install_task
  File "/usr/src/homeassistant/homeassistant/components/homeassistant_hardware/firmware_config_flow.py", line 310, in _install_firmware
    self._probed_firmware_info = await async_flash_silabs_firmware(
                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<9 lines>...
    )
    ^
  File "/usr/src/homeassistant/homeassistant/components/homeassistant_hardware/util.py", line 444, in async_flash_silabs_firmware
    raise HomeAssistantError("Failed to probe the firmware after flashing")
homeassistant.exceptions.HomeAssistantError: Failed to probe the firmware after flashing

I tried using the web updater at toolbox.openhomefoundation.org. When I click “Update firmware” it detected the device for me to select, but the update failed. Downloading the log showed this:

2026-01-20 20:18:52 emscripten zigpy.appdb[42] DEBUG SQLite version for <webserial_transport.MockSqlite3 object at 0x111c630>: 3.31.1
2026-01-20 20:18:52 emscripten universal_silabs_flasher.flasher[42] INFO Triggering rts_dtr bootloader
2026-01-20 20:18:52 emscripten webserial_transport[42] DEBUG Opening a serial connection at 115200 with rtscts=False

I noticed that the device (/dev/ttyACM0) is owned by root:uucp. Running stat /dev/ttyACM0 shows this:

  File: /dev/ttyACM0
  Size: 0         	Blocks: 0          IO Block: 4096   character special file
Device: 0,6	Inode: 2127        Links: 1     Device type: 166,0
Access: (0660/crw-rw----)  Uid: (    0/    root)   Gid: (  986/    uucp)
Access: 2026-01-20 21:21:07.787189555 -0600
Modify: 2026-01-20 21:21:07.787189555 -0600
Change: 2026-01-20 21:21:07.787189555 -0600
 Birth: 2026-01-20 21:21:07.777121978 -0600

Some forum posts say that you need to be in the ‘dialup’ group. Maybe it varies by distro. I’m running Arch and it was ‘uucp’ for me.

I added my user to the uucp group (sudo usermod -aG uucp [username]) and restarted. I checked that running groups showed the ‘uucp’ group. Then I tried flashing the firmware again. It still failed, but produced more logs:

2026-01-20 20:44:36 emscripten zigpy.appdb[42] DEBUG SQLite version for <webserial_transport.MockSqlite3 object at 0xe5dc28>: 3.31.1
2026-01-20 20:44:36 emscripten universal_silabs_flasher.flasher[42] INFO Triggering rts_dtr bootloader
2026-01-20 20:44:36 emscripten webserial_transport[42] DEBUG Opening a serial connection at 115200 with rtscts=False
2026-01-20 20:44:36 emscripten zigpy.serial[42] DEBUG Connection made: <webserial_transport.WebSerialTransport object at 0x18a40d8>
2026-01-20 20:44:36 emscripten universal_silabs_flasher.common[42] DEBUG Setting UART signals: rts=1, cts=-, dtr=0
2026-01-20 20:44:36 emscripten universal_silabs_flasher.common[42] DEBUG Setting UART signals: rts=0, cts=-, dtr=1
2026-01-20 20:44:37 emscripten universal_silabs_flasher.common[42] DEBUG Setting UART signals: rts=0, cts=-, dtr=0
2026-01-20 20:44:37 emscripten zigpy.serial[42] DEBUG Waiting for serial port to close
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Flushing pending writes
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Waiting for pending writes to finish
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Received exit sentinel, exiting
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Closing serial port
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Calling protocol connection_lost(None)
2026-01-20 20:44:37 emscripten zigpy.serial[42] DEBUG Connection lost: None
2026-01-20 20:44:37 emscripten universal_silabs_flasher.flasher[42] INFO Triggering baudrate bootloader
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Opening a serial connection at 150 with rtscts=False
2026-01-20 20:44:37 emscripten zigpy.serial[42] DEBUG Connection made: <webserial_transport.WebSerialTransport object at 0x1dde228>
2026-01-20 20:44:37 emscripten zigpy.serial[42] DEBUG Waiting for serial port to close
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Flushing pending writes
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Waiting for pending writes to finish
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Received exit sentinel, exiting
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Closing serial port
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Calling protocol connection_lost(None)
2026-01-20 20:44:37 emscripten zigpy.serial[42] DEBUG Connection lost: None
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Opening a serial connection at 300 with rtscts=False
2026-01-20 20:44:37 emscripten zigpy.serial[42] DEBUG Connection made: <webserial_transport.WebSerialTransport object at 0x1d94600>
2026-01-20 20:44:37 emscripten zigpy.serial[42] DEBUG Waiting for serial port to close
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Flushing pending writes
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Waiting for pending writes to finish
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Received exit sentinel, exiting
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Closing serial port
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Calling protocol connection_lost(None)
2026-01-20 20:44:37 emscripten zigpy.serial[42] DEBUG Connection lost: None
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Opening a serial connection at 1200 with rtscts=False
2026-01-20 20:44:37 emscripten zigpy.serial[42] DEBUG Connection made: <webserial_transport.WebSerialTransport object at 0x1cef4a8>
2026-01-20 20:44:37 emscripten zigpy.serial[42] DEBUG Waiting for serial port to close
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Flushing pending writes
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Waiting for pending writes to finish
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Received exit sentinel, exiting
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Closing serial port
2026-01-20 20:44:37 emscripten webserial_transport[42] DEBUG Calling protocol connection_lost(None)
2026-01-20 20:44:37 emscripten zigpy.serial[42] DEBUG Connection lost: None
2026-01-20 20:44:41 emscripten webserial_transport[42] DEBUG Opening a serial connection at 115200 with rtscts=False
2026-01-20 20:44:41 emscripten zigpy.serial[42] DEBUG Connection made: <webserial_transport.WebSerialTransport object at 0x1d81688>
2026-01-20 20:44:41 emscripten universal_silabs_flasher.gecko_bootloader[42] DEBUG Sending data b'\n'
2026-01-20 20:44:41 emscripten universal_silabs_flasher.gecko_bootloader[42] DEBUG Sending data GeckoBootloaderOption.EBL_INFO
2026-01-20 20:44:43 emscripten zigpy.serial[42] DEBUG Waiting for serial port to close
2026-01-20 20:44:43 emscripten webserial_transport[42] DEBUG Flushing pending writes
2026-01-20 20:44:43 emscripten webserial_transport[42] DEBUG Waiting for pending writes to finish
2026-01-20 20:44:43 emscripten webserial_transport[42] DEBUG Received exit sentinel, exiting
2026-01-20 20:44:43 emscripten webserial_transport[42] DEBUG Closing serial port
2026-01-20 20:44:43 emscripten webserial_transport[42] DEBUG Calling protocol connection_lost(None)
2026-01-20 20:44:43 emscripten zigpy.serial[42] DEBUG Connection lost: None
2026-01-20 20:44:43 emscripten universal_silabs_flasher.flasher[42] INFO Probing ApplicationType.GECKO_BOOTLOADER at 115200 baud
2026-01-20 20:44:43 emscripten webserial_transport[42] DEBUG Opening a serial connection at 115200 with rtscts=False
2026-01-20 20:44:43 emscripten zigpy.serial[42] DEBUG Connection made: <webserial_transport.WebSerialTransport object at 0x1d6bba0>
2026-01-20 20:44:43 emscripten universal_silabs_flasher.gecko_bootloader[42] DEBUG Sending data b'\n'
2026-01-20 20:44:43 emscripten universal_silabs_flasher.gecko_bootloader[42] DEBUG Sending data GeckoBootloaderOption.EBL_INFO
2026-01-20 20:44:45 emscripten zigpy.serial[42] DEBUG Waiting for serial port to close
2026-01-20 20:44:45 emscripten webserial_transport[42] DEBUG Flushing pending writes
2026-01-20 20:44:45 emscripten webserial_transport[42] DEBUG Waiting for pending writes to finish
2026-01-20 20:44:45 emscripten webserial_transport[42] DEBUG Received exit sentinel, exiting
2026-01-20 20:44:45 emscripten webserial_transport[42] DEBUG Closing serial port
2026-01-20 20:44:45 emscripten webserial_transport[42] DEBUG Calling protocol connection_lost(None)
2026-01-20 20:44:45 emscripten zigpy.serial[42] DEBUG Connection lost: None
2026-01-20 20:44:45 emscripten universal_silabs_flasher.flasher[42] DEBUG Probe timed out
2026-01-20 20:44:45 emscripten universal_silabs_flasher.flasher[42] INFO Probing ApplicationType.EZSP at 460800 baud
2026-01-20 20:44:45 emscripten webserial_transport[42] DEBUG Opening a serial connection at 460800 with rtscts=False
2026-01-20 20:44:45 emscripten zigpy.serial[42] DEBUG Connection made: <bellows.ash.AshProtocol object at 0x1d01b38>
2026-01-20 20:44:45 emscripten bellows.ezsp[42] DEBUG Resetting EZSP
2026-01-20 20:44:45 emscripten bellows.uart[42] DEBUG Resetting ASH
2026-01-20 20:44:45 emscripten bellows.ash[42] DEBUG Sending frame 32*CANCEL RstFrame() FLAG
2026-01-20 20:44:45 emscripten bellows.ash[42] DEBUG Sending data  1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1ac038bc7e
2026-01-20 20:44:47 emscripten bellows.ezsp[42] DEBUG EZSP startup/reset failed, retrying (1/3): TimeoutError()
2026-01-20 20:44:47 emscripten bellows.ezsp[42] DEBUG Resetting EZSP
2026-01-20 20:44:47 emscripten bellows.uart[42] DEBUG Resetting ASH
2026-01-20 20:44:47 emscripten bellows.ash[42] DEBUG Sending frame 32*CANCEL RstFrame() FLAG
2026-01-20 20:44:47 emscripten bellows.ash[42] DEBUG Sending data  1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1ac038bc7e
2026-01-20 20:44:50 emscripten bellows.ezsp[42] DEBUG EZSP startup/reset failed, retrying (2/3): TimeoutError()
2026-01-20 20:44:50 emscripten bellows.ezsp[42] DEBUG Resetting EZSP
2026-01-20 20:44:50 emscripten bellows.uart[42] DEBUG Resetting ASH
2026-01-20 20:44:50 emscripten bellows.ash[42] DEBUG Sending frame 32*CANCEL RstFrame() FLAG
2026-01-20 20:44:50 emscripten bellows.ash[42] DEBUG Sending data  1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1ac038bc7e
2026-01-20 20:44:52 emscripten zigpy.serial[42] DEBUG Waiting for serial port to close
2026-01-20 20:44:52 emscripten webserial_transport[42] DEBUG Flushing pending writes
2026-01-20 20:44:52 emscripten webserial_transport[42] DEBUG Waiting for pending writes to finish
2026-01-20 20:44:52 emscripten webserial_transport[42] DEBUG Received exit sentinel, exiting
2026-01-20 20:44:52 emscripten webserial_transport[42] DEBUG Closing serial port
2026-01-20 20:44:52 emscripten webserial_transport[42] DEBUG Calling protocol connection_lost(None)
2026-01-20 20:44:52 emscripten zigpy.serial[42] DEBUG Connection lost: None
2026-01-20 20:44:52 emscripten bellows.uart[42] DEBUG Connection lost: None
2026-01-20 20:44:52 emscripten universal_silabs_flasher.flasher[42] DEBUG Probe timed out
2026-01-20 20:44:52 emscripten universal_silabs_flasher.flasher[42] INFO Probing ApplicationType.SPINEL at 460800 baud
2026-01-20 20:44:52 emscripten webserial_transport[42] DEBUG Opening a serial connection at 460800 with rtscts=False
2026-01-20 20:44:52 emscripten zigpy.serial[42] DEBUG Connection made: <webserial_transport.WebSerialTransport object at 0x18e2648>
2026-01-20 20:44:52 emscripten universal_silabs_flasher.spinel[42] DEBUG Sending frame SpinelFrame(header=SpinelHeader(transaction_id=0, network_link_id=0, flag=2), command_id=<CommandID.RESET: 1>, data=b'\x02')
2026-01-20 20:44:52 emscripten universal_silabs_flasher.spinel[42] DEBUG Sending data b'~\x80\x01\x02\xea\xf0~'
2026-01-20 20:44:54 emscripten universal_silabs_flasher.spinel[42] DEBUG Device did not respond to reset, continuing
2026-01-20 20:44:54 emscripten universal_silabs_flasher.spinel[42] DEBUG Sending frame SpinelFrame(header=SpinelHeader(transaction_id=3, network_link_id=0, flag=2), command_id=<CommandID.PROP_VALUE_GET: 2>, data=b'\x02')
2026-01-20 20:44:54 emscripten universal_silabs_flasher.spinel[42] DEBUG Sending data b'~\x83\x02\x02\xe65~'
2026-01-20 20:44:56 emscripten universal_silabs_flasher.spinel[42] DEBUG Failed to send SpinelFrame(header=SpinelHeader(network_link_id=0, flag=2), command_id=<CommandID.PROP_VALUE_GET: 2>, data=b'\x02'), trying again in 0.10s (attempt 1 of 3)
2026-01-20 20:44:57 emscripten universal_silabs_flasher.spinel[42] DEBUG Sending frame SpinelFrame(header=SpinelHeader(transaction_id=3, network_link_id=0, flag=2), command_id=<CommandID.PROP_VALUE_GET: 2>, data=b'\x02')
2026-01-20 20:44:57 emscripten universal_silabs_flasher.spinel[42] DEBUG Sending data b'~\x83\x02\x02\xe65~'
2026-01-20 20:44:59 emscripten universal_silabs_flasher.spinel[42] DEBUG Failed to send SpinelFrame(header=SpinelHeader(network_link_id=0, flag=2), command_id=<CommandID.PROP_VALUE_GET: 2>, data=b'\x02'), trying again in 0.10s (attempt 2 of 3)
2026-01-20 20:44:59 emscripten universal_silabs_flasher.spinel[42] DEBUG Sending frame SpinelFrame(header=SpinelHeader(transaction_id=3, network_link_id=0, flag=2), command_id=<CommandID.PROP_VALUE_GET: 2>, data=b'\x02')
2026-01-20 20:44:59 emscripten universal_silabs_flasher.spinel[42] DEBUG Sending data b'~\x83\x02\x02\xe65~'
2026-01-20 20:45:01 emscripten universal_silabs_flasher.spinel[42] DEBUG Failed to send SpinelFrame(header=SpinelHeader(network_link_id=0, flag=2), command_id=<CommandID.PROP_VALUE_GET: 2>, data=b'\x02'), trying again in 0.10s (attempt 3 of 3)
2026-01-20 20:45:01 emscripten zigpy.serial[42] DEBUG Waiting for serial port to close
2026-01-20 20:45:01 emscripten webserial_transport[42] DEBUG Flushing pending writes
2026-01-20 20:45:01 emscripten webserial_transport[42] DEBUG Waiting for pending writes to finish
2026-01-20 20:45:01 emscripten webserial_transport[42] DEBUG Received exit sentinel, exiting
2026-01-20 20:45:01 emscripten webserial_transport[42] DEBUG Closing serial port
2026-01-20 20:45:01 emscripten webserial_transport[42] DEBUG Calling protocol connection_lost(None)
2026-01-20 20:45:01 emscripten zigpy.serial[42] DEBUG Connection lost: None
2026-01-20 20:45:01 emscripten universal_silabs_flasher.flasher[42] DEBUG Probe timed out

I tried the web updater with different cables, different USB ports and a different PC (Ubuntu 24.04). I don’t have access to a native Windows PC but I tried it in a VM with USB passthrough and (unsurprisingly) it didn’t work. Maybe there is a special way to pass through the serial connection, but I don’t know how.

In every case, the device LED never lights up when I plug it in and the web updater always failed.

When I press the reset button (for up to 20 seconds), nothing happens (it never blinks any color).

I tried performing a hardware reset:

  1. Unplug the ZBT-2 for 30 seconds or more.
  2. Press and hold the reset button down.
  3. While continuing to hold the button, plug the stick into a PC.
  4. Continue holding the button for about 5–10 seconds, then release it.
  5. Check if the LED blinks or stays solid.

Nothing happened. No light at all.

Finally I tried running the universal-silabs-flasher. I used pipx to run it in an isolated environment vs installing it globally.

pipx run universal-silabs-flasher --device /dev/ttyACM0 probe

Gave this output:

2026-01-20 21:49:04.419 compy universal_silabs_flasher.flasher INFO Probing ApplicationType.GECKO_BOOTLOADER at 115200 baud
2026-01-20 21:49:06.439 compy universal_silabs_flasher.flasher INFO Probing ApplicationType.EZSP at 115200 baud
2026-01-20 21:49:13.953 compy universal_silabs_flasher.flasher INFO Probing ApplicationType.EZSP at 460800 baud
2026-01-20 21:49:21.470 compy universal_silabs_flasher.flasher INFO Probing ApplicationType.SPINEL at 460800 baud
2026-01-20 21:49:21.487 compy universal_silabs_flasher.flasher INFO Detected ApplicationType.SPINEL, version 'SL-OPENTHREAD/2.4.4.0_GitHub-7074a43e4' (2.4.4.0) at 460800 baudrate (bootloader baudrate None)

Signs of life!

I tried flashing the zigbee firmware (in case the thread firmware was the problem):

pipx run universal-silabs-flasher --device /dev/ttyACM0 flash --firmware ~/Downloads/zbt2_zigbee_ncp_7.4.4.6.gbl --allow-cross-flashing

--allow-cross-flashing is a safety check to say “I’m really sure I want to replace thread with zigbee.”

If you skip that, you may get: “Error: Running image type FirmwareImageType.OPENTHREAD_RCP does not match firmware image type FirmwareImageType.ZIGBEE_NCP. If you intend to cross-flash, run with --allow-cross-flashing.”

I forgot to capture the output, but it flashed successfully! I plugged the ZBT-2 back into my Home Assistant Green, and it was detected. I set it up like a new device and this time the thread firmware flashed successfully. I’m now up and running!

Hope that helps anyone else in a similar situation.