Running OTBR in Docker

I’m trying to rung OTBR in Docker so it can work with a SLZB-07Mg24 USB stick.

Running the following command to launch OTBR:

$ docker run -it --rm \
    --sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" \
    -p 8080:8080 \
    -p 80:8888 \
    --dns=127.0.0.1 \
    --volume /dev/serial/by-id/usb-SMLIGHT_SMLIGHT_SLZB-07Mg24_6add7a39f772ed118f6073773dbf42d5-if00-port0:/dev/ttyMyStick \
    --privileged \
    openthread/otbr --radio-url spinel+hdlc+uart:///dev/ttyMyStick

Used the command to determine the USB device:

$ ls -la /dev/serial/by-id/usb-*
lrwxrwxrwx 1 root root 13 Jan 24 04:53 /dev/serial/by-id/usb-SMLIGHT_SMLIGHT_SLZB-07Mg24_6add7a39f772ed118f6073773dbf42d5-if00-port0 -> ../../ttyUSB0

The output from OTBR is:

WARNING: Localhost DNS setting (--dns=127.0.0.1) may fail in containers.
RADIO_URL: spinel+hdlc+uart:///dev/ttyMyStick
TREL_URL: 
TUN_INTERFACE_NAME: wpan0
BACKBONE_INTERFACE: eth0
NAT64_PREFIX: 64:ff9b::/96
DEBUG_LEVEL: 7
+++ dirname /app/script/server
++ cd /app/script/..
++ HAVE_SYSTEMCTL=0
++ have systemctl
++ command -v systemctl
++ HAVE_SERVICE=0
++ have service
++ command -v service
++ HAVE_SERVICE=1
++ [[ ! -n x ]]
++ echo 'Current platform is ubuntu'
Current platform is ubuntu
++ with BORDER_ROUTING
++ local value
+++ printenv BORDER_ROUTING
++ value=1
++ [[ -z 1 ]]
++ [[ 1 == 1 ]]
++ with DHCPV6_PD
++ local value
+++ printenv DHCPV6_PD
++ value=
++ [[ -z '' ]]
++ [[ -f examples/platforms/ubuntu/default ]]
++ [[ '' == 1 ]]
++ with BORDER_ROUTING
++ local value
+++ printenv BORDER_ROUTING
++ value=1
++ [[ -z 1 ]]
++ [[ 1 == 1 ]]
++ with NETWORK_MANAGER
++ local value
+++ printenv NETWORK_MANAGER
++ value=
++ [[ -z '' ]]
++ [[ -f examples/platforms/ubuntu/default ]]
++ [[ '' == 1 ]]
++ STAGE_DIR=/app/stage
++ BUILD_DIR=/app/build
++ [[ -d /app/stage ]]
++ mkdir -v -p /app/stage
mkdir: created directory '/app/stage'
++ [[ -d /app/build ]]
++ mkdir -v -p /app/build
mkdir: created directory '/app/build'
++ export PATH=/app/stage/usr/bin:/app/stage/usr/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
++ PATH=/app/stage/usr/bin:/app/stage/usr/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+++ basename /app/script/server
++ TASKNAME=server
++ BEFORE_HOOK=examples/platforms/ubuntu/before_server
++ AFTER_HOOK=examples/platforms/ubuntu/after_server
++ [[ ! -f examples/platforms/ubuntu/before_server ]]
++ BEFORE_HOOK=/dev/null
++ [[ ! -f examples/platforms/ubuntu/after_server ]]
++ AFTER_HOOK=/dev/null
+ . script/_nat64
++ NAT64_SERVICE=openthread
++ TAYGA_DEFAULT=/etc/default/tayga
++ TAYGA_CONF=/etc/tayga.conf
++ TAYGA_IPV4_ADDR=192.168.255.1
++ TAYGA_IPV6_ADDR=fdaa:bb:1::1
++ TAYGA_TUN_V6_ADDR=fdaa:bb:1::2
++ NAT64_PREFIX=64:ff9b::/96
++ DYNAMIC_POOL=192.168.255.0/24
++ NAT44_SERVICE=/etc/init.d/otbr-nat44
++ WLAN_IFNAMES=eth0
++ THREAD_IF=wpan0
+ . script/_dns64
++ BIND_CONF_OPTIONS=/etc/bind/named.conf.options
++ NAT64_PREFIX=64:ff9b::/96
++ DNS64_NAMESERVER_ADDR=127.0.0.1
+++ echo 64:ff9b::/96
+++ tr '"/"' '"/"'
++ DNS64_CONF='dns64 64:ff9b::/96 { clients { thread; }; recursive-only yes; };'
++ without NAT64
++ with NAT64
++ local value
+++ printenv NAT64
++ value=1
++ [[ -z 1 ]]
++ [[ 1 == 1 ]]
++ without DNS64
++ with DNS64
++ local value
+++ printenv DNS64
++ value=0
++ [[ -z 0 ]]
++ [[ 0 == 1 ]]
++ '[' ubuntu = raspbian ']'
++ '[' ubuntu = beagleboneblack ']'
++ '[' ubuntu = ubuntu ']'
++ RESOLV_CONF_HEAD=/etc/resolvconf/resolv.conf.d/head
+ . script/_firewall
++ FIREWALL_SERVICE=/etc/init.d/otbr-firewall
++ sudo modprobe ip6table_filter
sudo: modprobe: command not found
++ true
++ FIREWALL=1
+ OTBR_MDNS=mDNSResponder
+ OT_BACKBONE_CI=0
+ REFERENCE_DEVICE=0
+ main
+ [[ '' == \s\h\u\t\d\o\w\n ]]
+ startup
+ . /dev/null
+ sudo sysctl --system
* Applying /etc/sysctl.d/10-console-messages.conf ...
kernel.printk = 4 4 1 7
* Applying /etc/sysctl.d/10-ipv6-privacy.conf ...
net.ipv6.conf.all.use_tempaddr = 2
net.ipv6.conf.default.use_tempaddr = 2
* Applying /etc/sysctl.d/10-kernel-hardening.conf ...
kernel.kptr_restrict = 1
* Applying /etc/sysctl.d/10-link-restrictions.conf ...
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
* Applying /etc/sysctl.d/10-magic-sysrq.conf ...
kernel.sysrq = 176
* Applying /etc/sysctl.d/10-network-security.conf ...
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.tcp_syncookies = 1
* Applying /etc/sysctl.d/10-ptrace.conf ...
kernel.yama.ptrace_scope = 1
* Applying /etc/sysctl.d/10-zeropage.conf ...
vm.mmap_min_addr = 65536
* Applying /etc/sysctl.d/60-otbr-accept-ra.conf ...
net.ipv6.conf.eth0.accept_ra = 2
net.ipv6.conf.eth0.accept_ra_rt_info_max_plen = 64
* Applying /etc/sysctl.d/60-otbr-ip-forward.conf ...
net.ipv6.conf.all.forwarding = 1
net.ipv4.ip_forward = 1
* Applying /etc/sysctl.conf ...
+ nat64_start
+ with NAT64
+ local value
++ printenv NAT64
+ value=1
+ [[ -z 1 ]]
+ [[ 1 == 1 ]]
+ '[' openthread = tayga ']'
+ nat44_start
+ with DOCKER
+ local value
++ printenv DOCKER
+ value=1
+ [[ -z 1 ]]
+ [[ 1 == 1 ]]
+ service otbr-nat44 start
+ dns64_start
+ with NAT64
+ local value
++ printenv NAT64
+ value=1
+ [[ -z 1 ]]
+ [[ 1 == 1 ]]
+ with DNS64
+ local value
++ printenv DNS64
+ value=0
+ [[ -z 0 ]]
+ [[ 0 == 1 ]]
+ return 0
+ firewall_start
+ with FIREWALL
+ local value
++ printenv FIREWALL
+ value=1
+ [[ -z 1 ]]
+ [[ 1 == 1 ]]
+ with DOCKER
+ local value
++ printenv DOCKER
+ value=1
+ [[ -z 1 ]]
+ [[ 1 == 1 ]]
+ service otbr-firewall start
+ case "$1" in
+ firewall_start
+ firewall_stop
+ ip6tables -C FORWARD -o wpan0 -j OTBR_FORWARD_INGRESS
ip6tables v1.6.1: Couldn't load target `OTBR_FORWARD_INGRESS':No such file or directory

