[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.

Thanks @djbha for sharing your issue, along with the fixture.
I hit exactly the same problem on fresh installation of HO (+ZBT-2) in Docker, and running on the same host as zigbee2mqtt. Only after flashing it with the most recent stable release of firmware, the HO started to see it again.

I had the same issue when connected to USB 2.0, not able to install the firmware.
Reconnected with USB 3.0 and able to install the firmware for thread.

The same happend to me a few minutes, which naturally is an excellent reason to create an account and lament about this as well. :smiley:

Thanks for the details, I just had to chmod /dev/ttyACM0 on my linux machine and then the toolbox happily flashed my ZBT-2. Interestingly, the tool said the device already contained the correct firmware, maybe this were just issues with the docker/usb setup and the device just couldn’t be found again after flashing it?