Files
vps/hosts/hass.windy.lan.md
T

19 KiB
Raw Blame History

[hosts/hass.windy.lan.md#8DF6]

hass.windy.lan — Home Assistant (HAOS)

Role and access

Item Value
Role Home Assistant automation hub
IPv4 192.168.55.11 (LAN55)
DNS hass.windy.lan (AdGuard rewrite on dns.windy.lan; legacy hass.local alias)
SSH ssh hassio@hass.windy.lan
Host x88 Pro physical box (HAOS bare-metal, machine: green; verified 2026-08-18)
Platform Home Assistant OS; kernel 6.1.115-haos (aarch64)
Web UI http://hass.windy.lan:8123 (LAN); WAN port-forward hass on gw → :8123

Use hassio for routine SSH inspection. Key-only login was verified on 2026-08-13 from the WSL client (BatchMode=yes).

The ha supervisor CLI (/usr/bin/ha) authenticates with SUPERVISOR_TOKEN. Interactive login works because ~hassio/.zprofile runs exec sudo -i, which loads a root environment carrying the supervisor API token. Non-interactive ssh hassio 'command' does not source .zprofile and fails with unauthorized: missing or invalid API token. Run ha non-interactively via:

ssh -o BatchMode=yes hassio@hass.windy.lan 'sudo -n -i ha core info'

Verified 2026-08-13 that sudo -n -i ha core info works from the WSL client. Never copy the supervisor token into this repository.

The current SSH ED25519 host-key fingerprint is SHA256:DMcMOgDzFsFTon1fndXowEP7jlyOK3/AX3PVK8BATvk (verified 2026-08-13). Verify a changed key out of band before accepting it.

Do not store Home Assistant long-lived tokens, integration credentials, or recovery codes in this repository.

Network

Interface Address / role
end0 IPv4 static 192.168.55.11/24 (gw .254, DNS 192.168.66.36); IPv6 SLAAC auto with GUA on the current PD-derived /64 (240e:3bd:235:1fb2:* at 2026-08-22; rotates on PPPoE redial); primary LAN55 NIC (interface name verified live 2026-08-22 — end1 does not exist)
wlan0 Supervisor disabled (verified 2026-08-14, W1N-104); IPv6 remains off on this RTL8821CS radio
wg0 10.13.13.2/32; WireGuard (add-on / integration tunnel)
hassio / docker0 internal HAOS Docker bridges (172.30.32.0/23, 172.30.232.0/23)

LAN55 clients reach the HTTP API on dns.windy.lan:80 for the AdGuard Home integration; see hosts/dns.windy.lan.md.

API access

Home Assistant exposes a REST API at http://hass.windy.lan:8123/api/ (same as http://192.168.55.11:8123/api/). Authenticate with a long-lived access token created under Profile → Security → Long-lived access tokens.

HA_URL="http://hass.windy.lan:8123"
HA_TOKEN="<long-lived-access-token>"

# Health check — expect {"message":"API running."} and HTTP:200
curl -sS -w "\nHTTP:%{http_code}\n" \
  -H "Authorization: Bearer $HA_TOKEN" "$HA_URL/api/"

# Read one entity state
curl -sS -H "Authorization: Bearer $HA_TOKEN" \
  "$HA_URL/api/states/sensor.csg_30d_max"

# List entities / recent errors
curl -sS -H "Authorization: Bearer $HA_TOKEN" "$HA_URL/api/states"
curl -sS -H "Authorization: Bearer $HA_TOKEN" "$HA_URL/api/error_log"
  • 401 → token invalid or expired; create a new one.
  • 404 on /api/states/<id> → entity does not exist.
  • The token is a secret: never commit it here; keep it in the shell environment or a secrets file outside the repo.

HTTP proxy gotcha (verified 2026-08-13)

The WSL client had http_proxy set to Mihomo (192.168.66.99:7890). LAN hostnames sent through that proxy returned empty 502, even though DNS resolved and the HA UI was up. Direct 192.168.55.11:8123 worked, and hass.windy.lan:8123 worked only after clearing the HTTP proxy.

Before debugging a "502" on a LAN URL, check env | grep -i proxy and bypass the proxy:

unset http_proxy HTTP_PROXY all_proxy ALL_PROXY
curl -sS -w "\nHTTP:%{http_code}\n" \
  -H "Authorization: Bearer $HA_TOKEN" "$HA_URL/api/"

For a persistent fix, add .windy.lan (leading dot) and the LAN ranges to NO_PROXY, or add *.windy.lan to the proxy's own bypass/skip-proxy list. See ~/.config/zsh/env/local/environment.env for the client-side setting.

Safe verification

ssh -o BatchMode=yes hassio@hass.windy.lan 'hostname; ip -4 addr show end0'

From a LAN client, confirm DNS and UI reachability:

getent hosts hass.windy.lan
# expect 192.168.55.11

Local patches (custom components)

Manual custom-component install (this host)

Home Assistant loads custom integrations from <config>/custom_components/<domain>/ (HAOS: /config/homeassistant). A folder named after the integration domain, containing at least manifest.json and __init__.py, is enough; Core must be restarted after copying files. Official HA lookup order: <config>/custom_components/<domain> then built-in homeassistant/components/<domain>. See Integration file structure.

This host does not git-clone custom components. The live tree is a file copy. Do not git pull on HA.

Official plugin path (from windyboy/china_southern_power_grid_stat README): HACS or 手动下载安装. This host uses the latter. Releases here have no uploaded zip assets; use GitHub's Source code (zip) / zipball of the tag.

UI (Samba / File editor / Studio Code Server):

  1. Download Source code (zip) from the GitHub Release.
  2. Extract. Copy only the inner custom_components/china_southern_power_grid_stat/ tree — not the repo root, not a nested extra folder.
  3. Place it at /config/custom_components/china_southern_power_grid_stat/.
  4. Restart Core (Settings → System → Restart).
  5. First install only: Settings → Devices & services → Add integration.

SSH from the workstation (verified 2026-08-14, W1N-107). Replace v1.3.1 with the tag being installed:

TAG=v1.3.1
STAGE=/tmp/csg-${TAG}-deploy
mkdir -p "$STAGE"
gh api "repos/windyboy/china_southern_power_grid_stat/zipball/${TAG}" \
  > "$STAGE/src.zip"
unzip -q "$STAGE/src.zip" -d "$STAGE"
SRC=$(find "$STAGE" -type d -path '*/custom_components/china_southern_power_grid_stat' | head -1)
# expect .../custom_components/china_southern_power_grid_stat

ssh -o BatchMode=yes hassio@hass.windy.lan \
  'sudo -n -i mkdir -p /homeassistant/.csg-backups &&
   sudo -n -i cp -a /homeassistant/custom_components/china_southern_power_grid_stat \
     /homeassistant/.csg-backups/china_southern_power_grid_stat.bak-$(date +%Y%m%d)-manual'

rsync -a --delete \
  -e 'ssh -o BatchMode=yes' \
  "$SRC/" \
  hassio@hass.windy.lan:/homeassistant/custom_components/china_southern_power_grid_stat/

# --delete cannot remove Core-owned __pycache__; wipe as root, then restart
ssh -o BatchMode=yes hassio@hass.windy.lan \
  'sudo -n -i rm -rf /homeassistant/custom_components/china_southern_power_grid_stat/__pycache__ \
     /homeassistant/custom_components/china_southern_power_grid_stat/*/__pycache__ &&
   sudo -n -i ha core restart'

Wait until Core is up (ha core info returns, typically 12 min; this CLI build does not print a state: field). Then:

ssh -o BatchMode=yes hassio@hass.windy.lan \
  'sudo -n -i cat /homeassistant/custom_components/china_southern_power_grid_stat/manifest.json'
# version must match the tag

ssh -o BatchMode=yes hassio@hass.windy.lan \
  'sudo -n -i ha core logs -n 2500' | grep -E 'china_southern_power_grid_stat|cannot pickle' || true

Host constraints (do not skip):

  • Backups must live in /homeassistant/.csg-backups/. A *.bak-* directory next to the live folder is scanned as the same domain and Core fails with No module named '...bak-YYYYMMDD-...'.
  • Do not install this fork via HACS on this host. HACS still tracks CubicPill/china_southern_power_grid_stat v1.2.0; a HACS update would overwrite the live copy.
  • First poll after restart can time out to CSG over IPv4; if this-month sensors stay unknown while last-month filled, reload the config entry (UI: integration → Reload, or supervisor POST /core/api/config/config_entries/entry/<id>/reload).
  • runbooks/scripts/ha-maintenance.sh --restart-core --yes can print nothing and exit 1 in under a second without restarting Core. The wrapper's ssh line discards stderr (2>/dev/null); with pipefail, an ssh failure yields empty stdout + exit 1 before any remote command runs. Do not treat that as a completed restart. Confirm with elapsed time (~2 min for a real restart) and ha core info. Prefer ssh -o BatchMode=yes hassio@hass.windy.lan 'sudo -n -i ha core restart'.

Full command family: runbooks/home-assistant-maintenance.md.

china_southern_power_grid_stat live tree

v1.3.2 (934f58c, verified 2026-08-15, W1N-118): manual zipball of GitHub release v1.3.2 copied to /config/custom_components/china_southern_power_grid_stat. Earlier trees: v1.3.1/55a293fc (W1N-107), v1.3.0/69f13c90 (W1N-106), a433e8c (W1N-105), de01914 (W1N-103), eb8b174 (W1N-102). Backups: /homeassistant/.csg-backups/ (w1n102/104/105/106/107/118).

v1.3.0 crashed the coordinator on first refresh (TypeError: cannot pickle 'mappingproxy' object in copy.deepcopy(self._config) under Python 3.14 / HA 2026.8.1). v1.3.1 wraps those deepcopy calls with dict(...). Post-restart 22:13 CST: entry loaded, no pickle traceback. Native this-month sensors filled after reloading entry 01KGCQDSZCF523A9X6SV3BZ1B9 (ip_family: ipv4). Native cost/ladder sensors can stay unknown because CSG get_month_daily_cost_detail returns a marketing-system SQL error; the dashboard uses template ladder/cost entities instead. Do not change templates/csg_sensors.yaml or the 电力监控 dashboard for an install.

Home PPPoE IPv4 to CSG is still blackholed (curl -4 to 218.19.148.218:443 times out). end0 IPv6 is enabled (ipv6.method: auto); from HA, curl -6 https://95598.csg.cn returns HTTP 200 via 240e:f9:8060::1:16.

tianqi weather recorder patch (verified 2026-08-13, W1N-75): /config/custom_components/tianqi/weather.py has a local patch adding _unrecorded_attributes = frozenset({"hourly_temperature", "hourly_skycon", "hourly_cloudrate", "hourly_precipitation"}) to the WeatherEntity class. Without it, weather.guangzhou's state attributes (~19 KB, dominated by the 4 hourly_* arrays of up to 48 entries) exceed the recorder 16384-byte limit, so the recorder drops all attributes for the entity and logs Recorder.db_schema: State attributes for weather.guangzhou exceed maximum size of 16384 bytes. The patch excludes only the 4 arrays from recording (live state unchanged; other attributes still stored; ~6.3 KB payload). Backup at weather.py.bak-w1n75. Re-apply after any tianqi component update. The _unrecorded_attributes mechanism exists in Core 2026.8.1 (Entity.__init_subclass__state_info["unrecorded_attributes"], consumed by recorder shared_attrs_bytes_from_event).

matrix_e2ee live tree (E2E Matrix bot, verified 2026-08-20)

v0.3.12 (tag v0.3.12; feat — Matrix activity events matrix_e2ee_message_received / matrix_e2ee_verification_done + push diagnostics; v0.3.9 added Connection health binary sensor, SAS/command allowlist split, URL normalization, single-entry enforcement): source copy from /home/windy/project/ha-matrix-e2ee ea421ed (tag v0.3.12) deployed 2026-08-20 via SSH rsync from workstation (upgraded from v0.3.2, backup matrix_e2ee.bak-20260820-v0.3.2). Custom matrix_e2ee integration — Config Flow (UI). See docs/home-assistant-matrix.md. Update runbook: runbooks/matrix-e2ee-update.md.

Earlier: v0.3.2 (tag v0.3.2, W1N-182/#34: wizard waits for inbound SAS emojis) deployed 2026-08-18 from d35c484 (backup matrix_e2ee.bak-20260818-v0.3.1); v0.3.1 (GitHub #33: peer-initiated verification wizard fix) deployed 2026-08-18 from d22e935 (backup matrix_e2ee.bak-20260818-v0.3.0); v0.3.0 (W1N-180/#32: bot-initiated verification wizard; W1N-179/#31 receive_mac_event cancel-state fix) deployed 2026-08-18 from 216cc99 (backup matrix_e2ee.bak-20260818-v0.2.10).

  • Bot @hass:chans.xyz reused (E2EE device rO1R915ncu). Config Entry 01M04D7C1M4T2GX5VPG7NVQ7GV (source: import, state: loaded). All settings via Settings → Devices & Services → Matrix E2EE → Configure.
  • Config Entry options: allowed_rooms ["!gidvAzpDzwtzfEDrqu:chans.xyz", "!boxfylDSzOvrWkcsyY:chans.xyz"], allowed_users ["@zhiqiang:chans.xyz"], command_prefix "!". verification_peer_users not set (v0.3.9+ SAS allowlist split from allowed_users, W1N-156): defaults to empty → only the bot's own account may drive SAS; @zhiqiang is denied until the option is added via Settings → Devices & Services → Matrix E2EE → Configure.
  • Storage: /config/.storage/matrix_e2ee_session.json + /config/.storage/matrix_e2ee_store/. Backups: /homeassistant/.matrix-e2ee-backups/ (incl. matrix_e2ee.bak-20260820-v0.3.2, matrix_e2ee.bak-20260818-v0.3.1, matrix_e2ee.bak-20260818-v0.3.0, matrix_e2ee.bak-20260818-v0.2.10, matrix_e2ee.bak-20260816-v0.2.9, matrix_e2ee.bak-20260816-v0.2.8); full HA backup slugs 3d9d36db (pre-v0.1.4) + 9f223f35 (pre-v0.2.0).
  • v0.3.12: Matrix activity events + push diagnostics (matrix_e2ee_message_received / matrix_e2ee_verification_done). v0.3.9: Connection health binary sensor (W1N-185/#40), config-entry diagnostics (W1N-184/#39), SAS/command allowlist split verification_peer_users (W1N-156/#41), SAS/sync logs demoted warning→info/debug (W1N-188/#38), URL normalization + single-entry enforcement (W1N-190/#42). v0.3.8: m.key.verification.done handshake for request-based SAS (W1N-183/#35). v0.3.2: wizard waits for inbound SAS emojis before the compare step (W1N-182/#34). v0.3.1: verification wizard waits for a peer-initiated inbound SAS instead of the bot starting SAS (GitHub #33). v0.3.0: bot-initiated device verification wizard (W1N-180/#32). v0.2.11: receive_mac_event no longer overrides canceled state (W1N-179/#31).
  • v0.2.9: restore SAS emoji rendering after vodozemac migration (W1N-175/#29). v0.2.8: SAS commitment unpadded base64 for Element interop (W1N-174/#28). v0.2.7: SAS cancel code/reason logging. v0.2.6: verification state logging + request→ready bridge. v0.2.4: _patch_nio_sas_timeout() + _repair_dropped_start(); VERIFICATION_TIMEOUT_SECONDS 600→240.
  • Automation 1761188403590「Matrix 聊天关卫生间灯」: trigger matrix_e2ee_command (command 关卫生间灯), actions light.turn_off + matrix_e2ee.send_message (room !gidvAzpDzwtzfEDrqu).
  • SAS not yet completed: every device requires explicit confirm_verification. Encrypted-room commands stay fail-closed until @zhiqiang's device is verified. Since v0.3.9 the SAS driver gate uses verification_peer_users (empty on this host) instead of allowed_users — add @zhiqiang:chans.xyz there before retrying the wizard. Three paths available: SAS manual confirm, fingerprint, or the device verification wizard (v0.3.0 bot-initiated, reworked in v0.3.1/v0.3.2 to wait for a peer-initiated inbound SAS from Element with emoji comparison), see docs/home-assistant-matrix.md § Device verification.

Known issues

Bluetooth hci0 instability — RTL8821CS (verified 2026-08-13, W1N-74): The local Bluetooth controller hci0 is an RTL8821CS combo chip on the x88 Pro board. Kernel logs show recurring hci0: hardware error 0x00, Opcode 0x200c tx timeout (HCI_LE_Set_Scan_Parameters), Unable to disable scanning: -110, Peer device has reset — the chip hardware-stalls during active scanning. HA's bluetooth_auto_recovery power-cycle then times out after 5 s and retries every ~2 min: bluetooth_auto_recovery.recover: Could not reset the power state of the Bluetooth adapter hci0 ... due to timeout after 5 seconds. The HAOS image already ships custom systemd units to cope (x88-bt-hci-recovery.service and a "Patch HA Bluetooth scanner mode for x88 RTL8821CS" service, visible in host journal). No user impact: there are no BLE entities in HA (xiaomi_ble / bthome / led_ble / bluetooth / esphome domains are all empty; platforms merely load from stray advertisements). Real IoT devices are Zigbee (via Zigbee2MQTT) or WiFi/MQTT/cloud. An ESPHome Bluetooth-proxy ESP32 (/config/esphome/bluetooth.yaml, bluetooth_proxy: active, WiFi ubnt-haas) is configured but currently offline (ESPHome add-on stopped, port 6053 unreachable) and produced no entities. Follow-up (optional): disable the local adapter and rely on the ESPHome proxy, or stop the bluetooth integration entirely.

eMMC disk lifetime 10% (verified 2026-08-13, W1N-76): ha host info reports disk_life_time: 10 — the boot eMMC (/dev/mmcblk2, CJTD4R 0xacacc064, 64 GB) has ~10% life left. disk_free: 40.2/56.4 GB. Full backup pre-maintenance-20260813 (slug 411a4ba5, 144.26 MB) taken 2026-08-13 covers current config; monitor disk_life_time on each health snapshot and plan a disk replacement / data-disk migration before the eMMC fails.

Matter Server (verified 2026-08-21)

  • Add-on core_matter_server (homeassistant/aarch64-addon-matter-server) runs the Matter commissioner on this host (host networking; add-on container app_core_matter_server).
  • After the ISP PD prefix rotates (PPPoE redial), the add-on can cache a stale IPv6 GUA in its mDNS advertisement — clients trying that dead address make Matter commissioning/connection fail. Fix: restart the add-on so it re-enumerates addresses: ssh hassio@hass.windy.lan 'sudo -n -i ha apps restart core_matter_server' (ha addons restart ... also works; "addons" is deprecated in favor of "apps").
  • Verified 2026-08-21 (W1N-207): stale 240e:3bd:234:2f22:* AAAA in mDNS removed by restart; advertisement now carries only current GUA 240e:3bd:235:1fb2:* + link-local; CASE sessions with Aqara M3 / SmartThings hubs resumed over IPv6 link-local.

Open items (2026-08-21, W1N-207): a phone on LAN55 was querying five known _matter._tcp instances of which only HA answered — the other Matter nodes are offline / not announcing (device-side; user to confirm power/Wi-Fi). HA's IPv6 default route via NetworkManager was observed missing once (curl -6 intermittent, while ping6 and curl -6 --noproxy work) — not the Matter root cause; re-check on the next health snapshot.