Try `ip6tables -h' or 'ip6tables --help' for more information.
+ ip6tables -L OTBR_FORWARD_INGRESS
ip6tables: No chain/target/match by that name.
+ ipset_destroy_if_exist otbr-ingress-deny-src
+ ipset list otbr-ingress-deny-src
ipset v6.34: Kernel support protocol versions 6-7 while userspace supports protocol versions 6-6
The set with the given name does not exist
+ ipset_destroy_if_exist otbr-ingress-deny-src-swap
+ ipset list otbr-ingress-deny-src-swap
ipset v6.34: Kernel support protocol versions 6-7 while userspace supports protocol versions 6-6
The set with the given name does not exist
+ ipset_destroy_if_exist otbr-ingress-allow-dst
+ ipset list otbr-ingress-allow-dst
ipset v6.34: Kernel support protocol versions 6-7 while userspace supports protocol versions 6-6
The set with the given name does not exist
+ ipset_destroy_if_exist otbr-ingress-allow-dst-swap
+ ipset list otbr-ingress-allow-dst-swap
ipset v6.34: Kernel support protocol versions 6-7 while userspace supports protocol versions 6-6
The set with the given name does not exist
+ ipset create -exist otbr-ingress-deny-src hash:net family inet6
+ ipset create -exist otbr-ingress-deny-src-swap hash:net family inet6
+ ipset create -exist otbr-ingress-allow-dst hash:net family inet6
+ ipset create -exist otbr-ingress-allow-dst-swap hash:net family inet6
+ ip6tables -N OTBR_FORWARD_INGRESS
+ ip6tables -I FORWARD 1 -o wpan0 -j OTBR_FORWARD_INGRESS
+ ip6tables -A OTBR_FORWARD_INGRESS -m pkttype --pkt-type unicast -i wpan0 -j DROP
+ ip6tables -A OTBR_FORWARD_INGRESS -m set --match-set otbr-ingress-deny-src src -j DROP
+ ip6tables -A OTBR_FORWARD_INGRESS -m set --match-set otbr-ingress-allow-dst dst -j ACCEPT
+ ip6tables -A OTBR_FORWARD_INGRESS -m pkttype --pkt-type unicast -j DROP
+ ip6tables -A OTBR_FORWARD_INGRESS -j ACCEPT
+ start_service rsyslog
+ local service_name=rsyslog
+ [[ 0 == 1 ]]
+ [[ 1 == 1 ]]
+ sudo service rsyslog status
 * rsyslogd is not running
+ sudo service rsyslog start
 * Starting enhanced syslogd rsyslogd                                                                                                                      [fail] 
+ start_service dbus
+ local service_name=dbus
+ [[ 0 == 1 ]]
+ [[ 1 == 1 ]]
+ sudo service dbus status
 * dbus is not running
+ sudo service dbus start
 * Starting system message bus dbus                                                                                                                        [ OK ] 
+ [[ mDNSResponder == \a\v\a\h\i ]]
+ [[ 0 == 1 ]]
+ [[ 0 == 1 ]]
+ have service
+ command -v service
+ sudo service mdns status
Usage: /etc/init.d/mDNS {start|stop|reload|restart}
+ sudo service mdns start
Starting Apple Darwin Multicast DNS / DNS Service Discovery daemon: mdnsd.
mDNSResponder: Default: mDNSResponder (Engineering Build) (Jan 22 2025 15:22:37) starting
+ without WEB_GUI
+ with WEB_GUI
+ local value
++ printenv WEB_GUI
+ value=1
+ [[ -z 1 ]]
+ [[ 1 == 1 ]]
+ start_service otbr-web
+ local service_name=otbr-web
+ [[ 0 == 1 ]]
+ [[ 1 == 1 ]]
+ sudo service otbr-web status
 * otbr-web is not running
+ sudo service otbr-web start
 * Starting thread web interface otbr-web                                                                                                                  [ OK ] 
otbr-web[168]: [INFO]-WEB-----: Running 0.3.0-b510577
+ start_service otbr-agent
+ local service_name=otbr-agent
otbr-web[168]: [INFO]-WEB-----: Border router web started on wpan0
+ [[ 0 == 1 ]]
+ [[ 1 == 1 ]]
+ sudo service otbr-agent status
otbr-web[168]: [ERR ]-WEB-----: OpenThread daemon is not running.
 * otbr-agent is not running
+ sudo service otbr-agent start
 * Starting thread border agent otbr-agent                                                                                                                 [ OK ] 
+ . /dev/null
otbr-agent[195]: [NOTE]-AGENT---: Running 0.3.0-b510577
otbr-agent[195]: [NOTE]-AGENT---: Thread version: 1.4.0
otbr-agent[195]: [NOTE]-AGENT---: Thread interface: wpan0
otbr-agent[195]: [NOTE]-AGENT---: Radio URL: spinel+hdlc+uart:///dev/ttyMyStick
otbr-agent[195]: [NOTE]-ILS-----: Infra link selected: eth0
otbr-agent[195]: [INFO]-RCP_HOS-: OpenThread log level changed to 5
otbr-agent[195]: 49d.18:13:26.467 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:0, cmd:RESET
otbr-agent[195]: 49d.18:13:26.467 [D] P-SpinelDrive-: Waiting response: key=0
otbr-agent[195]: 49d.18:13:28.469 [W] P-SpinelDrive-: Wait for response timeout
otbr-agent[195]: 49d.18:13:28.469 [I] P-SpinelDrive-: co-processor self reset successfully
otbr-agent[195]: 49d.18:13:28.469 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:1, cmd:PROP_VALUE_GET, key:PROTOCOL_VERSION
otbr-agent[195]: 49d.18:13:28.469 [D] P-SpinelDrive-: Waiting response: key=1
otbr-agent[195]: 49d.18:13:30.471 [W] P-SpinelDrive-: Wait for response timeout
otbr-agent[195]: 49d.18:13:30.471 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:1, cmd:PROP_VALUE_GET, key:PROTOCOL_VERSION
otbr-agent[195]: 49d.18:13:30.472 [D] P-SpinelDrive-: Waiting response: key=1
otbr-agent[195]: 49d.18:13:32.474 [W] P-SpinelDrive-: Wait for response timeout
otbr-agent[195]: 49d.18:13:32.474 [C] Platform------: Init() at spinel_driver.cpp:83: Failure
otbr-agent[195]: 49d.18:13:32.474 [D] P-SpinelDrive-: Sent spinel frame, flg:0x2, iid:0, tid:1, cmd:PROP_VALUE_GET, key:PROTOCOL_VERSION
otbr-agent[195]: 49d.18:13:32.474 [D] P-SpinelDrive-: Waiting response: key=1
otbr-agent[195]: 49d.18:13:34.476 [W] P-SpinelDrive-: Wait for response timeout

The web server doesn’t seem to be running.
I have flushed the OpenThread (RCP) SL-OPENTHREAD/2.4.4.0_GitHub-7074a43e4 firmware on the SLZB-07mg24.

Would appreciate some input here on how to proceed. Once this works I will be trying to connect it to the Home Assistant.

I already replied to your other thread on this topic, but note that if you run OTBR with “network_mode=host” you don’t need the sysctl and port settings, not to mention avoiding other IPv6 routing/mDNS issues if Matter server is running in a different container on the same host. This specific error appears to be related to accessing the USB stick. Again see my other post for specifying a baud rate as part of the radio-url and see if that helps.

Your port mapping is backward. You are mapping HOST:80 to CONTAINER:8888

You should give this image a try instead. It includes the HA tweaks found in the add on container without requiring HAOS.

There is also a post I made detailing my struggles to run OTBR as a container and getting it to work with HA docker.

1 Like

The problem was with the image. The one I was using was only exposing web service. Doesn’t seem like it has the REST APIs running. Or at least I couldn’t make that happen. The image you provided really did the trick! Thanks a lot!

I could add the Thread and Matter integrations. Here is what my config now looks like.

The question is, now what do I do now? :smiley: Should I connect Matter add=on to Thread?

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: "ghcr.io/ownbee/hass-otbr-docker:latest"
    privileged: true
    network_mode: host
    cap_add:
      - SYS_ADMIN
      - NET_ADMIN    
    environment:
      DEVICE: "/dev/ttyUsbTread"
      BACKBONE_IF: wlp0s20f3 #enp3s0
      FLOW_CONTROL: 1
      FIREWALL: 1
      NAT64: 1
      BAUDRATE: 460800
      OTBR_REST_PORT: 8081
      OTBR_WEB_PORT: 7586
      AUTOFLASH_FIRMWARE: 0      
    devices:
      - /dev/serial/by-id/usb-SMLIGHT_SMLIGHT_SLZB-07Mg24_6add7a39f772ed118f6073773dbf42d5-if00-port0:/dev/ttyUsbTread
      - /dev/net/tun:/dev/net/tun
    volumes:
      - ./openthread/data:/var/lib/thread

  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

This was my abbreviated setup checklist

:white_check_mark: Run and have access to OTBR WEB

:white_check_mark: Correctly recognize RCP dongle

:white_check_mark: Autodiscover Thread after enabling the OTBR integration http://localhost:8081

:white_check_mark: Have HA create a new preferred thread network & enable “used for Android + IOS credentials

:white_check_mark: Run python-matter-server and enable matter integration in HA ws://localhost:5580/ws

:white_check_mark: Sync Thread Credentials on mobile device
Settings > Companion App > Troubleshooting > Sync Thread credentials
Run sync twice, the message should read “Home Assistant and this device use the same network

:white_check_mark: Device Pairing
Add device via HA Companion App Settings > Devices & services > Devices tab > Add Device button > Add Matter device

1 Like

This is awesome! I would have never figured this out…

@D34DC3N73R, the OTBR crashed overnight. Is this something familiar?

13:03:23.187 [C] Platform------: ------------------ BEGINNING OF CRASH -------------
13:03:23.187 [C] Platform------: *** FATAL ERROR: Caught signal: 6 (Aborted)
otbr-agent: /usr/src/ot-br-posix/src/mdns/mdns.cpp:232: void otbr::Mdns::Publisher::OnServiceResolved(std::string, DiscoveredInstanceInfo): Assertion `aInstanceInfo.mNetifIndex > 0' failed.
13:03:23.193 [C] Platform------: # 0: /usr/sbin/otbr-agent(+0x35c3c9) [0x59e00e9f03c9]
13:03:23.193 [C] Platform------: # 1: /usr/sbin/otbr-agent(+0x35c4f4) [0x59e00e9f04f4]
13:03:23.193 [C] Platform------: # 2: /lib/x86_64-linux-gnu/libc.so.6(+0x3c050) [0x783fabcf4050]
13:03:23.193 [C] Platform------: # 3: /lib/x86_64-linux-gnu/libc.so.6(+0x8aebc) [0x783fabd42ebc]
13:03:23.193 [C] Platform------: # 4: /lib/x86_64-linux-gnu/libc.so.6 gsignal+0x12 [0xabcf3fb2]
13:03:23.193 [C] Platform------: # 5: /lib/x86_64-linux-gnu/libc.so.6 abort+0xd3 [0xabcde472]
13:03:23.193 [C] Platform------: # 6: /lib/x86_64-linux-gnu/libc.so.6(+0x26395) [0x783fabcde395]
13:03:23.193 [C] Platform------: # 7: /lib/x86_64-linux-gnu/libc.so.6(+0x34eb2) [0x783fabceceb2]
13:03:23.194 [C] Platform------: # 8: /usr/sbin/otbr-agent otbr::Mdns::Publisher::OnServiceResolved(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, otbr::Mdns::Publisher::DiscoveredInstanceInfo)+0x284 [0xe8c899c]
13:03:23.194 [C] Platform------: # 9: /usr/sbin/otbr-agent otbr::Mdns::Publisher::OnServiceRemoved(unsigned int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)+0x105 [0xe8c8cb9]
13:03:23.194 [C] Platform------: #10: /usr/sbin/otbr-agent otbr::Mdns::PublisherMDnsSd::ServiceSubscription::HandleBrowseResult(_DNSServiceRef_t*, unsigned int, unsigned int, int, char const*, char const*, char const*)+0x243 [0xe8e0e0b]
13:03:23.194 [C] Platform------: #11: /usr/sbin/otbr-agent otbr::Mdns::PublisherMDnsSd::ServiceSubscription::HandleBrowseResult(_DNSServiceRef_t*, unsigned int, unsigned int, int, char const*, char const*, char const*, void*)+0x46 [0xe8e0bc0]
13:03:23.194 [C] Platform------: #12: /lib/libdns_sd.so(+0x5d02) [0x783fac1a6d02]
13:03:23.194 [C] Platform------: #13: /lib/libdns_sd.so DNSServiceProcessResult+0x36a [0xac1a595d]
13:03:23.194 [C] Platform------: #14: /usr/sbin/otbr-agent otbr::Mdns::PublisherMDnsSd::Process(otbr::MainloopContext const&)+0x2ee [0xe8de04a]
13:03:23.194 [C] Platform------: #15: /usr/sbin/otbr-agent otbr::MainloopManager::Process(otbr::MainloopContext const&)+0x6e [0xea22f26]
13:03:23.194 [C] Platform------: #16: /usr/sbin/otbr-agent otbr::Application::Run()+0x1cb [0xe8b2817]
13:03:23.194 [C] Platform------: #17: /usr/sbin/otbr-agent(+0x222ca4) [0x59e00e8b6ca4]
13:03:23.194 [C] Platform------: #18: /usr/sbin/otbr-agent main+0x92 [0xe8b6f62]
13:03:23.194 [C] Platform------: #19: /lib/x86_64-linux-gnu/libc.so.6(+0x2724a) [0x783fabcdf24a]
13:03:23.194 [C] Platform------: #20: /lib/x86_64-linux-gnu/libc.so.6 __libc_start_main+0x85 [0xabcdf305]
13:03:23.194 [C] Platform------: #21: /usr/sbin/otbr-agent _start+0x21 [0xe8b2171]
13:03:23.194 [C] Platform------: ------------------ END OF CRASH ------------------

