Note: The instructions in this post have been substantially revised and can now be found here. The new instructions include:

  1. installing mosquitto and Z2M in a separate jail,
  2. a symlink from the /dev/cuaXX to /zigbee, and
  3. allowing mosquitto to receive connections from outside the jail.

In a previous post, we got Home Assistant Core and it’s dependencies installed into a Bastille jail. We were able to start HA were greeted by the initial landing page. Now it’s time to make HA more useful by pulling in Zigbee data.

Why Zigbee? It’s what I have

Now there are two ways to let HA use Zigbee, Zigbee Home Automation and Zigbee2MQTT. I am going to use Zigbee2MQTT because I prefer to keep services separate where possible.

Software Link to heading

  • Home Assistant Core 2023.12 (installed in a previous post)
  • Zigbee2MQTT 1.34.0 (installed from Github)
  • Mosquitto 2.0.15 (installed from FreeBSD ports)

Getting the Zigbee USB dongle ready Link to heading

I know that the Zigbee dongle is recognised by the FreeBSD kernel because I get this output from dmesg when I pull it out and plug it back in:

ugen2.3: <ITead Sonoff Zigbee 3.0 USB Dongle Plus> at usbus2 (disconnected)
uslcom0: at uhub1, port 2, addr 2 (disconnected)
uslcom0: detached
ugen0.4: <ITead Sonoff Zigbee 3.0 USB Dongle Plus> at usbus0
uslcom0 on uhub2
uslcom0: <ITead Sonoff Zigbee 3.0 USB Dongle Plus, class 0/0, rev 2.00/1.00, addr 3> on usbus0

On my system, the dongle is accessible at /dev/cuaU0. So far, I haven’t worked out how to rename this to something unique to this dongle, for example /dev/zigbee. I can use a devd rule to create a symlink from /dev/zibgee to /dev/cuaU0, but this symlink does not show up in the jail. So for now, we will have to persist with /dev/cuaU0

Passing this new device to the jail Link to heading

By default, the /dev/cuaU0 device is not visible within the jail:

root@home-assistant:~ # ls -lah /dev/
dr-xr-xr-x   9 root wheel     512B Dec 24 18:15 .
drwxr-xr-x  14 root wheel      24B Dec 10 16:33 ..
crw-------   1 root wheel     0x27 Dec 24 18:15 bpf
lrwxr-xr-x   1 root wheel       3B Dec 24 18:15 bpf0 -> bpf
crw-rw-rw-   1 root wheel     0x42 Dec 13 11:55 crypto
dr-xr-xr-x   2 root wheel     512B Dec 24 18:15 fd
crw-rw-rw-   1 root wheel     0x14 Dec 24 19:11 null
crw-------   1 root wheel     0xab Dec 13 11:56 pf
dr-xr-xr-x   2 root wheel     512B Dec 24 18:15 pts
crw-r--r--   1 root wheel      0x4 Dec 13 11:56 random
lrwxr-xr-x   1 root wheel       4B Dec 24 18:15 stderr -> fd/2
lrwxr-xr-x   1 root wheel       4B Dec 24 18:15 stdin -> fd/0
lrwxr-xr-x   1 root wheel       4B Dec 24 18:15 stdout -> fd/1
lrwxr-xr-x   1 root wheel       6B Dec 24 18:15 urandom -> random
crw-rw-rw-   1 root wheel     0x15 Dec 13 11:55 zero
crw-rw-rw-   1 root operator  0x3f Dec 13 11:55 zfs

To make it visible, we need to allow the jail running home-assistant to see cuaXX devices.

First, we create a new ruleset within /etc/devfs.rules:

root@freebsd-server:~ # cat /etc/devfs.rules
[bastille_vnet=13]        <---- This is what you will have if you are using VNET jails
#add path 'bpf*' unhide
#add include $devfsrules_jail
add include $devfsrules_hide_all
add include $devfsrules_unhide_basic
add include $devfsrules_unhide_login
add include $devfsrules_jail
add include $devfsrules_jail_vnet
add path 'bpf*' unhide

[bastille_homeassistant=14] <---- This is what you need to create
add include $devfsrules_hide_all
add include $devfsrules_unhide_basic
add include $devfsrules_unhide_login
add include $devfsrules_jail
add include $devfsrules_jail_vnet
add include $bastille_vnet
add path 'cua*' unhide      <---- The important part

bastille_vnet is what you would have created when you were setting up the jails. bastille_homeassistant is what we’ve created specially for home assistant. It includes all of the rules used by the bastille_vnet ruleset, plus another rule that allows cuaXX devices to be visible.

Next, you need to restart the devfs service:

root@freebsd-server:~ # service devfs restart

Then, we change the ruleset used by the home-assistant jail:

