[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: ```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 | |---|---| | `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](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 end0' ``` 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.2** (`934f58c`, verified 2026-08-15, W1N-118): manual zipball of GitHub release [v1.3.2](https://github.com/windyboy/china_southern_power_grid_stat/releases/tag/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. **`templates/csg_sensors.yaml` hardened 2026-08-29 (W1N-239):** added `availability` templates to all 12 `csg_*` sensors (numeric sensors can't render `unknown`/`unavailable` in `state`; availability suppresses rendering instead — native CSG down ⇒ derived sensors show `unavailable`, no more fake zeros / "一档" / `0%`). `csg_yesterday_kwh` now falls back to `last_month_by_day`'s last entry when `this_month_by_day` is empty (month start); ladder constants (`t1/t2/p1/p2/p3`) deduped into per-block `variables:` (Block B + Block D); `csg_mom_change` parses `date` defensively. Backup: `/homeassistant/.csg-backups/csg_sensors.yaml.bak-20260829-w1n239`. **Verified:** `ha core check` OK; Core restart required (trigger-based template blocks don't settle on `template.reload` — W1N-114 precedent); post-restart all 12 entities numeric & consistent (302.47 kWh→180.28 元, 324.03 kWh→194.06 元, mom_change -3.6%, yesterday 7.66 kWh/2026-08-28), no template errors in Core logs. **`csg_sensors.yaml` off-by-one fixed 2026-08-29 (W1N-241):** CSG data lags 1 day (`sum(this_month_by_day)` == `this_month_total_usage`, data stops at yesterday), but templates used `now().day` as "days elapsed" → `csg_predicted_usage` underestimated ~1 daily avg (~3%) and `csg_mom_change` compared this-month 28 days vs last-month 29 days (-3.6% vs true -0.3%). Both now derive the day number from `this_month_by_day[-1].date` (fallback `now().day` when empty). Added `sensor.csg_this_month_daily_avg` (month-to-date avg, 302.47/28=10.8) and `sensor.csg_prediction_progress` (usage/predicted %, 90.3) in Block C (trigger adds `csg_predicted_usage`). Backup: `/homeassistant/.csg-backups/csg_sensors.yaml.bak-20260829-w1n241`. **Verified (8/29):** predicted 324.03→334.81, mom_change -3.6→-0.3, daily_avg 10.8, progress 90.3, predicted_cost 194.06→200.94 (334.81 kWh ladder), ladder cost 180.28 unchanged, `ha core check` OK after restart, no template errors; 14 csg_* entities total. **电力监控面板(`lovelace.dashboard_unknown` / view `power-monitor`) updated 2026-08-29 (W1N-240 + W1N-242):** 「本月累计」gauge 对齐夏季阶梯: `max:650`、segments `0/260/600`(绿/橙/红 = 一/二/三档;冬季 11-01 需切 `max:450`、`0/200/400` — **seasonal switch point**,见下文)。「📊 统计 数据」卡新增本年/去年 4 行(原生传感器,口径标注「电费(账单)」、本年 「(至今)」)+ 本月日均/预测进度 2 行(`csg_this_month_daily_avg` / `csg_prediction_progress`,W1N-242);面板共引用 **20** 个实体。改前备份: `/homeassistant/.lovelace-backups/dashboard-unknown-power-monitor-20260829-204845.json` (W1N-240)、`-20260829-210708.json`(W1N-242) (改法:WS `lovelace/config/save`,参数 `url_path: dashboard-unknown` + `config`;勿直改 `.storage/`)。验证:WS 读回 18→20 实体 diff ✓、gauge 配置一致 ✓、URL `http://hass.windy.lan:8123/dashboard-unknown/power-monitor`。 **`csg_sensors.yaml` W1N-242:** `csg_predicted_usage` / `csg_mom_change` / `csg_this_month_daily_avg` 三处取 `days[-1]` 前补 `sort(attribute='date')`(与 `csg_yesterday_kwh` 一致,防上游乱序取错 数据日)。备份 `csg_sensors.yaml.bak-20260829-w1n242`。验证:Core restart 后回归值不变(334.81 / -0.3 / 10.8 / 90.3 / 200.94 / 180.28)。 **CSG 面板重构 2026-09-04(VPS-90,先核对计价后展示层改动):** 核对 `power-monitor` 计价与 8 月账单一致(198.65 vs 账单 198.64,差 ≤0.01 元, 因模板用公众圆整价 0.589/0.639/0.889、账单用 6 位精确价),不改阶梯常量。 改动:① `csg_sensors.yaml` Block B 新增 `sensor.csg_this_month_avg_price`(本月阶梯电费÷本月用电,`元/kWh`, availability 照 W1N-239 惯例;**csg_* 实体 14→15**);② 面板改名「环比上月」 →「环比上月同期」;glance「本月/上月」grid 去重为单卡「上月」(本月用电/电费 行归 💰核心数据卡);⚡阶梯电价卡加「本月实际均价」行(当前档位/当前电价/ 本月实际均价/档位剩余;面板唯一实体引用 20→21);③ `automations.yaml` 加 2 条提醒:`automation.csg_mian_ban_qie_dong_ji_dang_ti_xing`(10-25 09:00) 与 `automation.csg_mian_ban_qie_xia_ji_dang_ti_xing`(4-25 09:00)经 `matrix_e2ee.send_message` 提醒切 gauge。④ 金额单位混排(原生 CNY vs 模板 元)**维持**:`config/entity_registry/update` 拒绝自定义文本单位 (`extra keys not allowed … Got '元'`),用户确认接受。备份: `.lovelace-backups/dashboard-unknown-power-monitor-20260904-204757-pre-refactor.json`、 `.csg-backups/csg_sensors.yaml.bak-20260904-204757-pre-refactor`(及 `-205301-pre-avgprice`)、`.automations-backups/automations.yaml.bak-*`。 **WS 改法(2026.8,本机实测)**:core/主机 python 无 ws 库、core 容器内经 supervisor 代理 WS 被拒(loop prevention),用 `docker run --rm --network host -e SUPERVISOR_TOKEN`(supervisor 镜像 `aarch64-hassio-supervisor:2026.08.0`)连 `ws://172.30.32.2/core/websocket`; 命令名 `lovelace/config`(读)+ `lovelace/config/save`(写), `lovelace/config/get` 已不存在(unknown_command)。验证:新实体 0.589 元/kWh、15 个 csg_* 数值齐全、回归值不变(14.09/198.65/331.22/ 304.99/181.89)、automations on、`ha core check` OK、日志无 template 错误。 **CSG 长期归档(W1N-243, 2026-08-29):** scribe 库新增 `csg_history` 表(逐日 usage/cost/ladder/balance + 逐月累计;2026-07-01 起回填,永久), 由 TimescaleDB 每日任务 **1008** `csg_daily_snapshot()`(22:30 Asia/Shanghai;**TS job 非 pg_cron**)upsert 维护。日费用在原生 `latest_day_cost` 缺失时回退 = 昨日用电 × 当前档费率(模板 `csg_current_ladder_tariff` 0.639);月费用回退模板 `csg_this_month_ladder_cost`。**语义**:day 行 usage/cost 为该日值, ladder/balance 为 22:30 快照值。详见 [hosts/pgdb.md](../hosts/pgdb.md)。 > **Seasonal gauge switch (W1N-240 已知事项):** 每年 **11-01** 把 > `power-monitor` 视图「本月累计」gauge 切到冬季 `max:450` / > `0/200/400`,**5-01** 切回夏季 `max:650` / `0/260/600`(与模板 > `now().month` 季节逻辑对齐;模板常量在 Block B/D `variables`)。 > **提醒 automation(2026-09-04 起,VPS-90):** > `automation.csg_mian_ban_qie_dong_ji_dang_ti_xing`(10-25)与 > `automation.csg_mian_ban_qie_xia_ji_dang_ti_xing`(4-25)09:00 经 > `matrix_e2ee.send_message` 发操作步骤提醒;gauge 的 max/segments 无法 > 模板化,仍需人工改卡配置。 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](../docs/home-assistant-matrix.md). **Update runbook:** [runbooks/matrix-e2ee-update.md](../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](../docs/home-assistant-matrix.md). ### Scribe long-term history (verified 2026-08-29) - **Scribe 3.8.0** (`/homeassistant/custom_components/scribe/`), configured from `/homeassistant/scribe.yaml` — W1N-238 moved the block out of `configuration.yaml` on 2026-08-29 (main config now carries `scribe: !include scribe.yaml`; content moved verbatim; backup `configuration.yaml.bak-20260829-201724-w1n238`). Config entry `01KC2VFJWEQ3XDHY6TQKHPDVRB`, `source: import` — UI "Configure → Advanced" edits are overridden by the YAML on restart; treat YAML as authoritative. - TimescaleDB at `192.168.55.15:5432/scribe` (DB user `hass`; host in inventory, see [hosts/pgdb.md](../hosts/pgdb.md)). Database re-initialized 2026-08-29 14:06 CST (user-handled; earlier `relation "entities" does not exist` errors resolved). Health: `binary_sensor.scribe_database_connection`. - 2026-08-29 config applied (backup `/homeassistant/configuration.yaml.bak-20260829-scribe`): - `record_events: true` with `include_events` whitelist: `automation_triggered`, `matrix_e2ee_command`, `matrix_e2ee_message_received`, `matrix_e2ee_verification_done`, `script_started`, `tag_scanned`, `mobile_app_notification_action`, `homeassistant_start`, `homeassistant_stop`. - State noise trimmed: `exclude_domains` update/button; glob `sensor.zigbee2mqtt_bridge_*`; 4 hassio cpu/mem-percent entities. - Global `exclude_attributes` drops tianqi `hourly_*` arrays (~19 KB/state — the recorder-side `_unrecorded_attributes` patch does not apply to Scribe). - `enable_stats_io` + `enable_stats_size` on → 14 `sensor.scribe_*` stats entities (`scribe_states_written`, `scribe_events_written`, rates, sizes). - Verified post-restart 14:23 CST: writer started, `scribe_events_written=1` (homeassistant_start), states ~110/min, buffer 3, no scribe log errors. - **Scribe 3.8.0 has no retention option.** Retention ships only in the v4.x line, which as of 2026-08-29 has no stable release (v4.0.0rc1/v4.1.0rc1 are prereleases; user declined RCs — data keeps growing until an upgrade). v4.x is a major rewrite (writer.py largely rewritten, migration.py removed, TimescaleDB extension required): re-read release notes before upgrading. Do not expect retention YAML keys to validate on 3.8.0. - Recorder stays external-Postgres with `purge_keep_days: 365` (W1N-243, 2026-08-29, raised from 30 — ~300 MB/yr, 1% of the 30G pgdb disk) for native UI per-change history; Scribe is the permanent archive. Long-term statistics stay permanent (not purged by `purge_keep_days`). Note: extending retention does **not** recover pre-2026-08-29 raw history (already purged); only `csg_history` day/month values cover that period. ### Config layout: scribe.yaml + templates/ merge (W1N-238, verified 2026-08-29) - `configuration.yaml` line 29: `scribe: !include scribe.yaml`; line 9: `template: !include_dir_merge_list templates`. No `packages/`. - `scribe.yaml` (config root): the Scribe block, content identical to the former inline one; import semantics unchanged. - `templates/`: `csg_sensors.yaml` (12 template sensors, top-level **list**) + `quick_sensors.yaml` (scaffold for Quick-derived `quick_*` sensors, empty list with convention header). **`!include_dir_merge_list` merges per-file lists; non-list files are silently skipped** — every file in `templates/` must be a top-level list (`- sensor:` blocks). Directory include only picks up `*.yaml`, so the `.bak` / `.pre-*` backups in the dir are ignored. After adding sensors, verify template-platform entity count = 12 + N (entity registry `platform: template`). - Convention (per review + W1N-233): pure sums/averages stay min_max helpers (e.g. `sensor.dang_qian_zong_gong_lu`); only template-logic derivations (ladder pricing, cross-entity conditions) go into `quick_sensors.yaml`. - Post-change verification 20:19 CST: `ha core check` ok, 92 s restart (2026.8.3), `binary_sensor.scribe_database_connection` on, `scribe_states_written` 18581→19426 growing, template entities still 12, csg sensors numeric, no scribe/template log errors. ### Timescale Plotly card + database reader (verified 2026-08-29) Chart stack over the Scribe TimescaleDB archive. Upstream pair (no HACS; manual copies): reader `remmob/timescale_database_reader` **v1.1.0** (main `bb8776a`) + card `remmob/timescale-plotly-card` **2.2.0** (main `217961d`). - **Reader integration**: `/homeassistant/custom_components/timescale_database_reader/`. Config entry `01M165P77QT1FQEAVPNZHDT82W` ("Scribe", `source: user`): connects `hass@192.168.55.15:5432/scribe` (credentials = `secrets.yaml` `scribe_url`), `table: sensor_minute`. Exposes no entities/services — it serves WS command `timescale/query` (window ≤ 365 d, ≤ 50 000 rows, `downsample` bucket seconds). Benign startup warning `Error executing test query: column "time" does not exist`: the self-test SQL assumes the LTSS column name; the scribe table uses `minute` — real queries work (verified: 70 rows for a live power sensor). - **Card**: `/homeassistant/www/community/timescale-plotly-card/timescale-plotly-card.js` (root-owned, same convention as HACS dirs). Lovelace resource (storage) id `2e360d17b5aa4ce59c2fd13c43b51215` → `/hacsfiles/timescale-plotly-card/timescale-plotly-card.js`, type `module`. Card config matches the entry by `database: scribe` (name from the reader entry). Updates: replace the file, resource URL unchanged — browsers need a hard refresh or a bumped `?v=` query on the resource URL. - **pgdb side** (`sensor_minute_aggregate` cagg + `sensor_minute` hypertable + every-minute refresh job): see [hosts/pgdb.md](pgdb.md) § Databases. - **Agent-side HA WebSocket without a long-lived token** (verified 2026-08-29): connect `ws://supervisor/core/websocket` with header `Authorization: Bearer $SUPERVISOR_TOKEN`, then send `{"type":"auth","access_token":"$SUPERVISOR_TOKEN"}` — the Supervisor proxy swaps it for a core token (works as the internal Supervisor admin user). Note `lovelace/resources/create` in HA 2026.8 takes `res_type` (NOT `resource_type`). - Scribe stores numeric sensor values in `states_raw.value` with `state` NULL, so `sensor_minute.state` shows `'0'` for numeric sensors; the card plots `avg_state` (from `value`) — expected, not a bug. - **Quick 仪表盘(`dashboard-quick`)图表套件**(2026-08-29 创建,经 WS `lovelace/config/save` 写入;W1N-230 修复 + W1N-231 round-2 改进): 5 张 timescale 卡——大功率电器/常驻负载功率(按量级拆图,避免尖峰压扁 <70 W 基线)、按插座用电量(`energy_mode` + cumulative/diff,数据质量前提 见 pgdb 的 refresh 过程补丁)、室内外温湿度(温度左轴/湿度右轴,4 位置同色 配对)、人体感应活动状态(3 个 `motion_state`,banded `state_map` none/small/medium/large → 0-11,per-entity `line_color` 红/蓝/绿)。 空调实体引用为 `kong_diao_*`(`kong_tiao` 是笔误,W1N-230 修复;`grep -c kong_tiao` 应为 0)。灯区:2×2 嵌套 grid(`grid_options: {columns: "full"}`, 内层 `columns: 2`)+ 4 卡统一 `mushroom-light-card`(显式 name、 `use_light_color: false`、内联亮度/色温控制),heading icon `mdi:lightbulb-group`。heading badges:环境 4 温度(迷你/mini数显/数显/广州)、 大功率电器 空调/电脑当前功率、常驻负载 总功率 (`sensor.dang_qian_zong_gong_lu`,min_max **sum** helper,`round_digits: 0`, 任一源掉线 fail-closed → unknown)。常驻负载图卡级 `fill: 'tozeroy'` + 冰箱/主网络 per-entity `fill_color`(线色 20% 透明)+ 其余 5 条 `fill: false` (per-entity fill 逐系列退出,卡 JS `seriesConfig.fill !== false`)。 布局:视图 `type: sections` + `max_columns: 4`;灯/用电/环境/人体感应 `column_span: 4`,功率两图拆两个 `column_span: 2` 分区**并排**(等高 280px, 桌面并排、手机回落堆叠;去卡内 title 省半宽图垂直空间)。 **分区/卡片是两套尺寸键,不可混用**:分区宽 = `column_span` (`hui-sections-view.ts` 缺省按 1 列渲染,绝不省略);卡片宽 = `grid_options: {columns: }`(`hui-card.ts` 只读 `config.grid_options`, 写在卡片上的 `column_span` 被静默忽略;缺省 12 列,分区内格 = 12 × 分区 span,故 span-4 分区里缺省卡片只有 1/4 宽)。 修改前备份:`/homeassistant/.lovelace-backups/dashboard-quick-*.json` (W1N-230 修复: `20260829-190256`;round-2 改进: `20260829-194040`)。 ### 地图仪表盘:CARTO keyed tiles via `custom:map-card` (verified 2026-08-30, W1N-261) - **背景:** CARTO 自 2026-08-26 起对无 key 栅格瓦片打 "API KEY REQUIRED" 水印,内置地图卡/zone 编辑器全部受影响。Core 2026.8.3 的 `MapCardConfig` **没有任何瓦片配置项**(frontend 20260729.7 源码核对: `setup-leaflet-map.ts` 硬编码 CARTO voyager URL)。上游修复是 2026.9.0b1 起改用 OSMF 矢量瓦片(frontend PR #53816),stable 预计 2026-09-02 前后。 - **变更:** 「地图」仪表盘(url_path `map`,storage)唯一 map 卡替换为 `custom:map-card`([nathan-gs/ha-map-card](https://github.com/nathan-gs/ha-map-card) **v1.16.0**,手动安装非 HACS):`tile_layer_url` = `https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png?key=` (配 `tile_layer_options: {subdomains: abcd, maxZoom: 20}` + OSM/CARTO attribution)。实体不变:2 person + 4 zone(zone 用 `display: icon` + `circle: auto`,circle 读实体 `radius` 属性画半径圈)。 - **CARTO key 是 secret**: 只存在于服务端 lovelace 存储(dashboard `map` 的卡片配置)和用户本人处;勿写入本仓库或 Linear。 - **文件/资源:** `/homeassistant/www/community/ha-map-card/map-card.js` (root:root 644,678554 B,sha256 `f30dfb606e858d2216d5198d8cf758ce956d127006ebd7d66d4329153a247ec2`); Lovelace resource(storage)id `9d2b50b52c60420d89ebd041f722cf60` → `/hacsfiles/ha-map-card/map-card.js`,type module(WS `lovelace/resources/create`,2026.8 参数名 `res_type`)。升级 = 手动替换 该文件(不在 HACS 管理下,浏览器需强刷)。 - **备份:** `/homeassistant/.lovelace-backups/dashboard-map-map-20260830-133714.json` (还原 = 把备份里的 `views[0].cards[0]` 写回后再 WS `lovelace/config/save` url_path `map`)。 - **验证 8/30:** 同瓦片无 key=水印 / 带 key=干净(256×256 PNG 视觉对比); resource HTTP 200 text/javascript;WS 读回卡片配置(type/entities/key/ attribution/options)全部符合;HA 主机 `curl -4` 带 key 瓦片 200。 - **Follow-up:** Core 升 2026.9.0 stable 后内置地图/zone 编辑器自动切 OSMF 矢量瓦片;届时可保留 custom 卡(继续 keyed CARTO)或用备份还原 内置卡。zone 编辑器等其余内置地图的水印在 2026.9 前无解。 ## 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. Verified 2026-08-23 (read-only, W1N-207): add-on `started`, version `9.0.4`, no update pending; current GUA `240e:3bd:238:4812:*` (PD rotated again since 08-22) advertised correctly over v4+v6. Both ESP32-C2 bulbs now announce `_matter._tcp` (multi-fabric, including this host's fabric `DCE86145C137AF0E`) — but they **refuse TCP 5540 on IPv4 and IPv6**, so matter-server holds **zero established :5540 sessions** (device-side failure mode C; no errors logged — see [docs/matter-pairing-troubleshoot.md §8](../docs/matter-pairing-troubleshoot.md)). ## 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