@D34DC3N73R, one more thing. When setting up Thread network what security considerations should be made? I believe I used all default settings from the OTBR web ui. That probably is not the best choice.

Do I just go there into the web UI to “form” a new network? What else needs to be changed there in HA for the settings to take effect?

I’ve never had the crash you experienced with that image. Might be worth creating a github issue. Or just restart the container and see if you experience it again.

In regards to creating a thread network, I would recommend letting HA take care of that. If the thread integration shows a network from OTBR, choose the “reset network” option and it’ll be configured for you. After the network is loaded (takes a minute or two to go through the reset process), then choose the “Make Preferred Network” and the “Use for Android + IOS Credentials” option. Then you’ll be ready to sync credentials in the companion app.

I’ll submit to the OTBR project.

Resetting through HA worked. Although I do not see any other indication of the completion. What would be the place to confirm the Thread network parameters?

You can click on the (i) icon and see the parameters. You won’t really need any of them though. Just set it as the preferred network in the thread integration and choose “use for android/ios credentials”. That’s pretty much it. Afterwards sync credentials in the companion app and you’ll be ready to pair a device.

Got it. Will do.

With matter is it possible to discover all nearby devices without scanning QR codes? Basically, can I get the guys to install the switches and use them as “dumb switches” before I bring in the HA and have them discovered?