root@freebsd-server:~ # cat /usr/local/bastille/jails/home-assistant/jail.conf
home-assistant {
  devfs_ruleset = 14;               <----- Set this to the number of the ruleset you just created
  enforce_statfs = 2;
  exec.clean;
  exec.consolelog = /var/log/bastille/home-assistant_console.log;
  exec.start = '/bin/sh /etc/rc';
  exec.stop = '/bin/sh /etc/rc.shutdown';
  host.hostname = home-assistant;
  mount.devfs;
  mount.fstab = /usr/local/bastille/jails/home-assistant/fstab;
  path = /usr/local/bastille/jails/home-assistant/root;
  securelevel = 2;

  vnet;
  vnet.interface = e0b_bastille4;
  exec.prestart += "jib addm bastille4 igb0";
  exec.prestart += "ifconfig e0a_bastille4 ether 02:77:49:b9:d8:0a";
  exec.prestart += "ifconfig e0b_bastille4 ether 02:77:49:b9:d8:0b";
  exec.prestart += "ifconfig e0a_bastille4 description \"vnet host interface for Bastille jail home-assistant\"";
  exec.poststop += "jib destroy bastille4";
}

Finally, restart the jail.

Now, from inside the jail you can see the newly unhidden devices:

root@home-assistant:~ # ls -lah /dev/
dr-xr-xr-x   9 root wheel     512B Dec 24 18:15 .
drwxr-xr-x  14 root wheel      24B Dec 10 16:33 ..
crw-------   1 root wheel     0x27 Dec 24 18:15 bpf
lrwxr-xr-x   1 root wheel       3B Dec 24 18:15 bpf0 -> bpf
crw-rw-rw-   1 root wheel     0x42 Dec 13 11:55 crypto
crw-rw----   1 uucp dialer    0xe1 Dec 24 13:22 cuaU0       <----- Here,
crw-rw----   1 uucp dialer    0xee Dec 24 13:22 cuaU0.init  <----- here,
crw-rw----   1 uucp dialer    0xf2 Dec 24 13:22 cuaU0.lock  <----- and here!
dr-xr-xr-x   2 root wheel     512B Dec 24 18:15 fd
crw-rw-rw-   1 root wheel     0x14 Dec 24 19:11 null
crw-------   1 root wheel     0xab Dec 13 11:56 pf
dr-xr-xr-x   2 root wheel     512B Dec 24 18:15 pts
crw-r--r--   1 root wheel      0x4 Dec 13 11:56 random
lrwxr-xr-x   1 root wheel       4B Dec 24 18:15 stderr -> fd/2
lrwxr-xr-x   1 root wheel       4B Dec 24 18:15 stdin -> fd/0
lrwxr-xr-x   1 root wheel       4B Dec 24 18:15 stdout -> fd/1
lrwxr-xr-x   1 root wheel       6B Dec 24 18:15 urandom -> random
crw-rw-rw-   1 root wheel     0x15 Dec 13 11:55 zero
crw-rw-rw-   1 root operator  0x3f Dec 13 11:55 zfs

We only need access to the cuaU0 device, the others were automatically created when the dongle is plugged in.

Mosquitto MQTT Link to heading

As I mentioned earlier, I will be using Zigbee2MQTT. This requires a MQTT server. Luckily, Mosquitto is available in the FreeBSD ports and it works out of the box.

root@home-assistant:~ # pkg install mosquitto

root@home-assistant:~ # service mosquitto enable
mosquitto enabled in /etc/rc.conf

root@home-assistant:~ # service mosquitto start
Starting mosquitto.
1703409712: mosquitto version 2.0.15 starting
1703409712: Config loaded from /usr/local/etc/mosquitto/mosquitto.conf.
1703409712: Starting in local only mode. Connections will only be possible from clients running on this machine.
1703409712: Create a configuration file which defines a listener to allow remote access.
1703409712: For more details see https://mosquitto.org/documentation/authentication-methods/
1703409712: Opening ipv4 listen socket on port 1883.
1703409712: Opening ipv6 listen socket on port 1883.
1703409712: mosquitto version 2.0.15 running

root@home-assistant:~ # sockstat -4
USER     COMMAND    PID   FD  PROTO  LOCAL ADDRESS         FOREIGN ADDRESS      
nobody   mosquitto  45787 3   tcp4   *:*                   *:*
nobody   mosquitto  45787 4   tcp4   127.0.0.1:1883        *:*

Zigbee2MQTT Link to heading

Unfortunately, Zigbee2MQTT is not available in the ports, but it is very easy to get up and running.

Requirements Link to heading

  1. You will need a working MQTT broker to get Z2M running.
  2. You will also need to install node npm git gmake gcc if you haven’t already done so.

Installation Link to heading

Clone the git repository, install the node dependencies, update the configuration to point to your zigbee controller, and start.

[homeassistant@home-assistant ~]$ git clone --depth 1 https://github.com/Koenkk/zigbee2mqtt.git

[homeassistant@home-assistant ~]$ cd zigbee2mqtt

[homeassistant@home-assistant ~/zigbee2mqtt]$ npm ci

