9.6 KiB
Zigbee2MQTT + Mosquitto on naruto — Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Deploy Zigbee2MQTT and Mosquitto as Docker containers on naruto, fully managed by the raspberry_pi Ansible role.
Architecture: The raspberry_pi role gains a defaults file, a handlers file, and two task files (directories + zigbee2mqtt). Three Jinja2 templates cover the compose file, Mosquitto config, and Zigbee2MQTT config. All Zigbee2MQTT tasks are guarded with when: inventory_hostname == 'naruto' since the USB dongle only exists there. Secrets live in a new vars/group_vars/raspberry_pi/secrets.yaml.
Tech Stack: Ansible, Docker Compose, Mosquitto, Zigbee2MQTT, Debian 11 (aarch64)
File Map
| Action | Path | Responsibility |
|---|---|---|
| Modify | roles/raspberry_pi/tasks/main.yaml |
Include 10_ and 20_ task files |
| Create | roles/raspberry_pi/tasks/10_directories.yaml |
Create /opt/docker/ tree on naruto |
| Create | roles/raspberry_pi/tasks/20_zigbee2mqtt.yaml |
Template configs, start compose |
| Create | roles/raspberry_pi/defaults/main.yaml |
Image versions and path vars |
| Create | roles/raspberry_pi/handlers/main.yaml |
Restart zigbee2mqtt handler |
| Create | roles/raspberry_pi/templates/zigbee2mqtt/docker-compose.yml.j2 |
Compose file |
| Create | roles/raspberry_pi/templates/zigbee2mqtt/mosquitto.conf.j2 |
Mosquitto config |
| Create | roles/raspberry_pi/templates/zigbee2mqtt/z2m-configuration.yaml.j2 |
Zigbee2MQTT config |
| Create | vars/group_vars/raspberry_pi/secrets.yaml |
Zigbee network key placeholder |
Task 1: Add defaults, handlers, and secrets placeholder
Files:
-
Create:
roles/raspberry_pi/defaults/main.yaml -
Create:
roles/raspberry_pi/handlers/main.yaml -
Create:
vars/group_vars/raspberry_pi/secrets.yaml -
Create defaults file
---
raspberry_pi_docker_base: /opt/docker
raspberry_pi_mosquitto_config_dir: "{{ raspberry_pi_docker_base }}/config/mosquitto"
raspberry_pi_z2m_config_dir: "{{ raspberry_pi_docker_base }}/config/zigbee2mqtt"
raspberry_pi_compose_dir: "{{ raspberry_pi_docker_base }}/compose"
raspberry_pi_mosquitto_version: "2"
raspberry_pi_z2m_version: "2"
Save to roles/raspberry_pi/defaults/main.yaml.
- Create handlers file
---
- name: Restart zigbee2mqtt
ansible.builtin.command:
cmd: docker compose restart zigbee2mqtt
chdir: "{{ raspberry_pi_compose_dir }}"
listen: restart zigbee2mqtt
when: inventory_hostname == 'naruto'
Save to roles/raspberry_pi/handlers/main.yaml.
- Create secrets placeholder
vault_raspberry_pi:
zigbee2mqtt:
network_key: "GENERATE"
Note: GENERATE tells Zigbee2MQTT to auto-generate a network key on first run and persist it to data. Replace with a fixed 16-integer array (e.g. [1, 3, 5, 7, 9, 11, 13, 15, 0, 2, 4, 6, 8, 10, 12, 13]) if you need a stable key across reinstalls.
Save to vars/group_vars/raspberry_pi/secrets.yaml.
- Commit
git add roles/raspberry_pi/defaults/main.yaml roles/raspberry_pi/handlers/main.yaml vars/group_vars/raspberry_pi/secrets.yaml
git commit -m "feat(raspberry_pi): add defaults, handlers, and secrets placeholder"
Task 2: Create directory task
Files:
-
Create:
roles/raspberry_pi/tasks/10_directories.yaml -
Create directory task file
---
- name: Create docker base directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: "0755"
become: true
loop:
- "{{ raspberry_pi_docker_base }}"
- "{{ raspberry_pi_compose_dir }}"
when: inventory_hostname == 'naruto'
- name: Create Mosquitto directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: "0755"
become: true
loop:
- "{{ raspberry_pi_mosquitto_config_dir }}"
- "{{ raspberry_pi_mosquitto_config_dir }}/data"
- "{{ raspberry_pi_mosquitto_config_dir }}/log"
when: inventory_hostname == 'naruto'
- name: Create Zigbee2MQTT directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: "0755"
become: true
loop:
- "{{ raspberry_pi_z2m_config_dir }}"
- "{{ raspberry_pi_z2m_config_dir }}/data"
when: inventory_hostname == 'naruto'
Save to roles/raspberry_pi/tasks/10_directories.yaml.
- Commit
git add roles/raspberry_pi/tasks/10_directories.yaml
git commit -m "feat(raspberry_pi): add directory setup task"
Task 3: Create templates
Files:
-
Create:
roles/raspberry_pi/templates/zigbee2mqtt/mosquitto.conf.j2 -
Create:
roles/raspberry_pi/templates/zigbee2mqtt/z2m-configuration.yaml.j2 -
Create:
roles/raspberry_pi/templates/zigbee2mqtt/docker-compose.yml.j2 -
Create Mosquitto config template
listener 1883
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log
allow_anonymous true
Save to roles/raspberry_pi/templates/zigbee2mqtt/mosquitto.conf.j2.
- Create Zigbee2MQTT config template
homeassistant:
enabled: true
mqtt:
server: mqtt://mosquitto:1883
serial:
port: /dev/serial/by-id/usb-SONOFF_SONOFF_Dongle_Lite_MG21_0263f93f46a2ef11b078926661ce3355-if00-port0
advanced:
network_key: {{ vault_raspberry_pi.zigbee2mqtt.network_key }}
log_level: info
frontend:
enabled: true
port: 8080
Save to roles/raspberry_pi/templates/zigbee2mqtt/z2m-configuration.yaml.j2.
- Create Docker Compose template
name: zigbee2mqtt
services:
mosquitto:
image: eclipse-mosquitto:{{ raspberry_pi_mosquitto_version }}
container_name: mosquitto
restart: unless-stopped
ports:
- 1883:1883
volumes:
- {{ raspberry_pi_mosquitto_config_dir }}/mosquitto.conf:/mosquitto/config/mosquitto.conf:ro
- {{ raspberry_pi_mosquitto_config_dir }}/data:/mosquitto/data
- {{ raspberry_pi_mosquitto_config_dir }}/log:/mosquitto/log
zigbee2mqtt:
image: koenkk/zigbee2mqtt:{{ raspberry_pi_z2m_version }}
container_name: zigbee2mqtt
restart: unless-stopped
depends_on:
- mosquitto
ports:
- 8080:8080
volumes:
- {{ raspberry_pi_z2m_config_dir }}/data:/app/data
- {{ raspberry_pi_z2m_config_dir }}/configuration.yaml:/app/data/configuration.yaml
- /run/udev:/run/udev:ro
devices:
- /dev/ttyUSB0:/dev/ttyUSB0
environment:
- TZ=Europe/Berlin
group_add:
- dialout
networks:
default:
driver: bridge
name: zigbee2mqtt
Save to roles/raspberry_pi/templates/zigbee2mqtt/docker-compose.yml.j2.
- Commit
git add roles/raspberry_pi/templates/
git commit -m "feat(raspberry_pi): add zigbee2mqtt and mosquitto templates"
Task 4: Create Zigbee2MQTT deploy task
Files:
-
Create:
roles/raspberry_pi/tasks/20_zigbee2mqtt.yaml -
Create deploy task file
---
- name: Deploy Mosquitto config
ansible.builtin.template:
src: zigbee2mqtt/mosquitto.conf.j2
dest: "{{ raspberry_pi_mosquitto_config_dir }}/mosquitto.conf"
mode: "0644"
become: true
when: inventory_hostname == 'naruto'
- name: Deploy Zigbee2MQTT config
ansible.builtin.template:
src: zigbee2mqtt/z2m-configuration.yaml.j2
dest: "{{ raspberry_pi_z2m_config_dir }}/configuration.yaml"
mode: "0644"
become: true
notify: restart zigbee2mqtt
when: inventory_hostname == 'naruto'
- name: Deploy docker-compose
ansible.builtin.template:
src: zigbee2mqtt/docker-compose.yml.j2
dest: "{{ raspberry_pi_compose_dir }}/docker-compose.yml"
mode: "0644"
become: true
when: inventory_hostname == 'naruto'
- name: Start Zigbee2MQTT stack
ansible.builtin.command:
cmd: docker compose up -d
chdir: "{{ raspberry_pi_compose_dir }}"
become: true
changed_when: false
when: inventory_hostname == 'naruto'
Save to roles/raspberry_pi/tasks/20_zigbee2mqtt.yaml.
- Commit
git add roles/raspberry_pi/tasks/20_zigbee2mqtt.yaml
git commit -m "feat(raspberry_pi): add zigbee2mqtt deploy task"
Task 5: Wire up role main.yaml
Files:
-
Modify:
roles/raspberry_pi/tasks/main.yaml -
Update main.yaml to include task files
Replace the current contents (---) with:
---
- name: Setup directories
ansible.builtin.include_tasks: 10_directories.yaml
- name: Setup Zigbee2MQTT
ansible.builtin.include_tasks: 20_zigbee2mqtt.yaml
- Commit
git add roles/raspberry_pi/tasks/main.yaml
git commit -m "feat(raspberry_pi): wire up role tasks"
Task 6: Run and verify
- Run the playbook
ansible-playbook playbooks/raspberry-pi.yaml
Expected: all tasks ok or changed on naruto, no failures. On pi, directory and zigbee2mqtt tasks should be skipped.
- Verify containers are running on naruto
ansible naruto -a "docker ps --format 'table {{.Names}}\t{{.Status}}'" -b
Expected:
NAMES STATUS
zigbee2mqtt Up X seconds
mosquitto Up X seconds
- Check Zigbee2MQTT logs for successful startup
ssh naruto "sudo docker logs zigbee2mqtt 2>&1 | tail -20"
Expected: lines like Zigbee2MQTT started!, no errors about serial port or MQTT connection.
- Verify Mosquitto is reachable from the LAN
ssh naruto "docker exec mosquitto mosquitto_pub -h localhost -t test -m hello && echo 'OK'"
Expected: OK
- Verify pi tasks were skipped
Check playbook output shows skipping: [pi] for all directory and zigbee2mqtt tasks.