Not that I’m aware of. As far as I know, if you don’t have the QR or 11-digit numerical code, the devices are not possible to pair. If the guys are willing, my advice would be to get a label maker or write the numerical codes on the inside of the faceplate when installing. They should work as dumb switches until paired.

So I should first add the devices before plugging them into the walls? Would you suggest taking the photos and maintaining the QR codes for my records in case I would need to recreate the network?

What type of devices do you have? If they are receptacles/outlets or light switches that require installation, meaning you will not easily be able to see the code printed on the device after install, I would suggest noting where the receptacle or switch is located, and the 11-digit numerical pairing code. You wouldn’t be able to add these devices until they have power. That’s why it makes sense to write the code or affix a label of the code on the inside of the faceplate in these circumstances. It saves you from having to uninstall the switch/receptacle to get the code (should you ever need it again) and then reinstall.

If they are smart plugs you can just pair them as you plug them in or anytime after. I haven’t seen a matter over thread device that doesn’t have the code printed on the device itself, but it is possible such devices exist.

@D34DC3N73R, those would be Inovelli white switches.

I also have Z-Wave Shelly plugs that area also going behind the plate. Is it the same case with Z-Wave, right?

I just bought the connect ZBT-2 dongle from HA.
This Dongle is usable as Zigbee or OTBR.
I do run HA as a Docker under Unraid.
I tried to create a otbr docker.
I don’t get it running
My first problem is that the dongle need to be flashed for OTBR.
I do not get that happening.
The first problem is flashing the connect ZTB-2.
With the image it should be possible to flash if AUTOFLASH_FIRMWARE is set to 1

