Li3, NVMe watchdog, Home Assistant/ESPHome, Frigate and Cockpit are
installed, enabled and restarted only when their ENABLE_* flag is 1.
--diff shows what would change in /etc. A unit that fails to restart is
recorded as a warning instead of aborting the deploy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
nftables.conf hardcoded the R5C network, van-br0-setup.service the R5C
interfaces and several configs wlan0/eth0. All now come from the host
config; the onboard LAN port is skipped when ONBOARD_LAN_IFACE is empty.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
SKIP_COCKPIT only existed in the deleted deploy.conf; under set -u the
cockpit block would abort with an unbound variable.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
deploy.conf is replaced by one versioned file per host plus an example.
deploy.sh validates the config, renders every template with a generic
token loop, refuses to install output with leftover @TOKENs and gains
--check/--templates. LAN_USB_IFACE is renamed LAN_IFACE.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
van-ap-dnsmasq.conf and 21-van-br0.network carry @TOKEN@ placeholders but
were installed unrendered. Armbian's netplan-generated 10-netplan-* file
matched the LAN port before 22-van-lan.network, so it was never bridged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Moves host-specific configuration out of the repo so the Pi 5 and R5C
can share one git checkout, with feature flags and template checks.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The eMMC is now the boot/root device and the SD card is a manual
failsafe. U-Boot has no persistent env and always prefers an SD with a
boot.scr, so the SD's boot script is kept dormant as
boot-failesafe-rename-to:boot.scr.
- Locate the SD by root UUID instead of /dev/mmcblk0p2
- Preserve the SD's dormant boot script; never create a live boot.scr
and refresh the dormant copy from the eMMC
- Copy fstab and armbianEnv.txt with the root UUID rewritten to the SD's
- Add rsync -x; drop the Pi-specific /boot/firmware exclude
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
SD card filesystem corrupted: ext4 journal version incompatible with
standard e2fsck (1.47.2 from Linux host). Filesystem inaccessible.
Root cause: Podman homeassistant container startup during network
instability and system reconfiguration caused unclean shutdown and
filesystem corruption.
Recovery: Flash fresh Armbian 6.18, run deploy.sh with fixes:
- DNS resolution fix added to HOMEASSISTANT.md
- resolv.conf fix should be added to deploy.sh post-processing
- Deploy containers after network is stable
All scripts and configs are ready and committed. Only need fresh SD card.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Root cause: /etc/resolv.conf was a broken symlink to systemd-resolved's
stub resolver, which doesn't exist after disabling the service.
Fixed by creating proper /etc/resolv.conf pointing to dnsmasq on 10.42.1.1
and upstream resolvers (1.1.1.1, 8.8.8.8).
This fix allows apt-get to work again and enables podman installation.
**Result: ✅ podman installed, homeassistant container starting**
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Added HOMEASSISTANT.md with migration steps and configuration details.
Home Assistant config copied to R5C at /srv/homeassistant.
**BLOCKED**: podman installation fails on R5C due to extreme network latency
(1000+ ms Starlink connection) and DNS resolution failures. Package downloads
fail consistently.
Migration can proceed once R5C network is stable enough for apt-get to complete.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Added podman to REQUIRED_PACKAGES for running homeassistant and esphome
containers on R5C.
Created docker/start-containers.sh script to start both containers with
persistent /srv volumes for configuration.
Ready for migration of homeassistant + esphome from 10.42.0 to 10.42.1.
Configs can be copied from old vanrouter at /srv/homeassistant and
/srv/esphome, then containers started on R5C.
Ports:
- Home Assistant: 8123 (accessed via --network=host)
- ESPHome: 6052
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
systemd-resolved listening on port 53 (even localhost-only) blocks
dnsmasq from binding to port 53 globally, causing DHCP to fail on reboot.
Created drop-in systemd-resolved-disable.conf to prevent the service from
listening on DNS. dnsmasq becomes the sole DNS+DHCP server on bridge br0.
Critical fix: without this, clients don't receive DHCP after reboot.
Verified: 5+ active DHCP leases, clients receiving 10.42.1.x addresses.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Moved all packages from OPTIONAL_PACKAGES to REQUIRED_PACKAGES:
- rsync, mosquitto, python3-paho-mqtt
- mobile-broadband-provider-info, zerotier-one
- gpsd, gpsd-clients
Deploy will now fail if any package is missing, ensuring all
van-* services have their dependencies available.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Fixed two issues in nftables.conf:
1. Unsubstituted placeholder @GATEWAY_NETWORK@ - hardcoded to 10.42.1.0/24
2. Missing semicolon on masquerade rule
NAT now working - clients can access internet through R5C gateway.
Masquerade rule: traffic from 10.42.1.0/24 not destined for br0 is NATed
to the outgoing interface (Starlink/Ethernet uplink).
Verified:
- R5C gateway: 10.42.1.1/24
- Dual-band WiFi: 5GHz (ch 149) + 2.4GHz (ch 6)
- DHCP: 3+ active leases
- Internet: R5C can ping 1.1.1.1, clients ready for internet access
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Fixed hostapd-2g.conf to properly specify interface wlx3c3332002066 (D-Link)
for 2.4GHz AP. Both APs now operational:
- 5GHz: wlp1s0 (MT7925E), channel 149, 802.11ac/ax
- 2.4GHz: wlx3c3332002066 (D-Link), channel 6, 802.11n
Both bridged to br0 (10.42.1.0/24), served by dnsmasq DHCP.
D-Link interface usb-modeswitch now working properly (no CDROM mode).
DHCP clients can connect to either band and receive addresses in
the 10.42.1.10-254 range with gateway 10.42.1.1.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Fixed three critical issues preventing DHCP from working:
1. Config file rendering: Placeholders (@GATEWAY_IP@, etc.) weren't being
substituted. Using direct heredoc writes instead of sed for reliability.
2. Service dependency blocking: van-br0-setup was blocked waiting for
hostapd.service to start, preventing bridge members from being added.
Changed to only depend on systemd-networkd so boot completes.
3. Port 53 conflict: systemd-resolved was listening on port 53, blocking
dnsmasq from binding. Disabled DNS in systemd-resolved config.
R5C now operational:
- 5GHz AP on channel 149
- Bridge: 10.42.1.1/24 with enP2p33s0 + wlp1s0
- DHCP: 10.42.1.10-254 (4+ active leases)
- van-ap-dnsmasq service: running and serving DHCP
- hostapd-2g: disabled (interface wlx3c3332002066 not available)
Disabled hostapd-2g.service until D-Link RTL8188GU is fixed (stuck in CDROM mode).
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Updated hostapd.conf to run 5GHz AP on channel 149 with MT7925E (mt76 driver).
Now supports full 802.11ac (VHT) and 802.11ax (WiFi 6) capabilities.
Also fixed deploy.sh rendering issue that left unsubstituted placeholders in
network configs, causing Ethernet flaps. Configs are now properly deployed.
- hw_mode=a (5GHz), channel=149
- VHT and HE (WiFi 6) capabilities enabled
- Proper bridge configuration with gateway IP 10.42.1.1/24
- DHCP range 10.42.1.10-254 verified working
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
RTL8188GU WiFi adapter (0bda:1a2b) ships in CDROM mode on initial connection.
Install usb-modeswitch package and config to automatically switch to network
mode on boot.
Required for 2.4GHz AP to function when using D-Link DWA-171 or similar
RTL8188GU-based adapters.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Move python3-gps, python3-bleak to REQUIRED_PACKAGES (needed for van-li3-battery,
van-gps-owntracks). Add modemmanager to REQUIRED_PACKAGES (provides mmcli for
van-sms-* services).
Deploy will now fail if these packages are missing, preventing silent failures
when GPS or SMS services are needed.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Replaced RTL8822CE with Mediatek MT7925E PCIe WiFi card. The mt76 driver
(MT7925E) properly supports AP bridge mode, unlike rtw88 (RTL8822CE).
R5C is now PRODUCTION-READY:
- 5GHz AP on channel 149 with 802.11ac/ax support
- WiFi clients bridge properly and receive DHCP
- Ethernet failover fully functional
- NAT/routing configured
RTL8822CE limitation: bridging in AP mode not supported by rtw88 driver.
MT7925E solution: mt76 driver stack supports full bridge forwarding.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
dnsmasq was failing to start or staying inactive because it was starting
before the bridge members were added. Updated van-ap-dnsmasq.service to
depend on van-br0-setup.service, ensuring the bridge is ready before
dnsmasq binds to br0.
Fixes: DHCP not provisioning addresses on boot.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
systemd-networkd Bridge= directive wasn't reliably adding interfaces to br0
on Armbian. Created persistent systemd service to ensure enP2p33s0 and wlp1s0
are added to the bridge after boot, maintaining 10.42.1.0/24 network.
This ensures the gateway bridge stays configured even after R5C reboots.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Upgrade R5C from Debian to Armbian 6.18 fixes RTL8822CE 5GHz limitation
- Updated deploy.conf with Armbian's systemd network interface names:
- wlan1/wlan2 → wlp1s0 (single WiFi chip; both APs on same interface)
- eth0/eth1 → enP2p33s0/enP1p17s0 (MAC-derived naming)
- 5GHz AP now working on channel 149 with 802.11ac/ax support
- DHCP functional: devices receiving IPs from 10.42.1.10-254 range
- One test device successfully provisioned with lease at 10.42.1.66
Status: R5C AP operational, ready for production failover role
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
FriendlyElec R5C RTL8822CE WiFi chip does not support 5GHz AP mode.
Both wlan0 and wlan1 locked to 2.4GHz-only (mode 2) by firmware/driver.
Tested:
- Channel 149 (5GHz): fails with 'mode (2) not found'
- Channel 36 (5GHz): fails with 'mode (2) not found'
- Both wlan0 and wlan1: same limitation
- Regulatory domain persistence doesn't change mode
Solution: Configure hostapd for 2.4GHz (hw_mode=g, channel 6)
- VanLink2 now on wlan1, 2.4GHz channel 6
- VanLink2-2G still on wlan2, 2.4GHz channel 6
- Both working stably
R5C not recommended for 5GHz AP use; consider Pi 5 or other board
if 5GHz dual-band AP is required.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Problem: wlan1 regulatory domain not set on boot, defaults to 2.4GHz-only mode,
preventing channel 149 (5GHz) from being available. hostapd fails on startup.
Solution:
1. Modified regdomain.service to:
- Run BEFORE hostapd/hostapd-2g services (Before= directive)
- Run earlier in boot (After= sysinit.target)
- RemainAfterExit=yes to persist setting
2. Added 80-regdomain.rules udev rule:
- Triggers on regulatory device changes
- Executes 'iw reg set CA' automatically
3. Added hostapd drop-in config:
- Explicitly requires regdomain.service
- Adds Wants= to ensure it starts
This ensures the regulatory domain is set BEFORE hostapd attempts to use
channel 149, fixing the boot-time AP failure loop.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Problem: RTL8822CE driver caused kernel panic (watchdog hard LOCKUP) when
hostapd tried to use VHT 802.11ac with 80MHz channel width on channel 149.
Root cause: Driver/firmware instability with VHT capabilities on this chip.
Solution: Disable VHT, use HT40+ (802.11n) only.
- Removes ieee80211ac (VHT), ieee80211ax (802.11ax), and 80MHz channel width
- Keeps HT40+ with Short GI for reasonable performance
- Stable operation on channel 149 (5745 MHz)
This allows the 5GHz AP to start and beacon without lockup on R5C hardware.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Problem: R5C spontaneously rebooted due to hardware watchdog timeout
Cause 1: van-gps-owntracks in infinite restart loop (MQTT connect failed)
Cause 2: mosquitto only listening on localhost, so clients couldn't connect
Solution:
- Add restart limits to van-gps-owntracks and van-li3-battery services
(max 3 restarts per 5 minutes prevents infinite restart loops)
- Configure mosquitto to listen on all interfaces (0.0.0.0:1883)
- Deploy mosquitto listener config via deploy.sh
- Restart mosquitto during deploy to apply config changes
This prevents the system from hitting the 11-second hardware watchdog timeout
when services crash and systemd auto-restarts them indefinitely.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
The system dnsmasq.service from the Debian dnsmasq package conflicts with
van-ap-dnsmasq.service on port 53 (DNS/DHCP). Explicitly disable the system
service during deploy so only van-ap-dnsmasq (bound to br0 for AP DHCP) runs.
Fixes DHCP failures where clients connect to the AP but don't get IP addresses
because port 53 is already in use.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Check for required packages (hostapd, iw, nftables, dnsmasq, systemd-resolved)
at deploy start and fail if any are missing. Also warn about optional packages
used by specific components (rsync, mosquitto, gpsd, python3-paho-mqtt, etc.).
Missing required packages halt deployment with clear error message. Optional
packages generate warnings that are captured in deploy-warnings.json for the
router-status MQTT dashboard.
This prevents silent failures when the R5C or other hardware doesn't have
expected dependencies installed.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Clarify that WIFI_5G_IFACE=wlan1 uses the built-in RTL8822CE chip on
FriendlyElec R5C with rtl88x2ce driver, not the RTL8852BU from the Pi 5.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Add GATEWAY_IP, GATEWAY_PREFIX, GATEWAY_NETWORK, DHCP_RANGE_START, and
DHCP_RANGE_END variables to deploy.conf. Update ap/21-van-br0.network,
ap/van-ap-dnsmasq.conf, and ap/nftables.conf to use @TOKEN@ placeholders
for IP configuration instead of hardcoding 10.42.x addresses.
This allows deploying the same configuration to multiple routers on
different networks (e.g., Pi 5 on 10.42.0.x and R5C on 10.42.1.x) by
simply editing deploy.conf before running ./deploy.sh.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Cockpit's login was unreliable (Firefox rejected sometimes, Android failed
outright with a TLS handshake error) and it was a resource hog on this Pi.
van-router-mqtt publishes the same AP/client/WAN/Starlink/modem status and
accepts the same control actions (AP/modem restart, WAN connect/disconnect,
prefer) via MQTT with HA discovery, using the local Mosquitto broker already
running here. It republishes van-failover/van-thermal's own state.json files
verbatim rather than modifying either daemon (van-thermal is deliberately
stdlib-only; van-failover's control loop wasn't worth an added dependency).
Adds a Lovelace dashboard (ha/dashboards/van-router.yaml) consuming those
entities. Cockpit's plugin is left deployed for now, not yet retired.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S2tpeaiHUriQbbnQ4SCbkf
Commands reverse-engineered from Bluetooth HCI snoop captures (adb bugreport)
of the official Carefree Connects (BT12) Android app, confirmed across three
independent captures including two isolated single-action live tests:
extend/retract (feature 0x05, values 0x01/0x02 -- the device toggles motor
state internally, there's no separate stop byte) and light on/off (feature
0x1a, values 0x19/0x01). Both live under GATT characteristic 02060002 on
service 02060001-50e1-405f-bab0-6bb582b4d96e.
Connects on demand per command (mirrors the app's own connect/act/disconnect
pattern) rather than holding a persistent connection like li3_battery, since
this device doesn't stream telemetry. Cover/light state is assumed/optimistic
-- the notify channel (02060003) isn't decoded yet, so a diagnostic sensor
just surfaces raw undecoded replies to build up data for that follow-on work.
Verified end-to-end against the real device: light on/off and awning
extend both worked through Home Assistant.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The li3 BMS has a persistently marginal BLE link from any single fixed
vantage point on this Pi (onboard adapter, a USB dongle that turned out to
be BR/EDR-only, and the Athom ESPHome BT proxy even after moving it closer).
Move capture into HA proper so bluetooth.async_ble_device_from_address can
pick whichever known source currently has the device, instead of hardcoding
one. Protocol parsing ported verbatim from li3/van-li3-battery.
van-li3-battery is disabled on the Pi; its MQTT discovery entities were
cleared.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Both sync-sd-backup.sh and sync-usb-backup.sh used set -e, so rsync
returning exit 24 (partial transfer due to vanished source files --
expected on a live root, e.g. a container's shm socket disappearing
mid-sync) aborted the script before it printed its own success message,
even though the actual file transfer completed fully. Only exit 24 is
now tolerated; any other rsync failure still aborts as before.
Found running sync-sd-backup.sh today: it transferred all 919k files
correctly but exited non-zero and skipped its completion line.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJfEELeh3ercpRBp8yYrYS
The in-kernel rtw89_8852bu on this Ubuntu kernel base predates mainline's
USB2->3 auto-switch for this chip, permanently capping the AP dongle at
USB2/480M. morrownr/rtw89 (dkms) has that switch. ap/rtw89.conf now
blacklists the in-kernel rtw89 modules and tunes the replacement
(disable_ps_mode + switch_usb_mode); ap/install-rtw89-driver.sh builds
and installs it, pinned to a specific upstream commit, kept separate
from deploy.sh since a dkms rebuild is too slow to run on every deploy.
Verified live: negotiates USB3/5000M on a dedicated USB3 controller
(480M on a USB2-only one, as expected), AP recovered via hostapd's
Restart=always + van-ap-watchdog with no manual intervention. Hit and
documented one real gotcha along the way: in-kernel rtw89_core refused
to unload while its own dependents (rtw89_8852b, rtw89_8852b_common)
were still loaded, which blocked the new module with a duplicate-symbol
error until all in-kernel modules were removed first.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJfEELeh3ercpRBp8yYrYS
Refreshes the USB disk (sda2) as a live boot-fallback clone of the
running root, same self-sync guard/excludes/rsync flags as the SD
script. Fixed copy-paste leftovers from sync-sd-backup.sh (SD_ROOT_PART,
mount point, and log message all still said "SD").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJfEELeh3ercpRBp8yYrYS
Path.resolve() doesn't raise on a nonexistent path — it just returns the
syntactic path unchanged — so when driver_name() ran before the radio's
netdev had enumerated (startup race with USB re-enum), it silently
returned the literal string "driver" instead of None. The loop's retry
guard (`if driver is None: retry`) never fired since "driver" is truthy,
so the bogus value was cached for the service's entire uptime and the
queue-flush wedge regex could never match real driver names — the 3rd
check added 2026-08-19 was silently inert. Fixes 2026-08-23 recurrence
where the 5GHz SSID went invisible and hostapd never got restarted.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJfEELeh3ercpRBp8yYrYS
mmcli's inline "number=...,text=..." properties string breaks on plain
spaces/quotes/colons in the text — found while testing van-sms-watch.
Sidesteps that entirely by passing the message via
--messaging-create-sms-with-text=<tempfile> instead. Tested live
(multi-word text, punctuation, multi-line stdin input all delivered).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>