diff --git a/README.md b/README.md index 9347791..6ca7f24 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ This directory is the source of truth. The live system files live under `/etc`, - Cockpit: `https://192.168.10.251:9090` (or `.27`, or ZeroTier). Log in with a Unix account; enable *Administrative access* for action buttons. - Regulatory domain **CA** (unlocks 5GHz ch149–161 @30dBm, no DFS). - ZeroTier network `d3ecf5726d041b2a`, pushed DNS domain `wrede.pvt` via `192.168.196.115` + `192.168.10.5`. -- Home Assistant (HAOS in libvirt VM `ha_van`) is bridged into `br0` at **`10.42.0.50:8123`** (pinned DHCP lease, name `homeassistant`); legacy URL `http://10.42.0.1:8123` still works via DNAT. +- Home Assistant runs **natively on this host** (Podman Quadlet, host network) at **`http://10.42.0.1:8123`** (name `homeassistant`). The old HAOS VM `ha_van` (`10.42.0.50`) was retired 2026-07-07. --- @@ -139,13 +139,13 @@ This directory is the source of truth. The live system files live under `/etc`, The `hbc` binary itself (venv at `~/venvs/hbd`, symlink `~/bin/hbc`) is installed once via the heartbeat project's own installer — see §4. `deploy.sh` only starts the service once it exists. -### `ha/` — Home Assistant VM +### `ha/` — Home Assistant (native container) | file | → installs to | purpose | |---|---|---| -| `ha_van.xml` | *(reference only — `virsh define ha/ha_van.xml` to restore)* | libvirt domain: HAOS VM, virtio NIC bridged onto `br0` (MAC `52:54:00:ad:0a:01`). Disk image lives outside the repo. | +| `homeassistant.container` | `/etc/containers/systemd/homeassistant.container` | Podman Quadlet: HA Container on the host network (`:8123`), config in `/srv/homeassistant`, host D-Bus mounted for onboard Bluetooth. | -Not touched by `deploy.sh` — the VM's LAN address/name come from `ap/van-ap-dnsmasq.conf` -(`dhcp-host` → `10.42.0.50`, `homeassistant`) and the legacy-URL DNAT from `ap/nftables.conf`. +The `homeassistant` LAN name comes from `ap/van-ap-dnsmasq.conf` (`host-record` → +`10.42.0.1`). The retired HAOS VM's domain XML (`ha_van.xml`) lives in git history. --- @@ -206,20 +206,25 @@ Not touched by `deploy.sh` — the VM's LAN address/name come from `ap/van-ap-dn - The Access Points card shows both bands (5GHz `hostapd`, 2.4GHz `hostapd-2g`) with per-band client lists and **Restart** buttons; the radio list lives in `vanrouter.js` (`const APS`). - **Prefer** sets the manual WAN preference (see *Manual preference* above); **Up/Down** connect/disconnect the NM device. -### Home Assistant VM (`ha_van`) -- HAOS runs as a libvirt KVM VM whose NIC is **bridged into `br0`** — it is a first-class LAN - device, not NAT'd behind libvirt's `virbr0`. VanLink + wired clients reach it directly at - `http://10.42.0.50:8123` (or `http://homeassistant:8123` / `homeassistant.local` via mDNS); - ZT clients route in via the ZT-managed `10.42.0.0/24` route. mDNS/SSDP device discovery works - because the VM shares the clients' L2 segment. -- The old NAT-era URL `http://10.42.0.1:8123` keeps working: nftables DNATs it to `10.42.0.50`, - with a hairpin masquerade for same-subnet clients (see comments in `ap/nftables.conf`). -- History: the VM used to sit on libvirt's `default` NAT net (`192.168.122.50`) with a - `/etc/libvirt/hooks/network` hook inserting FORWARD accepts above libvirt's REJECT. That broke - whenever libvirtd re-inserted its chains on restart (hook doesn't fire then) — bridging removed - the whole failure mode. The hook and the libvirt DHCP reservation are gone. -- Operate: `virsh {start,shutdown,domstate} ha_van`; autostart is per libvirt config. Verify: - `curl -s -o /dev/null -w '%{http_code}' http://10.42.0.50:8123/` → `200`. +### Home Assistant (native Podman container) +- HA Container runs on the host via a **Podman Quadlet** (`ha/homeassistant.container` → + `/etc/containers/systemd/`; systemd generates `homeassistant.service`). **Host network**: + HA binds `:8123` directly, so clients use `http://10.42.0.1:8123` (or + `http://homeassistant:8123`); mDNS/SSDP discovery sees the LAN because there's no bridge + or NAT in the way. Config lives in `/srv/homeassistant`. +- **Bluetooth**: host BlueZ (`bluetooth.service`) serves the Pi's onboard `hci0` to HA over + the mounted `/run/dbus` socket — add the Bluetooth integration in HA and it appears. +- No add-on store (that was HAOS's Supervisor): Mosquitto/Zigbee2MQTT-style add-ons become + their own containers/services if ever needed. +- Operate: `systemctl {status,restart} homeassistant`. Update: bump/pull the image + (`podman pull ghcr.io/home-assistant/home-assistant:stable`) and restart. After editing + the `.container` file: `./deploy.sh` (or install + `systemctl daemon-reload`), then + `systemctl restart homeassistant`. Verify: + `curl -s -o /dev/null -w '%{http_code}' http://10.42.0.1:8123/` → `200`. +- History: HA started as a HAOS VM on wayback (libvirt NAT, then bridged onto `br0` at + `10.42.0.50` with a legacy-URL DNAT), moved to this Pi as an aarch64 VM 2026-07-06, and + went native 2026-07-07 — the fixed 2 GiB VM allocation starved the 4 GB Pi. The domain + XML (`ha/ha_van.xml`) and the DNAT/hairpin nftables rules are in git history. ### Never sleep (lid-closed operation) - wayback lives lid-closed in the van and must stay up. Stock logind `HandleLidSwitch=suspend` would sleep it on lid close (even on AC). The `power/10-vanlink-nolid.conf` drop-in sets all three lid actions to `ignore`; `deploy.sh` also masks every sleep target so idle / GUI / a stray `systemctl suspend` can't suspend it. diff --git a/ap/nftables.conf b/ap/nftables.conf index 4ef329c..91ee99f 100755 --- a/ap/nftables.conf +++ b/ap/nftables.conf @@ -4,20 +4,8 @@ table ip van_router_nat delete table ip van_router_nat table ip van_router_nat { - chain prerouting { - type nat hook prerouting priority dstnat; policy accept; - # Home Assistant VM (ha_van) is bridged onto br0 at 10.42.0.50 — clients reach - # it directly. Keep the legacy http://10.42.0.1:8123 URL working for anything - # that bookmarked it (phones, ZT clients). - ip daddr 10.42.0.1 tcp dport 8123 dnat to 10.42.0.50:8123 - } chain postrouting { type nat hook postrouting priority srcnat; policy accept; ip saddr 10.42.0.0/24 oifname != "br0" masquerade - # Hairpin for the legacy 10.42.0.1:8123 DNAT when the client sits on the same - # subnet as the HA VM: without masquerade the VM would reply directly on br0 - # from 10.42.0.50 and the client (expecting 10.42.0.1) would drop it. ZT-sourced - # traffic doesn't match and doesn't need it — VM replies route back through us. - ip saddr 10.42.0.0/24 ip daddr 10.42.0.50 tcp dport 8123 oifname "br0" masquerade } } diff --git a/ap/van-ap-dnsmasq.conf b/ap/van-ap-dnsmasq.conf index 21dc0ce..abab1b6 100644 --- a/ap/van-ap-dnsmasq.conf +++ b/ap/van-ap-dnsmasq.conf @@ -5,8 +5,8 @@ domain-needed bogus-priv dhcp-authoritative dhcp-range=10.42.0.10,10.42.0.254,255.255.255.0,12h -# Home Assistant VM (libvirt ha_van, bridged onto br0) — pinned address, resolves -# as "homeassistant" via this dnsmasq; HAOS also announces homeassistant.local (mDNS). -dhcp-host=52:54:00:ad:0a:01,10.42.0.50,homeassistant +# Home Assistant runs natively on this host (Podman, host network, :8123) — +# "homeassistant" is just another name for the router address. +host-record=homeassistant,10.42.0.1 dhcp-option=option:router,10.42.0.1 dhcp-option=option:dns-server,10.42.0.1 diff --git a/deploy.sh b/deploy.sh index 6c92080..0455f0a 100755 --- a/deploy.sh +++ b/deploy.sh @@ -54,6 +54,13 @@ if [ ! -f /etc/van-battery/pushover.json ]; then echo " -> seeded /etc/van-battery/pushover.json (EDIT IT: add Pushover token + user)" fi +echo "== home assistant ==" +# Native HA (Podman Quadlet, replaced the ha_van VM). daemon-reload below +# regenerates homeassistant.service; started (not restarted) at the end so a +# deploy never bounces HA — after editing the .container, restart it manually. +install -D -m0644 ha/homeassistant.container /etc/containers/systemd/homeassistant.container +install -d -m0755 /srv/homeassistant + echo "== hardware watchdog ==" install -D -m0644 power/10-vanlink-watchdog.conf /etc/systemd/system.conf.d/10-vanlink-watchdog.conf @@ -68,6 +75,9 @@ systemctl daemon-reexec systemctl mask systemd-networkd-wait-online.service >/dev/null 2>&1 || true systemctl unmask hostapd >/dev/null 2>&1 || true systemctl enable regdomain.service hostapd hostapd-2g van-ap-dnsmasq nftables systemd-networkd van-failover van-thermal van-ap-watchdog van-ap-watchdog-2g >/dev/null 2>&1 || true +# bluetooth: host BlueZ serves the onboard hci0 to the HA container over D-Bus +systemctl enable --now bluetooth >/dev/null 2>&1 || true +systemctl start homeassistant || echo " -> homeassistant failed to start (podman/quadlet — check journalctl -u homeassistant)" systemctl restart van-thermal # Pick up unmanaged-devices changes so NM releases/keeps the right interfaces. nmcli general reload 2>/dev/null || systemctl reload NetworkManager 2>/dev/null || true diff --git a/ha/ha_van.xml b/ha/ha_van.xml deleted file mode 100644 index 7a44414..0000000 --- a/ha/ha_van.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - ha_van - af014c94-de20-4f52-8d6b-438f16cd82e6 - Home Assistant OS (aarch64) - 2097152 - 2097152 - 2 - - hvm - - - - - - - - - - - - destroy - restart - destroy - - /usr/bin/qemu-system-aarch64 - - - - - - - - - - - - - - - - - - - - - /dev/urandom - - - - diff --git a/ha/homeassistant.container b/ha/homeassistant.container new file mode 100644 index 0000000..f04363b --- /dev/null +++ b/ha/homeassistant.container @@ -0,0 +1,34 @@ +# Home Assistant Container as a Podman Quadlet (replaced the ha_van HAOS VM +# 2026-07-07 — the fixed 2 GiB VM allocation starved the 4 GB Pi; VM domain XML +# is in git history under ha/ha_van.xml). Installed by deploy.sh to +# /etc/containers/systemd/; systemd generates homeassistant.service from it. +# +# Host networking: HA binds :8123 on the host directly, so the LAN URL is +# http://10.42.0.1:8123 (name: homeassistant) — no bridge port, no DNAT. +# /run/dbus gives HA the host BlueZ stack = the Pi's onboard Bluetooth (hci0). +[Unit] +Description=Home Assistant (Podman container) +Wants=network-online.target bluetooth.service +After=network-online.target bluetooth.service + +[Container] +Image=ghcr.io/home-assistant/home-assistant:stable +ContainerName=homeassistant +Network=host +Volume=/srv/homeassistant:/config +Volume=/run/dbus:/run/dbus:ro +Environment=TZ=America/Toronto +# Bluetooth needs two escapes: Ubuntu's dbus-daemon mediates D-Bus per +# AppArmor label and the default containers profile can't send to BlueZ +# (AddMatch denied), and habluetooth wants NET_ADMIN+NET_RAW for adapter +# recovery. HA is rootful + host-net anyway. +PodmanArgs=--security-opt apparmor=unconfined +AddCapability=NET_ADMIN NET_RAW + +[Service] +Restart=always +# First start pulls the image (~600 MB) — allow for a slow uplink. +TimeoutStartSec=900 + +[Install] +WantedBy=multi-user.target