The log says it is not recognizing the connect ZBT-2 as an USB.
My image:

docker run
  -d
  --name='otbr'
  --net='host'
  --pids-limit 2048
  --privileged=true
  -e TZ="Europe/Berlin"
  -e HOST_OS="Unraid"
  -e HOST_HOSTNAME="Tower2"
  -e HOST_CONTAINERNAME="otbr"
  -e 'DEVICE'='/dev/ttyUsbThread'
  -e 'BACKBONE_IF'='br0'
  -e 'FLOW_CONTROL'='1'
  -e 'FIREWALL'='1'
  -e 'NAT64'='1'
  -e 'BAUDRATE'='460800'
  -e 'OTBR_REST_PORT'='8081'
  -e 'OTBR_WEB_PORT'='7586'
  -e 'AUTOFLASH_FIRMWARE'='1'
  -e 'THREAD_PATH'='/data'
  -l net.unraid.docker.managed=dockerman
  -l net.unraid.docker.icon='https://openthread.io/guides/images/ot-logo-thread.png'
  -v '/mnt/user/appdata/otbr/':'/data':'rw'
  -v '/dev/serial/by-id/usb-Nabu_Casa_ZBT-2_DCB4D90FC138-if00':'/dev/ttyUsbThread':'rw'
  -v '/dev/net/tun':'/dev/net/tun':'rw' 'ghcr.io/ownbee/hass-otbr-docker:latest'