added 813 packages, and audited 814 packages in 7s

90 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

[homeassistant@home-assistant ~/zigbee2mqtt]$ cat data/configuration.yaml 
homeassistant: true
permit_join: true
mqtt:
  base_topic: zigbee2mqtt
  server: mqtt://127.0.0.1:1883
serial:
  port: /dev/cuaU0
  adapter: zstack
frontend: true
advanced:
  log_output:
    - console
  log_level: info               <----- debug will give a lot of output
  timestamp_format: YYYY-MM-DD HH:mm:ss
  channel: 25

[homeassistant@home-assistant ~/zigbee2mqtt]$ npm start

zigbee2mqtt@1.34.0 start
node index.js

<I've cut down the output here to show you the main things to look for>
Zigbee2MQTT:info  2023-12-24 20:30:26: Starting Zigbee2MQTT version 1.34.0 (commit #56589dc)
Zigbee2MQTT:info  2023-12-24 20:30:26: Starting zigbee-herdsman (0.25.0)
Zigbee2MQTT:info  2023-12-24 20:30:31: Coordinator firmware version: '{"meta":{"maintrel":1,"majorrel":2,"minorrel":7,"product":1,"revision":20210708,"transportrev":2},"type":"zStack3x0"}'
Zigbee2MQTT:warn  2023-12-24 20:30:31: `permit_join` set to  `true` in configuration.yaml.
Zigbee2MQTT:warn  2023-12-24 20:30:31: Allowing new devices to join.
Zigbee2MQTT:warn  2023-12-24 20:30:31: Set `permit_join` to `false` once you joined all devices.
Zigbee2MQTT:info  2023-12-24 20:30:31: Zigbee: allowing new devices to join.
Zigbee2MQTT:info  2023-12-24 20:30:31: Connecting to MQTT server at mqtt://127.0.0.1:1883
Zigbee2MQTT:debug 2023-12-24 20:30:31: Using MQTT anonymous login
Zigbee2MQTT:info  2023-12-24 20:30:31: Connected to MQTT server
Zigbee2MQTT:info  2023-12-24 20:30:31: MQTT publish: topic 'zigbee2mqtt/bridge/state', payload '{"state":"online"}'
Zigbee2MQTT:info  2023-12-24 20:30:31: Started frontend on port 8080
Zigbee2MQTT:info  2023-12-24 20:30:31: Zigbee2MQTT started!

You will see of output here as you add devices to the network, especially if you set the logging to debug.

We can set this up to run as a daemonised service with logging:

root@home-assistant:~ # cat /usr/local/etc/rc.d/zigbee2mqtt 
#!/bin/sh

# PROVIDE: zigbee2mqtt
# REQUIRE: DAEMON NETWORKING
# BEFORE: LOGIN
# KEYWORD: shutdown

. /etc/rc.subr

name="zigbee2mqtt"
rcvar=zigbee2mqtt_enable

: ${zigbee2mqtt_enable:="NO"}

# daemon
pidfile="/var/run/${name}.pid"
node="/usr/local/bin/node"
script_js="/home/homeassistant/zigbee2mqtt/index.js"
command=/usr/sbin/daemon
#procname="daemon"
command_args="-S -l local0 -s info -P ${pidfile} -r ${node} ${script_js}"

load_rc_config $name
run_rc_command "$1"

root@home-assistant:~ # cat /etc/syslog.conf 
#
#       Spaces ARE valid field separators in this file. However,
#       other *nix-like systems still insist on using tabs as field
#       separators. If you are sharing this file between systems, you
#       may want to use only tabs as field separators here.
#       Consult the syslog.conf(5) manpage.
*.err;kern.warning;auth.notice;mail.crit                /dev/console
*.notice;authpriv.none;kern.debug;lpr.info;mail.crit;news.err   /var/log/messages
security.*                                      /var/log/security
auth.info;authpriv.info                         /var/log/auth.log
mail.info                                       /var/log/maillog
cron.*                                          /var/log/cron
!-devd
*.=debug                                        /var/log/debug.log
*.emerg                                         *
daemon.info                                     /var/log/daemon.log
local0.info                                     /var/log/zigbee2mqtt.log        <------ Add this line here
<Later lines removed>

After you restart the syslogd service, enable and then start the zigbee2mqtt service, you can head to jail-ip-address:8080 to see the Zigbee2MQTT dashboard. If this is not working, check the log files for any misconfigurations.

Configuring devices Link to heading

In the Z2M dashboard, you can give custom names to devices as you add them. These names will be passed through to Home Assistant as device names.

Getting Zigbee into Home Assistant Link to heading

All that is needed now is to install and configure the MQTT integration into Home Assistant. Go to Settings -> Devices & services -> + ADD INTEGRATION. Search for and add MQTT. Set the broker to 127.0.0.1 and the port to 1883. Wait a few minutes and your added Zigbee devices should be discovered by Home Assistant.