diff --git a/README.md b/README.md index d279282..15820a5 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ identity/ Face<->MAC<->name correlation logic (Node-RED flow - [x] Project plan drafted - [x] Container host setup script v1 (HA, Mosquitto, Zigbee2MQTT USB, Frigate, Grocy) -- [ ] Add Node-RED + monitoring (Netdata) + dashboard (Homepage) + ntfy + Portainer to compose stack +- [x] Node-RED + monitoring (Netdata) + dashboard (Homepage) + ntfy + Portainer added to compose stack +- [x] Backup (restic) setup — scripted, off by default until a backup target is picked (`ENABLE_BACKUPS`) - [ ] Bermuda / ESPHome BLE proxy configs - [ ] RuView node configs - [ ] Frigate peephole camera config (real RTSP details) @@ -36,7 +37,6 @@ identity/ Face<->MAC<->name correlation logic (Node-RED flow - [ ] LLM host (Ollama) setup script - [ ] CalDAV / Nextcloud calendar integration notes - [ ] Identity correlation flow (Node-RED) -- [ ] Backup (restic) setup ## Quick start @@ -46,6 +46,18 @@ sudo ./setup-container-host.sh ``` Edit the variables at the top of the script first (timezone, Zigbee USB device -path, Mealie/hardware-accel toggles). See `docs/project-plan.md` for the full -phased rollout order — don't skip straight to Phase 4+ automations before the -Phase 2 reactive baseline (presence -> light, no LLM in the loop) is working. +path, Mealie/hardware-accel toggles, and whether to enable Node-RED/Netdata/ +Homepage/ntfy/Portainer and restic backups — all but backups are on by +default). See `docs/project-plan.md` for the full phased rollout order — don't +skip straight to Phase 4+ automations before the Phase 2 reactive baseline +(presence -> light, no LLM in the loop) is working. + +The script brings up everything that runs on this one Debian host: Home +Assistant, Mosquitto, Zigbee2MQTT, Node-RED, Frigate, Grocy, Netdata, a +Homepage dashboard, ntfy, and Portainer, plus an optional scheduled restic +backup timer. What it can't do for you, because they need separate hardware, +credentials, or physical setup: pairing Zigbee sensors, flashing +RuView/ESPHome/Bermuda BLE proxy boards, pointing Frigate at a real camera +RTSP URL, the Grocy kiosk touchscreen, the separate LLM/GPU host, and wiring +up the Nextcloud CalDAV integration — see the Status checklist above and +`docs/project-plan.md` for those. diff --git a/hosts/container-host/scripts/setup-container-host.sh b/hosts/container-host/scripts/setup-container-host.sh index 97e6cd4..9c35fd6 100755 --- a/hosts/container-host/scripts/setup-container-host.sh +++ b/hosts/container-host/scripts/setup-container-host.sh @@ -3,14 +3,23 @@ # Smart Home Container Host Setup # Target: Debian 12 (Bookworm) or Raspberry Pi OS Lite (Bookworm-based) # -# Sets up Docker + a compose stack for: +# Sets up Docker + a compose stack for everything that lives on this single +# host per docs/project-plan.md Phase 1 and Phase 9 (the LLM/GPU host is a +# separate physical machine — see hosts/llm-host): # - Home Assistant (Container install) # - Mosquitto (MQTT broker) -# - Zigbee2MQTT (talks to your network-attached SLZB-06 coordinator) +# - Zigbee2MQTT (talks to your USB-attached CC2652P/Haozee coordinator) +# - Node-RED (automation glue, identity-correlation flow home) # - Frigate (NVR + face recognition, for the peephole cam) # - Grocy (self-hosted inventory / shopping list) +# - Mealie (optional meal planning) +# - Netdata (host + container monitoring) +# - Homepage (single dashboard landing page) +# - ntfy (self-hosted push notifications) +# - Portainer (Docker GUI) +# - restic scheduled backups (optional, off by default) # -# Run as: sudo ./setup-smart-home-host.sh +# Run as: sudo ./setup-container-host.sh # # EDIT THE VARIABLES BELOW BEFORE RUNNING. @@ -24,6 +33,21 @@ TIMEZONE="Europe/Vienna" # Adjust to your timezone ENABLE_MEALIE="false" # Set to "true" to also deploy Mealie ENABLE_INTEL_HWACCEL="false" # Set to "true" if this host has an Intel iGPU for Frigate +# --- Phase 1/9 add-ons — on by default, set to "false" to skip any of them --- +ENABLE_NODERED="true" +ENABLE_NETDATA="true" +ENABLE_HOMEPAGE="true" +ENABLE_NTFY="true" +ENABLE_PORTAINER="true" + +# --- Scheduled backups (restic) — off by default until you pick a target --- +# Set ENABLE_BACKUPS=true and RESTIC_REPOSITORY to a local path (e.g. an +# external/USB drive mount, or a NAS mount), or a remote target restic +# supports (s3:..., sftp:..., b2:..., rest:...). See https://restic.net +ENABLE_BACKUPS="false" +RESTIC_REPOSITORY="/mnt/backup/smart-home-restic" +BACKUP_SCHEDULE="03:30" # systemd OnCalendar time, daily at this local time + # --- Zigbee adapter: USB (CC2652P/CH340C, e.g. Haozee/Sonoff Dongle-P style) --- # Run `ls -l /dev/serial/by-id/` AFTER plugging the adapter in, and paste the # full path it shows here. This is more stable across reboots than /dev/ttyUSB0. @@ -34,7 +58,7 @@ ZIGBEE_USB_DEVICE="/dev/serial/by-id/usb-1a86_USB_Serial-if00-port0" # Sanity checks # --------------------------------------------------------------------------- if [[ $EUID -ne 0 ]]; then - echo "Please run as root (sudo ./setup-smart-home-host.sh)" >&2 + echo "Please run as root (sudo ./setup-container-host.sh)" >&2 exit 1 fi @@ -49,9 +73,18 @@ if [[ ! -e "$ZIGBEE_USB_DEVICE" ]]; then echo " Continuing anyway — the zigbee2mqtt container just won't start correctly until this is fixed." fi +if [[ "$ENABLE_BACKUPS" == "true" && "$RESTIC_REPOSITORY" == "/mnt/backup/smart-home-restic" ]]; then + echo "Warning: ENABLE_BACKUPS=true but RESTIC_REPOSITORY is still the placeholder path." + echo " Edit RESTIC_REPOSITORY at the top of this script to point at real backup storage." +fi + +HOST_IP="$(hostname -I 2>/dev/null | awk '{print $1}')" +HOST_IP="${HOST_IP:-}" + echo "=== Smart Home Container Host Setup ===" echo "Base directory: $BASE_DIR" echo "Timezone: $TIMEZONE" +echo "Host IP (detected): $HOST_IP" echo "Zigbee adapter (USB): ${ZIGBEE_USB_DEVICE}" echo @@ -66,7 +99,12 @@ apt-get install -y \ curl \ gnupg \ lsb-release \ - jq + jq \ + openssl + +if [[ "$ENABLE_BACKUPS" == "true" ]]; then + apt-get install -y restic +fi # --------------------------------------------------------------------------- # 2. Install Docker Engine + Compose plugin (official Docker repo) @@ -103,6 +141,21 @@ mkdir -p "$BASE_DIR"/{homeassistant,mosquitto/config,mosquitto/data,mosquitto/lo if [[ "$ENABLE_MEALIE" == "true" ]]; then mkdir -p "$BASE_DIR"/mealie/data fi +if [[ "$ENABLE_NODERED" == "true" ]]; then + mkdir -p "$BASE_DIR"/nodered/data +fi +if [[ "$ENABLE_NETDATA" == "true" ]]; then + mkdir -p "$BASE_DIR"/netdata/{config,lib,cache} +fi +if [[ "$ENABLE_HOMEPAGE" == "true" ]]; then + mkdir -p "$BASE_DIR"/homepage/config +fi +if [[ "$ENABLE_NTFY" == "true" ]]; then + mkdir -p "$BASE_DIR"/ntfy/{data,config} +fi +if [[ "$ENABLE_PORTAINER" == "true" ]]; then + mkdir -p "$BASE_DIR"/portainer/data +fi # --------------------------------------------------------------------------- # 4. Mosquitto config @@ -120,7 +173,7 @@ log_dest file /mosquitto/log/mosquitto.log EOF # --------------------------------------------------------------------------- -# 5. Zigbee2MQTT config (pre-seeded for the SLZB-06 network coordinator) +# 5. Zigbee2MQTT config (pre-seeded for your USB CC2652P dongle) # --------------------------------------------------------------------------- echo "--- Writing Zigbee2MQTT config ---" cat > "$BASE_DIR/zigbee2mqtt/data/configuration.yaml" < "$BASE_DIR/homepage/config/settings.yaml" <<'EOF' +title: Smart Home +theme: dark +color: slate +headerStyle: clean +EOF + + cat > "$BASE_DIR/homepage/config/widgets.yaml" <<'EOF' +- resources: + cpu: true + memory: true + disk: / +EOF + + cat > "$BASE_DIR/homepage/config/bookmarks.yaml" <<'EOF' +[] +EOF + + cat > "$BASE_DIR/homepage/config/services.yaml" < "$BASE_DIR/docker-compose.yml" < "$RESTIC_PASSWORD_FILE" + chmod 600 "$RESTIC_PASSWORD_FILE" + echo " Generated a new restic repository password at $RESTIC_PASSWORD_FILE." + echo " BACK THIS FILE UP SOMEWHERE ELSE — losing it makes the backup repo unreadable." + fi + + cat > "$BASE_DIR/backup.env" < "$BASE_DIR/backup.sh" <<'BACKUP_EOF' +#!/usr/bin/env bash +# Backs up all stateful smart-home volumes with restic. +# Stops the stack briefly for a consistent snapshot of SQLite-backed configs +# (HA, Zigbee2MQTT, Grocy, Node-RED), then restarts it. +# Frigate's recorded video is excluded — it's large and non-critical to keep; +# face-recognition embeddings live under frigate/config, which IS backed up. +set -euo pipefail +BASE_DIR="/opt/smart-home" +source "$BASE_DIR/backup.env" + +cd "$BASE_DIR" + +if ! restic snapshots --no-lock >/dev/null 2>&1; then + echo "Initializing new restic repository at $RESTIC_REPOSITORY" + restic init +fi + +echo "Stopping stack for a consistent backup..." +docker compose stop + +restic backup "$BASE_DIR" \ + --exclude "$BASE_DIR/frigate/media" \ + --exclude "$BASE_DIR/.restic-password" \ + --exclude "$BASE_DIR/backup.env" + +echo "Restarting stack..." +docker compose start + +restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune +BACKUP_EOF + chmod +x "$BASE_DIR/backup.sh" + + cat > /etc/systemd/system/smart-home-backup.service < /etc/systemd/system/smart-home-backup.timer <:8123" -echo " Zigbee2MQTT UI : http://:8080" -echo " Frigate : http://:5000" -echo " Grocy : http://:9283" +echo " Home Assistant : http://${HOST_IP}:8123" +echo " Zigbee2MQTT UI : http://${HOST_IP}:8080" +echo " Frigate : http://${HOST_IP}:5000" +echo " Grocy : http://${HOST_IP}:9283" if [[ "$ENABLE_MEALIE" == "true" ]]; then - echo " Mealie : http://:9925" + echo " Mealie : http://${HOST_IP}:9925" +fi +if [[ "$ENABLE_NODERED" == "true" ]]; then + echo " Node-RED : http://${HOST_IP}:1880" +fi +if [[ "$ENABLE_NETDATA" == "true" ]]; then + echo " Netdata : http://${HOST_IP}:19999" +fi +if [[ "$ENABLE_HOMEPAGE" == "true" ]]; then + echo " Homepage : http://${HOST_IP}:3000" +fi +if [[ "$ENABLE_NTFY" == "true" ]]; then + echo " ntfy : http://${HOST_IP}:8090" +fi +if [[ "$ENABLE_PORTAINER" == "true" ]]; then + echo " Portainer : http://${HOST_IP}:9000" fi echo echo "Next steps:" @@ -312,8 +614,17 @@ echo " ZIGBEE_USB_DEVICE at the top of this script matches 'ls -l /dev/seria echo " and that the dongle has Z-Stack coordinator firmware (not stock gateway firmware)." echo " 3. Complete Home Assistant's onboarding wizard at :8123." echo " 4. In HA, add the MQTT integration (it should auto-discover Zigbee2MQTT devices)." -echo " 5. Add the CalDAV integration pointing at your Nextcloud instance." -echo " 6. Point HA's Ollama conversation integration at your separate GPU/LLM host." +echo " 5. Portainer: open :9000 within a few minutes of first start to set the admin password" +echo " before anyone else on the LAN can claim that instance." +echo " 6. Node-RED runs in its own bridge network, but Home Assistant uses network_mode: host," +echo " so Node-RED can't reach it by container name — use http://${HOST_IP}:8123 plus a" +echo " long-lived access token (HA profile page) when wiring up the HA nodes." +echo " 7. Add the CalDAV integration pointing at your Nextcloud instance." +echo " 8. Point HA's Ollama conversation integration at your separate GPU/LLM host." +if [[ "$ENABLE_BACKUPS" != "true" ]]; then + echo " 9. Backups are off. Set ENABLE_BACKUPS=true and RESTIC_REPOSITORY at the top of this" + echo " script and re-run once you have backup storage (external drive, NAS, or remote) ready." +fi echo echo "Updating later: cd $BASE_DIR && docker compose pull && docker compose up -d" -echo "Backing up: stop the stack (docker compose down), then tar/copy $BASE_DIR, then start it again." +echo "Backing up manually: sudo $BASE_DIR/backup.sh (requires ENABLE_BACKUPS=true was run once)"