9c89e518740079bd92fabcff17d2244c02bd5968a8ae13653947b94f8ad33e4b

The command finished successfully!

My log:
s

6-rc: info: service mdns: starting
s6-rc: info: service s6rc-oneshot-runner: starting
s6-rc: info: service mdns successfully started
s6-rc: info: service s6rc-oneshot-runner successfully started
s6-rc: info: service fix-attrs: starting
s6-rc: info: service banner: starting
Default: mDNSResponder (Engineering Build) (Jan 14 2025 21:04:03) starting
s6-rc: info: service banner successfully started
s6-rc: info: service universal-silabs-flasher: starting
s6-rc: info: service fix-attrs successfully started
s6-rc: info: service legacy-cont-init: starting
s6-rc: info: service legacy-cont-init successfully started
s6-rc: info: service universal-silabs-flasher successfully started
s6-rc: info: service otbr-agent: starting
s6-svlisten1: fatal: /run/s6-rc/servicedirs/otbr-agent failed permanently or its supervisor died
s6-rc: warning: unable to start service otbr-agent: command exited 1
s6-rc: info: service legacy-cont-init: stopping
s6-rc: info: service universal-silabs-flasher: stopping
s6-rc: info: service mdns: stopping
/run/s6/basedir/scripts/rc.init: warning: s6-rc failed to properly bring all the services up! Check your logs (in /run/uncaught-logs/current if you have in-container logging) for more information.
/run/s6/basedir/scripts/rc.init: fatal: stopping the container.
Default: mDNSResponder (Engineering Build) (Jan 14 2025 21:04:03) stopping
s6-rc: info: service universal-silabs-flasher successfully stopped
s6-rc: info: service banner: stopping
s6-rc: info: service banner successfully stopped
s6-rc: info: service legacy-cont-init successfully stopped
s6-rc: info: service fix-attrs: stopping
s6-rc: info: service mdns successfully stopped
s6-rc: info: service fix-attrs successfully stopped
s6-rc: info: service s6rc-oneshot-runner: stopping
s6-rc: info: service s6rc-oneshot-runner successfully stopped
INFO: Starting mDNS Responder...
INFO: The selected serial port is not a USB device.
WARNING: No firmware found for the selected device, assuming firmware is installed.
INFO: Setup OTBR firewall...
INFO: Starting otbr-agent...
[NOTE]-AGENT---: Running 0.3.0-b067e5ac-dirty
[NOTE]-AGENT---: Thread version: 1.3.0
[NOTE]-AGENT---: Thread interface: wpan0
[NOTE]-AGENT---: Radio URL: spinel+hdlc+uart:///dev/ttyUsbThread?uart-baudrate=460800&uart-flow-control
[NOTE]-AGENT---: Radio URL: trel://br0
[NOTE]-ILS-----: Infra link selected: br0
[INFO]-RCP_HOS-: OpenThread log level changed to 4
32d.08:50:13.243 [W] P-SpinelDrive-: Wait for response timeout
32d.08:50:13.243 [I] P-SpinelDrive-: co-processor self reset successfully
32d.08:50:15.245 [W] P-SpinelDrive-: Wait for response timeout
32d.08:50:17.247 [W] P-SpinelDrive-: Wait for response timeout
32d.08:50:17.247 [C] Platform------: Init() at spinel_driver.cpp:83: Failure
32d.08:50:19.249 [W] P-SpinelDrive-: Wait for response timeout
WARNING: otbr-agent exited with code 1 (by signal 0).
Chain OTBR_FORWARD_INGRESS (0 references)
target     prot opt source               destination         
DROP       all  --  anywhere             anywhere             PKTTYPE = unicast
DROP       all  --  anywhere             anywhere             match-set otbr-ingress-deny-src src
ACCEPT     all  --  anywhere             anywhere             match-set otbr-ingress-allow-dst dst
DROP       all  --  anywhere             anywhere             PKTTYPE = unicast
ACCEPT     all  --  anywhere             anywhere            
otbr-ingress-deny-src
otbr-ingress-deny-src-swap
otbr-ingress-allow-dst
otbr-ingress-allow-dst-swap
Chain OTBR_FORWARD_EGRESS (0 references)
target     prot opt source               destination         
ACCEPT     all  --  anywhere             anywhere            
INFO: OTBR firewall teardown completed.
INFO: mDNS ended with exit code 4 (signal 0)...

