OpenThread
The CTRL ONE ships configured for WiFi. Its radio also supports Thread, so the panel can join a Thread mesh instead and reach Home Assistant over IPv6. This page covers what that changes, how to read the network dataset, and how to build and flash the Thread firmware.
Thread is optional. If you are setting up a CTRL ONE for the first time, start with the setup guide — the device, the display and the widgets are identical either way.
A CTRL ONE on Thread is not a Matter device. It uses the same ESPHome API as when it it's configured to use WiFi.
Thread and Matter are not the same thing. Thread is a network standard: it gets devices onto a network and lets them reach each other. Matter is an application standard: how a controller and a device describe and control one another. A device can use Thread without Matter, and Matter can run over other networks.
- There is no Matter setup code, no pairing, no fabric and no multi-admin.
- No phone or companion app is needed to move credentials around: the dataset comes from the border router.
- Home Assistant needs neither the Matter integration nor the Thread integration to use the panel — only a border router for the mesh, and the ESPHome integration for the device.
What you need
- A CTRL ONE. Its radio supports Thread, so no extra hardware is needed.
- A Thread border router on your network: Home Assistant's OpenThread Border Router app with a Connect ZBT-1 or Connect ZBT-2, or any other OpenThread-based border router. Without one, a Thread device has no path to the rest of your network.
- The Thread network's active dataset, as a TLV string (see below).
- The ESPHome Device Builder installed on a computer, to edit and install the configuration, plus a USB-C cable in case the panel cannot be reached over the network.
Read the network dataset
Thread networks are protected by an operational dataset: the network key, channel, PAN ID, extended PAN ID and network name, encoded as a single hex TLV string. It is what a device needs to join, so treat it like a WiFi password.
Ask the border router for it on its command line:
ot-ctl dataset active -x
That prints one line of hex, for example:
0e080000000000010000000300001035060004001fffe00208e227ac6a7f24052f0708fdb753eb517cb4d3051062b2442a928d9ea3b947a1618fc4085a030f4f70656e5468726561642d39383733...
In Home Assistant the same dataset is visible in the Thread integration (Settings > Devices & Services > Thread, then Configure): it lists the Thread networks it knows and their datasets, and can take a dataset pasted in as a TLV. Home Assistant can only read the credentials of OpenThread-based border routers, not of vendor border routers such as Google's or Apple's.
Note: the TLV must have an even number of hex characters and be at most 254 bytes. It is a secret — keep it out of public repositories.
Configure the Thread build
The Thread firmware is the WiFi firmware with the network transport swapped: display, sensors, widgets and Home Assistant entities are unchanged. One file is all you need: esphome/core/ctrl-one-thread.yaml is complete on its own, and pulls the OpenThread transport, the LVGL setup and the widgets out of this repository for you.
In the ESPHome Device Builder, use that configuration and set the dataset as a substitution:
substitutions:
thread_tlv: "0e08000000000001000000030000103506..." # ot-ctl dataset active -x
packages:
main:
refresh: 4h
url: https://codeberg.org/elvinhome/ctrl-one
ref: v1.2.2
files:
- esphome/core/thread.yaml # the OpenThread transport
- esphome/core/lvgl.yaml
# ... the same widget packages as in the WiFi configuration
That is the whole change: the dataset, plus one entry in the list of files the configuration imports. esphome/core/thread.yaml carries the OpenThread setup — IPv6, the network credentials, the Thread diagnostics — so there is nothing else to edit, and no other files need to be on your computer.
Also remove the WiFi provisioning helpers — wifi:, esp32_improv: and improv_serial: — if your configuration still has them. Both improv components depend on the WiFi component, and the OpenThread component explicitly conflicts with WiFi, so they cannot be compiled into a Thread build. The shipped firmware keeps all three blocks together in esphome/core/wifi.yaml for exactly this reason.
A Thread panel is configured entirely by its configuration file: there is no Bluetooth or serial provisioning step, because the network credentials are part of the firmware. Changing the dataset means rebuilding and flashing.
Build and flash
- Install the Thread configuration. A panel still running the WiFi build can take this over the air: the running firmware receives the new image over WiFi and reboots into it.
- If no network route is available, flash over USB-C instead — in the Device Builder, Install > Plug into this computer.
- The panel joins the mesh on boot. The dataset from the configuration is applied over anything previously stored on the device, so no provisioning step is needed.
- Home Assistant picks the panel up through the ESPHome integration. If discovery does not reach it, add the device manually in that integration using its hostname or IPv6 address — discovery relies on mDNS over IPv6.
- Later updates work as before: the Device Builder installs over Thread, as long as the border router is up and forwarding IPv6 traffic.
How ESPHome uses Thread
ESPHome's OpenThread component joins a Thread network and then runs the ordinary ESPHome API and OTA server over it. Nothing else in the configuration changes. Points worth knowing:
- Radio. The CTRL ONE's radio supports Thread, so no extra hardware is needed and the panel joins the mesh by itself. ESPHome handles the radio; you only supply the network credentials.
- Credentials. Either the complete dataset as a TLV string, as used here, or the individual fields (
network_key,channel,pan_id,ext_pan_id,network_name,pskc,mesh_local_prefix). Exactly one of the two ways is used, never both. - Device type. FTD (the default) is always awake and can be promoted to a router, which suits a mains-powered panel. MTD with a
poll_periodbecomes a sleepy end device that only wakes to poll its parent: less power, but the ESPHome OTA server does not work while it sleeps. - IPv6 and the border router. Thread is IPv6 end to end. The border router bridges the mesh and your LAN, so the panel is simply another IPv6 host: Home Assistant and the Device Builder both talk to it directly.
- Mesh, not star. Thread devices route for one another. A mains-powered device like the panel can extend coverage to battery-powered Thread devices elsewhere in the house.
- Bandwidth. Thread is designed for many small devices exchanging small amounts of data. Publishing a handful of sensor values and receiving commands is well within that; it is not a transport for media or bulk uploads.
- WiFi is off. The component declares a conflict with the WiFi component, so a Thread build contains no WiFi stack at all — Thread is not a fallback, it is the only transport.
- Provisioning. The improv helpers (Bluetooth and serial) are built on the WiFi component, so on Thread there is nothing to provision at runtime.
- Diagnostics. Besides the parent RSSI, the Thread build publishes the device's Thread role (leader, router, child) as a diagnostic entity in Home Assistant — the first things to look at when the mesh link is weak.
- Flash usage. The Thread stack takes a real slice of the panel's flash, which the interface already fills. If a Thread build stops fitting, trim optional parts of the configuration — the parent RSSI sensor, or widgets and fonts you do not use.
- Status bar. The link icon in the display footer tracks the Home Assistant API connection rather than the radio, so it behaves the same on Thread.
If the panel does not join
- Check the dataset. The TLV has to be copied completely — an even number of hex characters, no line breaks — and come from the network your border router is on.
- Check the border router. It is the only route in and out of the mesh, so it has to be running and have working IPv6 connectivity to your network.
- Check the link. Thread shares the 2.4 GHz band with WiFi and Zigbee. If the panel is far from the border router, the mesh link is weak: move it closer, or add another mains-powered Thread device nearby. The Signal Strength sensor on the panel reports the RSSI of its parent router.
- If Home Assistant does not discover it, add the device by hand in the ESPHome integration using its hostname or IPv6 address.
- Going back to WiFi is a matter of flashing a build with the WiFi package again — over the air while the panel is on the mesh, or over USB if it is not.
Repository
The transport split lives in the firmware repository, and both configurations import what they need from it — you never have to copy these files yourself:
- esphome/core/wifi.yaml — WiFi, the Bluetooth and serial provisioning helpers, and the WiFi signal sensor. This is what the shipped configuration imports.
- esphome/core/thread.yaml — the OpenThread transport.
- esphome/core/ctrl-one-thread.yaml — a complete Thread configuration.
Only one of the two transports can be active at a time: WiFi and OpenThread cannot be compiled into the same firmware.
Further reading: