HA Docker with SLZB-07Mg24 Matter/Thread USB

Hi All, brand you to all of this. Trying to bringup HA with the USB dongles. Got one of each (ZWave, Zigbee and Matter/Thread). To make things worse, setting things up using docker compose.

I have performed the following steps:

  1. Using SMLIGHT SLZB-07Mg24 USB stick for Matter/Thread. I’ve updated the firmware to Thread already.
  2. Added matter-server container to docker compose.
  3. Installed Matter(Beta) integration and connected it to the matter-server websocket server. Looks like that worked.

Yet I didn’t get to the point to specify which USB device to use for Thread. Would I need to bring-up the OpenThreadBorderRouter container and point it to the Thread USB dongle (SLZB-07Mg24)?

I would appreciate some input on this. Very hard to find docs on this. TY!
Below is how my docker compose looks like.

I also have another Matter network provided by the Apple TV. Would it cause any troubles?

services:
  homeassistant:
    container_name: homeassistant
    image: "ghcr.io/home-assistant/home-assistant:stable"
    volumes:
      - /home/ubuntu/home-assistant/config:/config
      - /etc/localtime:/etc/localtime:ro
      - /run/dbus:/run/dbus:ro
    restart: unless-stopped
    privileged: true
    network_mode: host

  zwavejs2mqtt:
    container_name: zwavejs2mqtt
    image: zwavejs/zwavejs2mqtt:latest
    restart: always
    tty: true
    stop_signal: SIGINT
    environment:
      - SESSION_SECRET=xxxxxxxxxxxxx
      - ZWAVEJS_EXTERNAL_CONFIG=/usr/src/app/store/.config-db
    devices:
      - '/dev/serial/by-id/usb-Zooz_800_Z-Wave_Stick_533D004242-if00:/dev/zwave'
    volumes:
      - /home/ubuntu/home-assistant/addons/z-wave-js/:/usr/src/app/store
    network_mode: host

  matter-server:
    container_name: matter-server
    image: ghcr.io/home-assistant-libs/python-matter-server:stable
    restart: unless-stopped
    security_opt:
      - apparmor=unconfined
    volumes:
      - /home/ubuntu/home-assistant/addons/matter-server:/data
      - /run/dbus:/run/dbus:ro
    network_mode: host

  otbr:
    image: siliconlabsinc/openthread-border-router:latest
    privileged: true
    network_mode: host
    devices:
      - "/dev/ttyACM0:/dev/ttyACM0" # Adjust if your RCP is on a different port
    environment:
      - OTBR_DOCKER_MODE=rcp # Use a physical RCP
      - OTBR_RADIO_URL="spinel+hdlc:///dev/ttyACM0" # Adjust if your RCP is on a different port
    volumes:
      - ./otbr-data:/var/lib/otbr # Persist OTBR data

You should create a docker network that the containers can use to communicate on.
You should also add hostnames to your containers to allow, for example, connection to zwave container at hostname:port instead of IP:port

No. You can add matter devices to multiple networks.
I never used apple but in HA there is a “share device” link. It puts the device in commisioning mode and allows it to be paired to another network. Pretty sure this is standard for all matter hubs.

That’s another way of doing, but I have started simple with using host network. That way I can connect using localhost:port.

But my question was more about setting up Thread

I have the same four containers on my server. I also have HomePods mini with TBR function which shouldn’t interfere with OTBR. I keep my two networks separate, as different TBR vendors don’t play well together at the moment.

Both HA and Matter containers must be run with network_mode = host; openthread works best in host mode but can be made to work with macvlan networks. Zwave only uses one port so doesn’t need host mode.

(Zwavejs2mqtt has been renamed zwavejs-ui by the way. Not sure if they’re maintaining containers under the old name still so consider switching at some point.)

OTBR can be tricky to run in docker. There are a few topics on this forum but you’re right about minimal documentation. My docker-compose.yaml looks like this; note I’m using a different image which perhaps explains the difference in environment variables, which is what assigns the USB port to the otbr-agent process:

services:
  openthread:
    container_name: openthread
    image: openthread/otbr
    restart: unless-stopped
    privileged: true
    tty: true
    network_mode: host
    environment:
      - RADIO_URL=spinel+hdlc+uart:///dev/ttyUSB0?uart-baudrate=460800
      - DEBUG_LEVEL=4
    devices:
      - '/dev/ttyACM1:/dev/ttyUSB0'
    volumes:
      - ./data:/var/lib/thread
      - ./config:/app/etc/docker

If it works, you should be able to open the web console at your server port 8080, and execute commands in the container to check the status, such as:

peter@felix:~$ docker exec -it openthread /usr/sbin/ot-ctl dataset active
Active Timestamp: 1
Channel: 11
Channel Mask: 0x07fff800
Ext PAN ID: c81e32a04a3bcd0f
Mesh Local Prefix: fd34:c5c5:ecec:7f7f::/64
Network Key: 1e5511343b529c1e96ac7919cd0abc0a
Network Name: OpenThreadDocker
PAN ID: 0x1abc
PSKc: redacted
Security Policy: 672 onrc 0
Done

@peterxian, I had some progress with this. Your instructions were definitely helpful. I managed to get the OTBR running and could “Form a new network”. Though I am still stuck at the next step - adding the Open Thread Border Routing integration. The HA refuses to connect to “http://localhost:8888/” when adding an integration. This is what my docker compose looks like:

services:

  homeassistant:
    container_name: homeassistant
    image: "ghcr.io/home-assistant/home-assistant:stable"
    volumes:
      - /home/ubuntu/home-assistant/config:/config
      - /etc/localtime:/etc/localtime:ro
      - /run/dbus:/run/dbus:ro
      - /dev/serial/by-id:/dev/serial/by-id:ro
    restart: unless-stopped
    privileged: true
    ports:
      - 8123:8123
    network_mode: host 

  otbr:
    hostname: otbr
    container_name: otbr
    image: "openthread/otbr:latest"
    privileged: true
    sysctls:
      net.ipv6.conf.all.disable_ipv6: 0
      net.ipv4.conf.all.forwarding: 1
      net.ipv6.conf.all.forwarding: 1
      net.ipv6.conf.all.accept_ra_rt_info_max_plen: 64
      net.ipv6.conf.all.accept_ra: 2
    environment:
      - RADIO_URL=spinel+hdlc+uart:///dev/ttyUsbTread?uart-baudrate=460800
      - DEBUG_LEVEL=4
    devices:
      - "/dev/serial/by-id/usb-SMLIGHT_SMLIGHT_SLZB-07Mg24_6add7a39f772ed118f6073773dbf42d5-if00-port0:/dev/ttyUsbTread"
    volumes:
      - ./openthread/data:/var/lib/thread
    ports:
      - 8888:80
    networks:
      - ha

  matter-server:
    container_name: matter-server
    image: ghcr.io/home-assistant-libs/python-matter-server:stable
    restart: unless-stopped
    security_opt:
      - apparmor=unconfined
    volumes:
      - /home/ubuntu/home-assistant/addons/matter-server:/data
      - /run/dbus:/run/dbus:ro
    networks:
      - ha

  zwave-js-ui:
    hostname: zwave-js-ui
    container_name: zwave-js-ui
    image: zwavejs/zwave-js-ui:latest
    restart: always
    tty: true
    stop_signal: SIGINT
    environment:
      - SESSION_SECRET=xxxx
      - ZWAVEJS_EXTERNAL_CONFIG=/usr/src/app/store/.config-db
    devices:
      - "/dev/serial/by-id/usb-Zooz_800_Z-Wave_Stick_533D004242-if00:/dev/zwave"
    volumes:
      - /home/ubuntu/home-assistant/addons/z-wave-js/:/usr/src/app/store
    ports:
     - "8091:8091" # port for web interface
     - "3000:3000" # port for Z-Wave JS websocket server
    networks:
      - ha
  
networks:
  ha:
    external: true

The thread network seems to be active:

docker exec -it otbr  /usr/sbin/ot-ctl dataset active
Active Timestamp: 1
Channel: 15
Wake-up Channel: 24
Channel Mask: 0x07fff800
Ext PAN ID: 1111111122222222
Mesh Local Prefix: fd17:1111:1111:1111::/64
Network Key: 00112233445566778899aabbccddeeff
Network Name: OpenThreadDemo
PAN ID: 0x1234
PSKc: 11111111
Security Policy: 672 onrc 0
Done

try using otbr hostname instead of localhost

You are still not using the right network config. Again, OpenThread should be in host mode or use macvlan networking. (Matter should be host mode, too, per its docs). This is required for mDNS support.

Also, at least in my image, the web interface runs on port 8080, not any of the ports you are using. You can check the otbr-web process should be running with a -p parameter specifying which port to use.

That’s very odd TBH. I have switched back to using host mode. This is what the docker compose looks like. The OTBR web interface is accessible now through “http://localhost:8080” (had to set the HTTP_PORT environment variable). The Open Thread Border Router Integration still complains about that url. Is the REST API endpoint same as the Web UI?

I don’t believe I even got the the point to configuring the Matter Server, right?

services:

  homeassistant:
    container_name: homeassistant
    image: "ghcr.io/home-assistant/home-assistant:stable"
    volumes:
      - /home/ubuntu/home-assistant/config:/config
      - /etc/localtime:/etc/localtime:ro
      - /run/dbus:/run/dbus:ro
      - /dev/serial/by-id:/dev/serial/by-id:ro
    restart: unless-stopped
    privileged: true
    network_mode: host

  otbr:
    hostname: otbr
    container_name: otbr
    image: "openthread/otbr:latest"
    privileged: true
    environment:
      - RADIO_URL=spinel+hdlc+uart:///dev/ttyUsbTread?uart-baudrate=460800
      - DEBUG_LEVEL=4
      - HTTP_PORT=8080      
    devices:
      - "/dev/serial/by-id/usb-SMLIGHT_SMLIGHT_SLZB-07Mg24_6add7a39f772ed118f6073773dbf42d5-if00-port0:/dev/ttyUsbTread"
    volumes:
      - ./openthread/data:/var/lib/thread
    network_mode: host

  matter-server:
    container_name: matter-server
    image: ghcr.io/home-assistant-libs/python-matter-server:stable
    restart: unless-stopped
    security_opt:
      - apparmor=unconfined
    volumes:
      - /home/ubuntu/home-assistant/addons/matter-server:/data
      - /run/dbus:/run/dbus:ro
    network_mode: host

The endpoint should be http://localhost:8081

If you have errors in the logs about the radio, try using the /dev/ttyUSBx path and if it works make a udev rule so the stick is always mounted to the same path. I’m not sure why, but many OTBR images make a note of not using /dev/serial/by-id/...

Where does the http://localhost:8081 come from? By default the port is 80, I have modified it with HTTP_PORT=8080 env variable.

The USB isn’t an issue. I can see it able to access the thread usb stick and scan for existing networks.

Here is what is in the docker entrypoint of the OTBR image:

[ -n "$RADIO_URL" ] || RADIO_URL="spinel+hdlc+uart:///dev/ttyUSB0"
[ -n "$TREL_URL" ] || TREL_URL=""
[ -n "$TUN_INTERFACE_NAME" ] || TUN_INTERFACE_NAME="wpan0"
[ -n "$BACKBONE_INTERFACE" ] || BACKBONE_INTERFACE="eth0"
[ -n "$NAT64_PREFIX" ] || NAT64_PREFIX="64:ff9b::/96"
[ -n "$DEBUG_LEVEL" ] || DEBUG_LEVEL="7"
[ -n "$HTTP_PORT" ] || HTTP_PORT=80

echo "RADIO_URL:" $RADIO_URL
echo "TREL_URL:" "$TREL_URL"
echo "TUN_INTERFACE_NAME:" $TUN_INTERFACE_NAME
echo "BACKBONE_INTERFACE: $BACKBONE_INTERFACE"
echo "NAT64_PREFIX:" $NAT64_PREFIX
echo "DEBUG_LEVEL:" $DEBUG_LEVEL

It’s not very well documented, but 8081 is the default OTBR REST API endpoint. The UI is port 80, and the API endpoint is port 8081. That is the URL to use when adding the home assistant OTBR integration.

@peterxian, @D34DC3N73R,
Switching openthread/otbr with ghcr.io/ownbee/hass-otbr-docker did the trick. Ideally I would like to stay within the “official images”, but that would work for now.

Here: Running OTBR in Docker - #3 by D34DC3N73R

So now what should be the next step when Thread and Matter integrations are configured?