** Press ANY KEY to close this window **

A part of the error is:

INFO: The selected serial port is not a USB device.
WARNING: No firmware found for the selected device, assuming firmware is installed.

Any ideas how to go on?
Do I need to flash manualy?
Any help is appreciated

Iam a little further.
I was able to flash the connect ZBT-2 with Thread.
HA is the network seeing:

I’m only not able to get it as preffered network.
My Docker:

docker run
  -d
  --name='otbr'
  --net='host'
  --pids-limit 2048
  --privileged=true
  -e TZ="Europe/Berlin"
  -e HOST_OS="Unraid"
  -e HOST_HOSTNAME="Tower2"
  -e HOST_CONTAINERNAME="otbr"
  -e 'DEVICE'='/dev/serial/by-id/usb-Nabu_Casa_ZBT-2_DCB4D90FC138-if00'
  -e 'BACKBONE_IF'='br0'
  -e 'FLOW_CONTROL'='1'
  -e 'FIREWALL'='1'
  -e 'NAT64'='1'
  -e 'BAUDRATE'='460800'
  -e 'OTBR_REST_PORT'='8082'
  -e 'OTBR_WEB_PORT'='7586'
  -e 'AUTOFLASH_FIRMWARE'='0'
  -e 'THREAD_PATH'='/data'
  -e 'OTBR_RADIO_URL'='uart:///dev/serial/by-id/usb-Nabu_Casa_ZBT-2_DCB4D90FC138-if00?transport=uart'
  -l net.unraid.docker.managed=dockerman
  -l net.unraid.docker.icon='https://openthread.io/guides/images/ot-logo-thread.png'
  -v '/mnt/user/appdata/otbr/':'/data':'rw'
  -v '/dev/serial/by-id/usb-Nabu_Casa_ZBT-2_DCB4D90FC138-if00':'/dev/serial/by-id/usb-Nabu_Casa_ZBT-2_DCB4D90FC138-if00':'rw'
  -v '/dev/net/tun':'/dev/net/tun':'rw' 'ghcr.io/ownbee/hass-otbr-docker:latest'
