Using Docker Compose to Build ZigBee Infrastructure

Miroslav Siagi
The Startup
Published in
5 min readAug 2, 2020
Logos of used services and ZibBee.

For me, the first half of 2020 was a start for ZigBee and home automation basics. After the lockdown, I’ve ended up with many temperature, humidity and motion sensors along with smart plugs, smart LED bulbs and water leak detectors.

Getting started

I strongly recommend to start with reading zigbee2mqtt.io. It contains basic tutorials and documentation. Regarding HW - I’ve started with the most crucial components such as:

After you got the basic HW, you must flash the sniffer. I’ve done that using MS Windows, because I had some troubles with Raspbian (at that time on Raspberry Pi 3B).

MQTT Broker

Before we configure zigbee2mqtt service, we must create MQTT broker. It’s responsible for receiving, filtering and publishing messages to subscribed clients. It serves for receiving information from ZigBee devices and for sending commands to the devices.

I’ve chosen Eclipse Mosquitto which is a lightweight open source MQTT broker. First off, let’s create a folder structure:

.
├── data-mosquitto
│ └── config
│ └── mosquitto.conf
│ └── data
| └── log
| └── mosquitto.log
├── docker-compose.yml
└── restart

For now, let’s use these commands to create the structure:

mkdir data-mosquitto
mkdir data-mosquitto/config
wget "https://raw.githubusercontent.com/eclipse/mosquitto/master/mosquitto.conf" -P ./data-mosquitto/config
touch docker-compose.yml
touch restart

Initially, I’ve left mosquitto.conf file without any change, but after a while I uncommented parameters regarding persistence and logging:

persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log

Due to a problem, I had to create the data and log subfolders manually with a specific user:

mkdir data-mosquitto/data
sudo chmod o+w ./data-mosquitto/data
sudo chown 1883:1883 ./data-mosquitto/data -R
mkdir data-mosquitto/log
sudo touch ./data-mosquitto/log/mosquitto.log
sudo chmod o+w ./data-mosquitto/log
sudo chmod o+w ./data-mosquitto/log/mosquitto.log
sudo chown 1883:1883 ./data-mosquitto/log -R

Docker Compose

While writing this article, I was using Docker of version 19.03.12 and Docker Compose 1.26.0. The main OS was Desktop version of Ubuntu 20.04 LTS.

Let’s have a look at docker-compose.yml file:

version: '3'
services:
mosquitto:
container_name: mosquitto
image: eclipse-mosquitto:latest
restart: always
deploy:
resources:
limits:
memory: 125M
ports:
- "1883:1883"
- "9001:9001"
volumes:
- ./data-mosquitto/config/mosquitto.conf:/mosquitto/config/mosquitto.conf
- ./data-mosquitto/data:/mosquitto/data
- ./data-mosquitto/log:/mosquitto/log

I also created bash script restart with following content:

#!/bin/bashstart=`date +%s`
echo "Running restart..."
docker-compose --compatibility down && docker-compose --compatibility up -dend=`date +%s`
echo -e "\nScript has reached its end after: " $((end-start)) " seconds"

Thanks to the compatibility parameter, we may apply a memory limit defined in docker-compose.yml.

Now, we let’s start our first service via restart script:

./restart

You should see the docker container running.

Zigbee2mqtt

To create zigbee2mqtt service, let’s append the following service into the docker-compose.yml:

zigbee2mqtt:
container_name: zigbee2mqtt
depends_on:
- mosquitto
image: koenkk/zigbee2mqtt
volumes:
- ./data-zigbee2mqtt:/app/data
- /run/udev:/run/udev:ro
devices:
- /dev/ttyACM0:/dev/ttyACM0
deploy:
resources:
limits:
memory: 100M
restart: always
privileged: true
environment:
- TZ=Europe/Prague

Important parameters, which might have different values for you, are:

  • devices (path to the sniffer)
  • environment (time zone parameter)

Let’s create a folder structure for zigbee2mqtt service along with a configuration file:

mkdir data-zigbee2mqtt
touch data-zigbee2mqtt/configuration.yaml

At first, I put the following content into the configuration.yaml file:

homeassistant: true
permit_join: true
mqtt:
base_topic: zigbee2mqtt
server: 'mqtt://{PC_IP_ADDRESS}:1883'
serial:
port: /dev/ttyACM0
advanced:
network_key: GENERATE

Remember, that port value might be different for you. Replace {PC_IP_ADDRESS} for the correct value as well. After this first startup, zigbee2mqtt service replaces GENERATE placeholder for a custom encryption network key. You may read more here.

