Zigbee Coordinator over the Network: Sonoff + ser2net
My Home Assistant server lives in the basement utility room. My Zigbee network lives on the ground floor and in the garden. That’s the whole problem: the coordinator sits exactly where it’s least useful — inside a metal cabinet, next to a USB 3 enclosure, three concrete floors away from the devices it’s supposed to serve.
Eventually I knew the symptom by heart. A device responds, then it doesn’t, and the log says “device did not respond”. No clean errors, just timeouts. With a battery-powered water valve in an outdoor pit it was reproducible: fine for a few on/off cycles, then gone.
The fix I ended up running isn’t elegant, but it holds: the dongle doesn’t stay at the server. It moves to where the Zigbee network actually is, and Home Assistant talks to it over the LAN — serial over IP.
How this actually works
The important mental model: the Raspberry Pi holding the dongle does no Zigbee logic at all. It knows nothing about your devices or network key. It just forwards a serial port over TCP. The entire coordinator stack still runs in ZHA on your HA host.
Pi (with dongle) → ser2net → TCP/IP → Home Assistant (ZHA)
That also means you keep your existing Zigbee network — as long as you move the same physical dongle. A different dongle means a new network and re-pairing everything.
What you need
| Part | Why |
|---|---|
| Sonoff ZBDongle-P or -E | The coordinator itself. Enumerates as a CP210x (10c4:ea60). |
| Raspberry Pi Zero 2 W | Plenty. The original Zero W is not. |
| USB extension cable | Gets the dongle away from the Pi’s body and WiFi antenna. |
| Static IP / DHCP reservation | Otherwise ZHA loses the coordinator on the next lease. |
What I actually run: a Sonoff Zigbee 3.0 USB Dongle Plus* on a Raspberry Pi Zero 2 W*, with an active USB 2.0 extension cable* in between.
Use the Pi Zero 2 W, not the old Zero W
This isn’t a preference. The original Pi Zero W is a single-core ARMv6 — too slow and too flaky for this job. The Zero 2 W is quad-core and holds up fine. If you’ve got an old Zero W in a drawer, leave it there.
Step 1: Install ser2net
On the Pi:
sudo apt update
sudo apt install -y ser2net
Then find the dongle. And not as /dev/ttyUSB0 — that number shifts the
moment a second USB-serial device shows up:
ls -l /dev/serial/by-id/
# usb-ITead_Sonoff_Zigbee_3.0_USB_Dongle_Plus_xxxxxxxx-if00-port0
Use that stable path in the config. To confirm the CP210x driver bound:
udevadm info -q all -n /dev/ttyUSB0 | grep -iE 'ID_MODEL|ID_USB_DRIVER'
# E: ID_MODEL=Sonoff_Zigbee_3.0_USB_Dongle_Plus
# E: ID_USB_DRIVER=cp210x
Step 2: Configure ser2net
/etc/ser2net.yaml:
connection: &zigbee
accepter: tcp,6638
connector: serialdev,/dev/serial/by-id/usb-ITead_Sonoff_Zigbee_3.0_USB_Dongle_Plus_xxxxxxxx-if00-port0,115200n81,local
options:
max-connections: 1
kickolduser: true
Three things here genuinely matter:
- 115200n81 is correct. Both the ZBDongle-P and the ZBDongle-E run fine at 115200 baud under ZHA.
max-connections: 1— only one process may ever hold the coordinator. Two concurrent clients produce exactly the kind of mess you don’t want to debug.kickolduser: true— without it, after an HA restart the dead old session is still parked on the port and blocks the new one.
Start and enable it:
sudo systemctl enable --now ser2net
sudo systemctl restart ser2net
sudo ss -tlnp | grep 6638
If ss shows the port listening, you’re most of the way there.
Step 3: Pin the IP address
This is not optional polish. ZHA stores the path as a fixed string. If the Pi’s IP changes, the coordinator is gone — and you will absolutely go looking for the problem in the wrong place.
The cleanest option is a DHCP reservation on your router, tied to the Pi’s MAC. That keeps the address centrally managed instead of buried in a config file on the Pi.
Step 4: Point ZHA at it
Back up first. Settings → System → Backups. Changing the coordinator path is a migration, not a click. If it goes sideways without a backup, you’re re-pairing half your house.
Then set the ZHA coordinator path:
socket://192.168.1.50:6638
A quick sanity check from the HA machine before you even open the UI:
nc -zv 192.168.1.50 6638
What does not work
These are the parts that cost me time.
WiFi as the transport is the classic trap. ser2net over WiFi can drop the serial link — and that produces exactly the “device did not respond” timeouts you were trying to get rid of. You trade an RF problem for a network problem and then mistake one for the other. Wired beats WiFi, every time. A USB Ethernet adapter on the Zero 2 W is the reliable path. If it has to be WiFi, only do it with genuinely strong signal at the Pi’s location.
Two clients at once. Zigbee2MQTT and ZHA on the same port — no. One coordinator, one consumer.
Leaving the dongle plugged straight into the Pi. With no extension it sits right next to the WiFi antenna. You’ve relocated the coordinator without fixing the interference.
Blaming the coordinator when a battery device drops. A sleepy end device loses its parent because there’s no router near it. The cure is a mains-powered Zigbee plug next to that device — not a relocated coordinator.
Honestly: when you should not do this
I wouldn’t recommend this build to most readers.
If your problem is just “the dongle sits next to an SSD and picks up noise”, buy a USB 2.0 extension cable for pocket change and move it 1–2 meters away. That alone fixes a large share of Zigbee interference cases, with no extra computer in the signal path that pulls an update one night and takes your Zigbee down with it.
If a single device at the edge of the mesh is struggling, the right answer is a mains-powered Zigbee router near it. That repairs the device → coordinator return path, which relocating the coordinator doesn’t address at all.
ser2net earns its keep in exactly one situation: when your HA host and your Zigbee network are physically separated and you can’t change that. For anything else you’re volunteering an extra, home-made point of failure — if the Pi reboots, the network hiccups, or ser2net wedges, your entire Zigbee network is down, not just one device.
Build checklist
- Pi Zero 2 W, wired Ethernet, DHCP reservation in place
- ser2net installed, device referenced via
/dev/serial/by-id/ max-connections: 1andkickolduser: trueset- Port 6638 reachable from the HA machine with
nc - ZHA backup taken, then switch to
socket://<ip>:6638 - Check LQI values after a few days — compare before and after
Mine has run stably for months. But the honest order of operations stays the same: extension cable first, then a router near the problem devices, and only if that isn’t enough, the coordinator over the network.