11d2d990dd8a9dd8dc53e4db55048ff934aa02b33405c37f7c93c6e1ebe8c994

The command finished successfully!

The log says:
:00:36.210 [I] Mle-----------: AttachState Announce → Idle
00:00:36.211 [I] BbrLocal------: Remove BBR Service: seqno (36), delay (5s), timeout (3600s), NotFound
00:00:36.211 [I] Mle-----------: AttachState Idle → Start
00:00:36.211 [N] Mle-----------: Attach attempt 6 unsuccessful, will try again in 8.046 seconds

What to do to get it preffered network?

You can’t set it as the preferred network until HA has learned the credentials. We can tell the credentials aren’t known because there is no circled-i (i) icon beside the network name. With OTBR, there are a few ways to fix this.

The most common is to add the OTBR integration, which usually happens automatically but can also be added manually using the Thread Integration’s “Add an OpenThread Border router” menu (top right) and enter the REST API URL (I believe localhost:8081 for HAOS).

Alternatively you could open the OTBR dashboard (homeassistant.local:8080) to view and extract the dataset TLV and enter it manually in the Thread integration’s “Add dataset from TLV” menu (top right). But this method does not enable the “reset border router” or “change channel” commands like the above method does.

Thanks for the answer, but I do not even get mij docker running anymore.
It is trying to use ttyACM0 (My zigbee dongle) instead of ttyACM3
the image is not using the right varaibles or I’m using the wrong ones:

docker run
  -d
  --name='otbr-docker'
  --net='host'
  --pids-limit 2048
  --privileged=true
  -e TZ="Europe/Berlin"
  -e HOST_OS="Unraid"
  -e HOST_HOSTNAME="Tower2"
  -e HOST_CONTAINERNAME="otbr-docker"
  -e 'BACKBONE_IF'='eth0'
  -e 'FLOW_CONTROL'='1'
  -e 'FIREWALL'='1'
  -e 'NAT64'='1'
  -e 'BAUDRATE'='460800'
  -e 'OTBR_REST_PORT'='8082'
  -e 'OTBR_WEB_PORT'='7586'
  -e 'AUTOFLASH_FIRMWARE'='0'
  -e 'THREAD_PATH'='/data'
  -e 'RADIO_URL'='spinel+hdlc+uart:///dev/ttyACM3?uart-baudrate=460800&uart-flow-control'
  -l net.unraid.docker.managed=dockerman
  -l net.unraid.docker.icon='https://openthread.io/guides/images/ot-logo-thread.png'
  -v '/mnt/user/appdata/otbr':'/data':'rw'
  --device='/dev/ttyACM3'
  --device='/dev/net/tun' 'ghcr.io/ownbee/hass-otbr-docker:latest'
21abf285fce4fb35305a2abed993e2859fd54606a0c3799194e27c409a2250f8

The command finished successfully!

I tried to fix it with ChatGPT but that is not helping at all

I do not know what is wrong with my image?
Th log shows it is trying to use ttyACM0 and baudrate 100000
Instead of ttyACM3 and baudrate 460800

What am I doing wrong??

Important log info:
[NOTE]-AGENT---: Radio URL: spinel+hdlc+uart://?uart-baudrate=460800&uart-flow-control
Showing no /dev/TTYACM3?
and:
33d.09:16:37.383 [C] Platform------: Init() at hdlc_interface.cpp:153: No such file or directory
Saying that line 153 gives an error. ChatGPT says because it is not seeing the UART device and the starting to use ttyACM0 (My zigbee dongle) what causes a crash in my zigbee2mqqt docker

In the image above I do use the general repository, but I also tried the HA one, both not working