Using Docker Compose to Build ZigBee Infrastructure
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:
- CC2531 USB sniffer
- CC debugger
- CC2531 downloader cable
- some supported Zigbee device(s) (I started out with a pair of temperature & humidity sensors)
- PC with good Docker support (I’ve started out with Raspberry Pi 3B, but ended up with Intel NUC10i3FNH)
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 -Rmkdir 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.
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_translategroup: !include groups.yaml
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yamlrecorder:
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:
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.