docs: add zigbee2mqtt implementation plan for naruto

This commit is contained in:
Tuan-Dat Tran
2026-06-03 02:57:22 +02:00
parent 29cc38872c
commit 8dcb429573

View File

@@ -0,0 +1,356 @@
# 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**
```yaml
---
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**
```yaml
---
- 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**
```yaml
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**
```bash
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**
```yaml
---
- 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**
```bash
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**
```yaml
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**
```yaml
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**
```bash
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**
```yaml
---
- 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**
```bash
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:
```yaml
---
- name: Setup directories
ansible.builtin.include_tasks: 10_directories.yaml
- name: Setup Zigbee2MQTT
ansible.builtin.include_tasks: 20_zigbee2mqtt.yaml
```
- [ ] **Commit**
```bash
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**
```bash
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**
```bash
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**
```bash
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**
```bash
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.