[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** | **PVE VM 180 (`haos`)** — not a separate physical host (verified 2026-08-09) | | 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: ```bash 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 | |---|---| | `end1` | IPv4 static `192.168.55.11/24` (gw `.254`, DNS `192.168.66.36`); IPv6 SLAAC `auto` with GUA on `240e:3bd:231:2082::/64`; primary LAN55 NIC | | `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](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**. ```bash HA_URL="http://hass.windy.lan:8123" HA_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/` → 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: ```bash 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 ```bash ssh -o BatchMode=yes hassio@hass.windy.lan 'hostname; ip -4 addr show end1' ``` From a LAN client, confirm DNS and UI reachability: ```bash 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 `/custom_components//` (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: `/custom_components/` then built-in `homeassistant/components/`. See [Integration file structure](https://developers.home-assistant.io/docs/creating_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](https://github.com/windyboy/china_southern_power_grid_stat)): HACS **or** [手动下载安装](https://github.com/windyboy/china_southern_power_grid_stat/releases). 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: ```bash 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 1–2 min; this CLI build does not print a `state:` field). Then: ```bash 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//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](../runbooks/home-assistant-maintenance.md). ### `china_southern_power_grid_stat` live tree **v1.3.1** (`55a293fc`, verified 2026-08-14, W1N-107): manual zipball of GitHub release [v1.3.1](https://github.com/windyboy/china_southern_power_grid_stat/releases/tag/v1.3.1) copied to `/config/custom_components/china_southern_power_grid_stat`. Earlier trees: v1.3.0/`69f13c90` (W1N-106), `a433e8c` (W1N-105), `de01914` (W1N-103), `eb8b174` (W1N-102). Backups: `/homeassistant/.csg-backups/` (w1n102/104/105/106/107). 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). `end1` 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`). ## 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. ## Related docs - [runbooks/home-assistant-maintenance.md](../runbooks/home-assistant-maintenance.md) — `ha` CLI maintenance runbook + [script](../runbooks/scripts/ha-maintenance.sh); custom-component zip install is §7 - [docs/lan-overview.md](../docs/lan-overview.md) — LAN map and gw port-forward - [hosts/dns.windy.lan.md](dns.windy.lan.md) — `hass.windy.lan` / `hass.local` rewrites