Zigbee sniffer and Zibee2mqtt and Eclipse Mosquitto communicating
Zigbee sniffer & Zibee2mqtt & Eclipse Mosquitto

From now on, we can pair our first device. There is a list with supported devices which contains a description how to pair a device along with configuration options.

After we pair our first device (docker-compose logs -f command will help you to see what’s going on), we can notice, that configuration.yaml file in data-zigbee2mqtt folder has been enriched by devices element:

homeassistant: true
permit_join: true
mqtt:
base_topic: zigbee2mqtt
server: 'mqtt://{PC_IP_ADDRESS}:1883'
serial:
port: /dev/ttyACM0
devices:
'0x00154c0805b8aa4c':
friendly_name: 0x00154c0805b8aa4c
advanced:
network_key:
- 81
...

Consider replacing value of friendly_name parameter for a friendly name like ColorLight_Alpha.

At this time, the folder structure should look like this:

.
├── data-mosquitto
│ └── config
│ └── mosquitto.conf
│ └── data
│ └── mosquitto.db
| └── log
| └── mosquitto.log
├── data-zigbee2mqtt
│ └── configuration.yaml
├── docker-compose.yml
└── restart

PostgreSQL

We’re almost there! Let’s prepare PostgreSQL database for Home Assistant. By default, Home Assistant uses a simple file-based database. Since I’ve had performance troubles with certain amount of data on Raspberry, I’ve decided to use a proper database and I’ve chosen PostgreSQL. Now, just append postgres service into docker-compose.yml file:

postgres:
container_name: postgres-ha
image: postgres
ports:
- "5432:5432"
volumes:
- ./data-postgres/:/var/lib/postgresql/data/
deploy:
resources:
limits:
memory: 250M

Also, create .env file:

touch .env

After that, replace {POSTGRES_PASSWORD} placeholder for some password:

POSTGRES_USER=postgres-ha
POSTGRES_PASSWORD={POSTGRES_PASSWORD}
POSTGRES_DB=ha

After executing of our restart script, the postgres service will create data-postgres folder with database’s content:

.
├── .env
├── data-mosquitto
│ └── config
│ └── mosquitto.conf
│ └── data
│ └── mosquitto.db
| └── log
| └── mosquitto.log
├── data-postgres
├── data-zigbee2mqtt
│ └── configuration.yaml
├── docker-compose.yml
└── restart

Home Assistant

We are finally at the final step! Our first main client is Home Assistant, where we can manage and control our devices. HA (Home Assistant) detects all devices automatically through its MQTT integration service.

Let’s add service definition into the docker-compose.yml file:

homeassistant:
container_name: home-assistant
depends_on:
- mosquitto
- postgres
image: homeassistant/home-assistant:stable
volumes:
- ./data-ha:/config
environment:
- TZ=Europe/Prague
deploy:
resources:
limits:
memory: 250M
restart: always
ports:
- 8123:8123

Before appending HA service definition into the docker-compose.yml file, change TZ parameter for your time zone. You can read about more information here.

Now, prepare a folder and a configuration file for HA:

mkdir data-ha
touch data-ha/configuration.yaml

The configuration file’s content is:

# Configure a default setup of Home Assistant (frontend, api, etc)
default_config:
# Text to speech
tts:
- platform: google_translate
group: !include groups.yaml
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml
recorder:
purge_keep_days: 30
db_url: "postgresql://postgres-ha:{POSTGRES_PASSWORD}@{PC_IP_ADDRESS}:5432/ha"
mqtt:
discovery: true
broker: {PC_IP_ADDRESS}
birth_message:
topic: 'hass/status'
payload: 'online'
will_message:
topic: 'hass/status'
payload: 'offline'

Don’t forget to replace {POSTGRES_PASSWORD} and {PC_IP_ADDRESS} placeholder for the relevant value.

That’s it!

After the final restart, you should have fully functioning Home Assistant integrated with MQTT broker receiving/sending messages from/to your sensors. Continue with logging in to HA (http://{PC_IP_ADDRESS}:8123), find your device in Configuration -> Devices and do your magic :)

Final words

After some time, I’ve added Zigbee2Mqtt Assistant for a basic device management activities such as:

  • allowing to join new devices to the network
  • upgrading firmware or
  • to see and check entire network like this:
Map of devices

Personally, I don’t use automation scripts in Home Assistant. Instead of those, I’ve started to use Node-RED which is an extremely powerful tool to create all the rules for your own home automation.

--

--

Miroslav Siagi
The Startup

Software engineer, casual swing dancer, audiophile, improvisation student, sci-fi movies/gadget fan, empathic rationalist, science fan, always student