Compare commits
19 Commits
8da0ab98f8
...
a905b25190
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a905b25190 | ||
|
|
25cc5ac271 | ||
|
|
2b857903a7 | ||
|
|
eb4e8445fc | ||
|
|
3799dc16d9 | ||
|
|
585c01ca62 | ||
|
|
14b93bf4f5 | ||
|
|
42e790656d | ||
|
|
da92fb0ccc | ||
|
|
d655cc54e2 | ||
|
|
9115d30c59 | ||
|
|
8dcb429573 | ||
|
|
29cc38872c | ||
|
|
f6e2ce8c1a | ||
|
|
956836dc67 | ||
|
|
aa8b591afd | ||
|
|
935389dc6d | ||
|
|
c4327a7596 | ||
|
|
b190022ff0 |
356
docs/superpowers/plans/2026-06-03-naruto-zigbee2mqtt.md
Normal file
356
docs/superpowers/plans/2026-06-03-naruto-zigbee2mqtt.md
Normal 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.
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# Zigbee2MQTT + Mosquitto on naruto — Design Spec
|
||||||
|
|
||||||
|
**Date:** 2026-06-03
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Run Zigbee2MQTT and Mosquitto as Docker containers on naruto, managed by the `raspberry_pi` Ansible role. Home Assistant (running in k3s) connects to Mosquitto over the LAN.
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
- Host: naruto (Pi 4, 192.168.20.13)
|
||||||
|
- Zigbee coordinator: SONOFF Dongle Lite MG21 on `/dev/ttyUSB0`
|
||||||
|
- Stable by-id path: `/dev/serial/by-id/usb-SONOFF_SONOFF_Dongle_Lite_MG21_0263f93f46a2ef11b078926661ce3355-if00-port0`
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Two containers via Docker Compose on naruto. Ansible templates all configs and manages the stack. Home Assistant adds the MQTT integration pointing at `192.168.20.13:1883`.
|
||||||
|
|
||||||
|
```
|
||||||
|
[SONOFF Dongle /dev/ttyUSB0]
|
||||||
|
|
|
||||||
|
[zigbee2mqtt container]
|
||||||
|
| MQTT (internal docker network)
|
||||||
|
[mosquitto container] :1883
|
||||||
|
|
|
||||||
|
[Home Assistant in k3s] — via LAN 192.168.20.13:1883
|
||||||
|
```
|
||||||
|
|
||||||
|
## Directory Layout on naruto
|
||||||
|
|
||||||
|
```
|
||||||
|
/opt/docker/
|
||||||
|
config/
|
||||||
|
mosquitto/
|
||||||
|
mosquitto.conf
|
||||||
|
data/
|
||||||
|
log/
|
||||||
|
zigbee2mqtt/
|
||||||
|
configuration.yaml
|
||||||
|
data/
|
||||||
|
compose/
|
||||||
|
docker-compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mosquitto Config
|
||||||
|
|
||||||
|
- Listens on port 1883
|
||||||
|
- No authentication (internal LAN only)
|
||||||
|
- Persistence enabled, logs to `/opt/docker/config/mosquitto/log/`
|
||||||
|
|
||||||
|
## Zigbee2MQTT Config
|
||||||
|
|
||||||
|
- Serial port: `/dev/serial/by-id/usb-SONOFF_SONOFF_Dongle_Lite_MG21_0263f93f46a2ef11b078926661ce3355-if00-port0`
|
||||||
|
- MQTT broker: `mqtt://mosquitto:1883` (internal docker network)
|
||||||
|
- Network key: stored in `vars/group_vars/raspberry_pi/secrets.yaml` as `vault_raspberry_pi.zigbee2mqtt.network_key`
|
||||||
|
- Frontend enabled on port 8080 for local device management
|
||||||
|
|
||||||
|
## Secrets
|
||||||
|
|
||||||
|
`vars/group_vars/raspberry_pi/secrets.yaml` (vault-encrypted, placeholder for now):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
vault_raspberry_pi:
|
||||||
|
zigbee2mqtt:
|
||||||
|
network_key: "YOUR_ZIGBEE_NETWORK_KEY"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ansible Changes
|
||||||
|
|
||||||
|
| Action | Path | Responsibility |
|
||||||
|
|--------|------|----------------|
|
||||||
|
| Modify | `roles/raspberry_pi/tasks/main.yaml` | Include numbered task files |
|
||||||
|
| Create | `roles/raspberry_pi/tasks/10_directories.yaml` | Create `/opt/docker/` tree |
|
||||||
|
| Create | `roles/raspberry_pi/tasks/20_zigbee2mqtt.yaml` | Template configs, start compose |
|
||||||
|
| 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` | Network key placeholder |
|
||||||
|
|
||||||
|
## Host Constraint
|
||||||
|
|
||||||
|
The `raspberry_pi` role applies to both naruto and pi. The Zigbee2MQTT tasks must be guarded with `when: inventory_hostname == 'naruto'` since the USB dongle is only on naruto.
|
||||||
11
playbooks/raspberry-pi.yaml
Normal file
11
playbooks/raspberry-pi.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
- name: Set up Raspberry Pis
|
||||||
|
hosts: raspberry_pi
|
||||||
|
gather_facts: true
|
||||||
|
roles:
|
||||||
|
- role: common
|
||||||
|
tags:
|
||||||
|
- common
|
||||||
|
- role: raspberry_pi
|
||||||
|
tags:
|
||||||
|
- raspberry_pi
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
- name: Update apt cache
|
||||||
|
ansible.builtin.apt:
|
||||||
|
update_cache: true
|
||||||
|
become: true
|
||||||
|
|
||||||
- name: Restart sshd
|
- name: Restart sshd
|
||||||
service:
|
service:
|
||||||
name: sshd
|
name: sshd
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
- name: Copy bash-configs
|
- name: Copy bash-configs
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: "files/bash/{{ item }}"
|
src: "files/bash/{{ item }}"
|
||||||
dest: "{{ ansible_env.HOME }}/.{{ item }}"
|
dest: "{{ ansible_facts['env']['HOME'] }}/.{{ item }}"
|
||||||
owner: "{{ ansible_user_id }}"
|
owner: "{{ ansible_facts['user_id'] }}"
|
||||||
group: "{{ ansible_user_id }}"
|
group: "{{ ansible_facts['user_id'] }}"
|
||||||
mode: "644"
|
mode: "644"
|
||||||
loop:
|
loop:
|
||||||
- bashrc
|
- bashrc
|
||||||
@@ -13,25 +13,25 @@
|
|||||||
- name: Copy ghostty infocmp
|
- name: Copy ghostty infocmp
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
src: files/ghostty/infocmp
|
src: files/ghostty/infocmp
|
||||||
dest: "{{ ansible_env.HOME }}/ghostty"
|
dest: "{{ ansible_facts['env']['HOME'] }}/ghostty"
|
||||||
owner: "{{ ansible_user_id }}"
|
owner: "{{ ansible_facts['user_id'] }}"
|
||||||
group: "{{ ansible_user_id }}"
|
group: "{{ ansible_facts['user_id'] }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
register: ghostty_terminfo
|
register: ghostty_terminfo
|
||||||
|
|
||||||
- name: Compile ghostty terminalinfo
|
- name: Compile ghostty terminalinfo
|
||||||
ansible.builtin.command: "tic -x {{ ansible_env.HOME }}/ghostty"
|
ansible.builtin.command: "tic -x {{ ansible_facts['env']['HOME'] }}/ghostty"
|
||||||
when: ghostty_terminfo.changed
|
when: ghostty_terminfo.changed
|
||||||
|
|
||||||
- name: Copy kitty infocmp
|
- name: Copy kitty infocmp
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
src: files/kitty/infocmp
|
src: files/kitty/infocmp
|
||||||
dest: "{{ ansible_env.HOME }}/kitty"
|
dest: "{{ ansible_facts['env']['HOME'] }}/kitty"
|
||||||
owner: "{{ ansible_user_id }}"
|
owner: "{{ ansible_facts['user_id'] }}"
|
||||||
group: "{{ ansible_user_id }}"
|
group: "{{ ansible_facts['user_id'] }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
register: kitty_terminfo
|
register: kitty_terminfo
|
||||||
|
|
||||||
- name: Compile kitty terminalinfo
|
- name: Compile kitty terminalinfo
|
||||||
ansible.builtin.command: "tic -x {{ ansible_env.HOME }}/kitty"
|
ansible.builtin.command: "tic -x {{ ansible_facts['env']['HOME'] }}/kitty"
|
||||||
when: kitty_terminfo.changed
|
when: kitty_terminfo.changed
|
||||||
|
|||||||
@@ -14,11 +14,17 @@
|
|||||||
become: true
|
become: true
|
||||||
|
|
||||||
- name: Add Gierens repository to apt sources
|
- name: Add Gierens repository to apt sources
|
||||||
ansible.builtin.apt_repository:
|
ansible.builtin.deb822_repository:
|
||||||
repo: "deb [signed-by=/etc/apt/keyrings/gierens.asc] http://deb.gierens.de stable main"
|
name: gierens
|
||||||
|
types: deb
|
||||||
|
uris: http://deb.gierens.de
|
||||||
|
suites: stable
|
||||||
|
components: main
|
||||||
|
signed_by: /etc/apt/keyrings/gierens.asc
|
||||||
state: present
|
state: present
|
||||||
update_cache: true
|
install_python_debian: true
|
||||||
become: true
|
become: true
|
||||||
|
notify: Update apt cache
|
||||||
|
|
||||||
- name: Install eza package
|
- name: Install eza package
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
@@ -28,7 +34,7 @@
|
|||||||
|
|
||||||
- name: Install bottom package
|
- name: Install bottom package
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
deb: https://github.com/ClementTsang/bottom/releases/download/0.9.6/bottom_0.9.6_amd64.deb
|
deb: https://github.com/ClementTsang/bottom/releases/download/0.9.6/bottom_0.9.6_{{ arch }}.deb
|
||||||
state: present
|
state: present
|
||||||
become: true
|
become: true
|
||||||
|
|
||||||
@@ -37,20 +43,21 @@
|
|||||||
register: neovim_installed
|
register: neovim_installed
|
||||||
changed_when: false
|
changed_when: false
|
||||||
ignore_errors: true
|
ignore_errors: true
|
||||||
|
when: ansible_facts['architecture'] != 'aarch64'
|
||||||
|
|
||||||
- name: Download Neovim AppImage
|
- name: Download Neovim AppImage
|
||||||
ansible.builtin.get_url:
|
ansible.builtin.get_url:
|
||||||
url: https://github.com/neovim/neovim/releases/download/v0.10.0/nvim.appimage
|
url: https://github.com/neovim/neovim/releases/download/v0.10.0/nvim.appimage
|
||||||
dest: /tmp/nvim.appimage
|
dest: /tmp/nvim.appimage
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
when: neovim_installed.rc != 0
|
when: ansible_facts['architecture'] != 'aarch64' and neovim_installed.rc != 0
|
||||||
register: download_result
|
register: download_result
|
||||||
|
|
||||||
- name: Extract Neovim AppImage
|
- name: Extract Neovim AppImage
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
cmd: "./nvim.appimage --appimage-extract"
|
cmd: "./nvim.appimage --appimage-extract"
|
||||||
chdir: /tmp
|
chdir: /tmp
|
||||||
when: download_result.changed
|
when: ansible_facts['architecture'] != 'aarch64' and download_result.changed
|
||||||
register: extract_result
|
register: extract_result
|
||||||
|
|
||||||
- name: Copy extracted Neovim files to /usr
|
- name: Copy extracted Neovim files to /usr
|
||||||
@@ -60,19 +67,19 @@
|
|||||||
remote_src: true
|
remote_src: true
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
become: true
|
become: true
|
||||||
when: extract_result.changed
|
when: ansible_facts['architecture'] != 'aarch64' and extract_result.changed
|
||||||
|
|
||||||
- name: Clean up extracted Neovim files
|
- name: Clean up extracted Neovim files
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: /tmp/squashfs-root
|
path: /tmp/squashfs-root
|
||||||
state: absent
|
state: absent
|
||||||
when: extract_result.changed
|
when: ansible_facts['architecture'] != 'aarch64' and extract_result.changed
|
||||||
|
|
||||||
- name: Remove Neovim AppImage
|
- name: Remove Neovim AppImage
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: /tmp/nvim.appimage
|
path: /tmp/nvim.appimage
|
||||||
state: absent
|
state: absent
|
||||||
when: download_result.changed
|
when: ansible_facts['architecture'] != 'aarch64' and download_result.changed
|
||||||
|
|
||||||
- name: Check if Neovim config directory already exists
|
- name: Check if Neovim config directory already exists
|
||||||
ansible.builtin.stat:
|
ansible.builtin.stat:
|
||||||
|
|||||||
@@ -5,24 +5,24 @@
|
|||||||
upgrade: true
|
upgrade: true
|
||||||
autoremove: true
|
autoremove: true
|
||||||
become: true
|
become: true
|
||||||
when: ansible_user_id != "root"
|
when: ansible_facts['user_id'] != "root"
|
||||||
|
|
||||||
- name: Install base packages
|
- name: Install base packages
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
name: "{{ common_packages }}"
|
name: "{{ common_packages }}"
|
||||||
state: present
|
state: present
|
||||||
become: true
|
become: true
|
||||||
when: ansible_user_id != "root"
|
when: ansible_facts['user_id'] != "root"
|
||||||
|
|
||||||
- name: Update and upgrade packages
|
- name: Update and upgrade packages
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
update_cache: true
|
update_cache: true
|
||||||
upgrade: true
|
upgrade: true
|
||||||
autoremove: true
|
autoremove: true
|
||||||
when: ansible_user_id == "root"
|
when: ansible_facts['user_id'] == "root"
|
||||||
|
|
||||||
- name: Install base packages
|
- name: Install base packages
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
name: "{{ common_packages }}"
|
name: "{{ common_packages }}"
|
||||||
state: present
|
state: present
|
||||||
when: ansible_user_id == "root"
|
when: ansible_facts['user_id'] == "root"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
notify:
|
notify:
|
||||||
- Restart sshd
|
- Restart sshd
|
||||||
become: true
|
become: true
|
||||||
when: ansible_user_id != "root"
|
when: ansible_facts['user_id'] != "root"
|
||||||
|
|
||||||
- name: Copy root sshd_config
|
- name: Copy root sshd_config
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
backup: true
|
backup: true
|
||||||
notify:
|
notify:
|
||||||
- Restart sshd
|
- Restart sshd
|
||||||
when: ansible_user_id == "root"
|
when: ansible_facts['user_id'] == "root"
|
||||||
|
|
||||||
- name: Copy pubkey
|
- name: Copy pubkey
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
community.general.timezone:
|
community.general.timezone:
|
||||||
name: "{{ timezone }}"
|
name: "{{ timezone }}"
|
||||||
become: true
|
become: true
|
||||||
when: ansible_user_id != "root"
|
when: ansible_facts['user_id'] != "root"
|
||||||
|
|
||||||
- name: Set timezone
|
- name: Set timezone
|
||||||
community.general.timezone:
|
community.general.timezone:
|
||||||
name: "{{ timezone }}"
|
name: "{{ timezone }}"
|
||||||
when: ansible_user_id == "root"
|
when: ansible_facts['user_id'] == "root"
|
||||||
|
|
||||||
- name: Configure NTP servers for systemd-timesyncd
|
- name: Configure NTP servers for systemd-timesyncd
|
||||||
ansible.builtin.lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
@@ -24,11 +24,11 @@
|
|||||||
enabled: true
|
enabled: true
|
||||||
state: started
|
state: started
|
||||||
become: true
|
become: true
|
||||||
when: ansible_user_id != "root"
|
when: ansible_facts['user_id'] != "root"
|
||||||
|
|
||||||
- name: Enable and start systemd-timesyncd
|
- name: Enable and start systemd-timesyncd
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
name: systemd-timesyncd
|
name: systemd-timesyncd
|
||||||
enabled: true
|
enabled: true
|
||||||
state: started
|
state: started
|
||||||
when: ansible_user_id == "root"
|
when: ansible_facts['user_id'] == "root"
|
||||||
|
|||||||
7
roles/raspberry_pi/defaults/main.yaml
Normal file
7
roles/raspberry_pi/defaults/main.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
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/zigbee2mqtt"
|
||||||
|
raspberry_pi_mosquitto_version: "2"
|
||||||
|
raspberry_pi_z2m_version: "2"
|
||||||
6
roles/raspberry_pi/handlers/main.yaml
Normal file
6
roles/raspberry_pi/handlers/main.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
- name: Restart zigbee2mqtt
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: docker compose restart zigbee2mqtt
|
||||||
|
chdir: "{{ raspberry_pi_compose_dir }}"
|
||||||
|
listen: restart zigbee2mqtt
|
||||||
34
roles/raspberry_pi/tasks/10_directories.yaml
Normal file
34
roles/raspberry_pi/tasks/10_directories.yaml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
- 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'
|
||||||
40
roles/raspberry_pi/tasks/20_zigbee2mqtt.yaml
Normal file
40
roles/raspberry_pi/tasks/20_zigbee2mqtt.yaml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
- name: Install docker-compose-plugin
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: docker-compose-plugin
|
||||||
|
state: present
|
||||||
|
become: true
|
||||||
|
when: inventory_hostname == 'naruto'
|
||||||
|
|
||||||
|
- 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'
|
||||||
6
roles/raspberry_pi/tasks/main.yaml
Normal file
6
roles/raspberry_pi/tasks/main.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
- name: Setup directories
|
||||||
|
ansible.builtin.include_tasks: 10_directories.yaml
|
||||||
|
|
||||||
|
- name: Setup Zigbee2MQTT
|
||||||
|
ansible.builtin.include_tasks: 20_zigbee2mqtt.yaml
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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:
|
||||||
|
- 8081: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
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
listener 1883
|
||||||
|
persistence true
|
||||||
|
persistence_location /mosquitto/data/
|
||||||
|
log_dest file /mosquitto/log/mosquitto.log
|
||||||
|
allow_anonymous true
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
homeassistant:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
mqtt:
|
||||||
|
server: mqtt://mosquitto:1883
|
||||||
|
|
||||||
|
serial:
|
||||||
|
port: /dev/ttyUSB0
|
||||||
|
adapter: ember
|
||||||
|
|
||||||
|
advanced:
|
||||||
|
network_key: {{ vault_raspberry_pi.zigbee2mqtt.network_key }}
|
||||||
|
log_level: info
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
enabled: true
|
||||||
|
port: 8080
|
||||||
@@ -19,7 +19,7 @@ nfs_server: 192.168.20.12
|
|||||||
# Packages
|
# Packages
|
||||||
#
|
#
|
||||||
|
|
||||||
arch: "{{ 'arm64' if ansible_architecture == 'aarch64' else 'amd64' }}"
|
arch: "{{ 'arm64' if ansible_facts['architecture'] == 'aarch64' else 'amd64' }}"
|
||||||
|
|
||||||
netcup_api_key: "{{ vault_netcup.api_key }}"
|
netcup_api_key: "{{ vault_netcup.api_key }}"
|
||||||
netcup_api_password: "{{ vault_netcup.api_password }}"
|
netcup_api_password: "{{ vault_netcup.api_password }}"
|
||||||
|
|||||||
3
vars/group_vars/raspberry_pi/secrets.yaml
Normal file
3
vars/group_vars/raspberry_pi/secrets.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
vault_raspberry_pi:
|
||||||
|
zigbee2mqtt:
|
||||||
|
network_key: "GENERATE"
|
||||||
1
vars/group_vars/raspberry_pi/vars.yaml
Normal file
1
vars/group_vars/raspberry_pi/vars.yaml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
---
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
[k3s:children]
|
[k3s:children]
|
||||||
k3s_server
|
k3s_server
|
||||||
k3s_agent
|
k3s_agent
|
||||||
k3s_storage
|
|
||||||
k3s_loadbalancer
|
k3s_loadbalancer
|
||||||
|
|
||||||
[k3s_server]
|
[k3s_server]
|
||||||
|
|||||||
3
vars/raspberry_pi.ini
Normal file
3
vars/raspberry_pi.ini
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[raspberry_pi]
|
||||||
|
naruto
|
||||||
|
pi
|
||||||
Reference in New Issue
Block a user