Files
vps/hosts/hass.windy.lan.md
T
windyboy d6fbbe13c5 docs(hass): 把主机文档收成现行状态
Quick 布局、季节 gauge、网卡 end1 和 Core 2026.9.3 与旧操作说明不一致。抓包命令改为 end1。
2026-09-23 08:36:57 +08:00

854 lines
54 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
[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) |
| Core | 2026.9.3; Supervisor 2026.09.2; HAOS 18.2 (verified 2026-09-23) |
| 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` (prefixes rotate on PPPoE redial; old GUAs can linger). Primary LAN55 NIC. Live name on 2026-09-23 is `end1`; the 2026-08-22 `end0` name is gone |
| `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="<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:
```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
`<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](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 12 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/<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](../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`(绿/橙/红 = 一/二/三档)。冬季档不要再手改,由下方 v4 的 `binary_sensor.csg_summer_tariff` 切换。「📊 统计
数据」卡新增本年/去年 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-04VPS-90;面板布局已被下方 2026-09-22 v4 取代。仍有效的是均价传感器和两条季节提醒):** 核对
`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 改法**`lovelace/config` 读、`lovelace/config/save`
`lovelace/config/get` 不存在)。客户端用 [`scripts/ha-ws-client.py`](../scripts/ha-ws-client.py)
见下方 Quick 节。勿直改 `.storage/`。验证:新实体
0.589 元/kWh、15 个 csg_* 数值齐全、回归值不变(14.09/198.65/331.22/
304.99/181.89)、automations on、`ha core check` OK、日志无 template 错误。
**CSG 面板 Sections 重构 2026-09-22VPS-90 v4:**
`dashboard-unknown/power-monitor` 从单列 Masonry 改为原生 Sections(最多 2 列、
关闭 dense placement),并新增子视图 `power-details`。主页按「本月概览 → 阶梯
电价 → 最新数据/季节累计 → 近 30 日趋势」组织:概览只保留本月用电/阶梯电费
与明确标为「预计」的用电/电费;余额因口径未独立验证不展示;欠费仅在
`sensor.0800041935246530_arrears > 0` 时显示。详情页承载上月、近 30 日、月日均、
环比及本年/去年账单统计;移除无决策价值的 `csg_prediction_progress` 展示。
趋势图使用 `custom:apexcharts-card` 读取 `sensor.csg_history.history_data` 最近 30 条
(该属性来源是上月 + 本月逐日明细),画日柱和 30 日均线,不接 Timescale reader
两条 series 均显式覆盖 `unit: kWh`,避免继承 `sensor.csg_history` 的记录数单位
「条」。用户首轮目视反馈后,「最新数据与本月进度」改为跨两栏:最新日用电与
本月日均各占桌面半栏,当前季节 gauge 全宽,窄屏由 Sections 自动回落堆叠。
外部实施审核收口时补齐预测不确定性标识:两张 Tile 分别命名为
「预计本月用电(估算)」/「预计本月电费(估算)」,且「本月概览」副标题明确
写明「预测按当前速率推算,月内会调整」;仅改展示文案,不改计算或实体。
季节 gauge 由 UI Template Helper 自动切换:配置入口
`01M337A71HTAEBF8AB769J02WF`domain `template`title
`CSG Summer Tariff`source `user`state `loaded`),实体
`binary_sensor.csg_summer_tariff`,状态模板
`{{ 5 <= now().month <= 10 }}``on` 显示夏季 `max 650 / 260 / 600``off`
显示冬季 `max 450 / 200 / 400`;状态既非 on 也非 off 时显示警告。现有 4-25 /
10-25 Matrix 提醒保留,不改 automation;该 helper 当前只被此 dashboard 引用。
写入前 WS 快照:
`/homeassistant/.lovelace-backups/dashboard-unknown-power-monitor-20260922-0815-pre-sections-v4.json`
4953 bytesSHA-256
`f5e3e05ab29a4c13b870547b0a189e95b793d85e82c85c61190dedf66811a874`)。
首轮 v4 到布局/单位修正前的第二快照:
`/homeassistant/.lovelace-backups/dashboard-unknown-power-monitor-20260922-0825-pre-layout-unit-fix.json`
SHA-256 `8b56e1303572405d7c850305ffd435cc5c7ca583330160fdd3f8b213473e1fcd`)。
外部审核文案修正前的第三快照:
`/homeassistant/.lovelace-backups/dashboard-unknown-power-monitor-20260922-0838-pre-estimate-copy-fix.json`
(文件 SHA-256 `b1c1e0c6946818ce7fb44d5d731e3e78aebe9c8d51ed465ce8505a0f1855c05c`)。
验证:`lovelace/config/save` 后规范化 JSON 哈希与 payload 一致;22 个引用实体全部
存在且无 unknown/unavailable;夏季 helper=`on`;图表 30 条为 2026-08-22 至
2026-09-20,重算均值 9.15 kWh 与 `sensor.csg_30d_avg` 一致;最终文案修正后的
规范化配置哈希为
`c16d11c01353f90761da5837b06cc7ad1ea7b400f249c99cf291c03f4980a28d`。浏览器没有 HA
登录态,390/768/桌面三宽度视觉验收仍待人工登录后完成。回滚时先把上述 JSON
经 WS `lovelace/config/save` 写回;再扫描 helper 引用,确认无消费者后才可删除
配置入口,勿直改 `.storage/`
**CSG 长期归档(W1N-243, 2026-08-29;任务改号 2026-09-21;口径统一 v5 2026-09-22:**
scribe 库 `csg_history` 表(逐日 usage/cost/ladder/balance + 逐月累计;2026-07-01 起
回填,永久),由 TimescaleDB **两个**任务维护(**均为 TS job,非 pg_cron**):
**1010** `csg_daily_snapshot()`14:30 UTC = 22:30 Asia/Shanghai)写当日行;
**1011** `csg_backfill_missing_days()`15:10 UTC = 23:10 Asia/Shanghai)是
**断档自愈**——只 `INSERT` 缺失日期、`ON CONFLICT DO NOTHING`**绝不覆盖既有行**。
> **归档逻辑全部在 pgdb 侧,不经过 HA。** HA 的 scribe 集成只往 `states_raw` 写状态,
> 上面两个任务在 pgdb 侧读 `states_raw` 落表。因此 `csg_history` 相关的口径/job 变更
> **不需要改 HA 配置、不需要重启 Core**。另注意:模板传感器
> `sensor.csg_history`state = 天数「51 条」)读的是集成 attributes
> `this_month_by_day` / `last_month_by_day`**与这张同名表无关**,别混淆。
**v5 口径(2026-09-22**:数据日期与当日用量取**同一实体同一行**
`sensor.0800041935246530_latest_day_kwh``latest_day_date` 属性 + `value`v3 曾用它的
属性定日期却用 `yesterday_kwh` 取值,两实体可能错配,且后者归档少一条 09-06 = 9.29)。
日费用 = `round(csg_ladder_cost_raw(c1) csg_ladder_cost_raw(c0), 2)`,即**当月累积的
边际差分**,与 v3 回填同口径;`csg_ladder_cost_raw` 是新增的**无舍入**版助手(直接用
`csg_ladder_cost(c1) csg_ladder_cost(c0)` 会二次舍入,7 月实测 1 天差 0.01)。月行由
`csg_ladder_cost` 单次舍入重算。**语义**day 行 usage/cost 为该日值,ladder/balance 为
快照值;各月 `|sum(day cost) month cost| ≤ 0.05` 元是**逐日舍入的累积**(非缺陷),
月行才是权威值。脚本 `compose/pgdb/csg-snapshot-v5.sql`,快照 `csg_history_bak_20260922`
健康检查:[pgdb-health](../runbooks/pgdb-health.md) 第 9 项专查归档新鲜度——**上游停更时
job 会反复 upsert 同一行、`last_run_status` 仍是 `Success`、日期上也没有缺口,只有
「最新 day 行日期」这条断言能发现**。详见 [hosts/pgdb.md](pgdb.md) § Known issues
2026-09-211008 从未成功的根因与回填)与 **2026-09-22**(v5 口径统一 + 自愈 + 校验)。
**CSG 预测值复核(2026-09-21,只读):**
`sensor.csg_*` 全部 14 个派生值用独立 Python 实现按同一输入重算,逐个与 HA
live state 完全一致(`csg_30d_avg` 9.07 / `csg_predicted_usage` 268.45 /
`csg_predicted_cost_ladder` 158.54 / `csg_this_month_ladder_cost` 99.35 /
`csg_mom_change` -21.6 等)。同时把 9 月逐日口径对回 scribe:集成
`this_month_by_day`09-01..09-1919 天,和 168.68)与 `states_raw`
`yesterday_kwh` 观测值逐日一致(唯一差异 09-06 = 9.29 是 scribe 漏采、集成有值),
确认输入数据无缺口。**因此「预测本月电费」不是算错,而是估计器的窗口问题**:
`csg_predicted_usage = 本月累计 + avg × 剩余天数`,旧版 `avg = csg_30d_avg`
`history_data[-30:]` 的**普通 30 日均值、跨月重叠**)→ 月初被上月(更热)拖高
09-02 实测 317.93 kWh / 190.16 元),随本月凉快日置换回落到 268.45 / 158.54
一个月内摆动 **-17%**。**另注**:集成原生
`latest_day_cost` / `this_month_total_cost` / `last_month_total_cost`
2026-08-29 起**恒为 unknown**`states_raw` 无一条有值),故面板上所有「元」
均为模板阶梯估算值,从未与账单口径对账。
**`csg_sensors.yaml` 预测速率 v22026-09-21,已实施):** 只改
`csg_predicted_usage` 的速率项,`csg_30d_avg` 语义不动(面板「近30日平均」照旧)。
新公式:`avg = mtd × w + recent7 × (1 w)`,其中 `mtd = 本月累计 / 已过天数`
`recent7` = `history_data` 末 7 条均值、`w = min(已过天数, 14) / 14`;两者都取不到
才退回 `csg_30d_avg`。**动机**:本月至今速率按天数爬升可信度,避免月初被上月污染;
近 7 日速率兜底,因为月初唯一的新数据就是上月末。Block C 触发器加
`sensor.csg_history`。**19 天回测**(基准 = 第 19 天本月至今速率外推,逐日重放
集成 `this_month_by_day` + `last_month_by_day`):
| 速率方案 | 预测电费摆动 | 最大偏差 | 平均绝对偏差 |
|---|---|---|---|
| 旧:30 日均值 | 21.1% | 35.99 元 | 14.04 元 |
| v2K=7 | 14.6% | 16.81 元 | 5.65 元 |
| **v2K=14(采用)** | **13.9%** | **15.33 元** | **4.94 元** |
| v2K=14 但兜底用 30 日均值 | 23.3% | 33.12 元 | 6.28 元 |
(末行证实病根就是那个跨月的 30 日均值兜底。)备份
`/homeassistant/.csg-backups/csg_sensors.yaml.bak-20260921-pre-predictor`
(改前)与 `…-pre-k14`K=7 中间态)。**验证**`ha core check` OKCore 重启两次
trigger 模板块不吃 `template.reload`W1N-114 先例);15 个 `csg_*` 全部数值、
无 template 错误;当天 `n=19 ≥ 14` 故两版同值,面板读回
`csg_predicted_usage` 266.34 kWh / `csg_predicted_cost_ladder` 157.19 元 /
`csg_prediction_progress` 63.3%;新值已进 scribe 归档
`states_raw` 08:17:04Z 158.54 → 08:17:10Z 157.19)。**注意**`w` 的爬升窗口
(14 天)是回测选出来的单月参数,属弱证据,换季/换季后值得复看。
> **季节 gauge** 不要手改 max/segments。`binary_sensor.csg_summer_tariff`
> (`on` = 510 月)切换夏季 `650 / 260 / 600` 与冬季 `450 / 200 / 400`
> 见上方 VPS-90 v4。`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 提醒;提醒正文这次没核对。
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`).
### `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 (3.8.0 setup 2026-08-29; 4.4.0 verified 2026-09-13)
- **Scribe 4.4.0** (`/homeassistant/custom_components/scribe/`, HACS repo
`jonathan-gtd/scribe`, = latest stable 2026-09-12; upgraded 2026-09-13 together
with Core 2026.9.1 / HAOS 18.2), 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.
- **4.x upgrade核对 2026-09-13(只读 + 一处配置变更)**live `manifest.json` =
4.4.0。两个 4.0 breaking change 在本机都不需要动作——数据库是 3.x 结构
`states_raw` PK `(metadata_id, time)` 在,4.2.0 的启动态去重因此可用),
TimescaleDB 2.29.2 已装。4.1.0 修了 `db_url` 优先级,YAML 里的
`!secret scribe_url` 现在是权威。`scribe.yaml` 现有键在 4.4.0 全部仍然合法
(未知键被忽略,`extra=vol.ALLOW_EXTRA`)。**配置优先级 YAML > entry
`options` > entry `data` > 默认值**,而 `_resolve_settings` 读的是
`hass.data[DOMAIN]["yaml_config"]`(只有 `async_setup` 会写),所以
**YAML 改动必须重启 Corereload config entry 不重读 YAML。**
- **`stats_io_interval: 300`2026-09-13 添加**,备份
`/homeassistant/scribe.yaml.bak-20260913-191558`)。4.4.0 不再让 HA 每 30s
轮询 I/O 统计传感器,改由集成自己每 60s 发布,间隔成为配置项。scribe 自己的
传感器此前是本机自写历史的主要来源(变更前 24h:11 019 / 87 461 行状态 =
12.6%),60s → 300s 把这部分降约 5 倍(每个 I/O 传感器约 1440 → 288 行/天)。
验证:`ha core check` OK;重启 88s`ScribeWriter started successfully`
无 scribe error/warningscribe Repairs 问题 0 条;传感器发布间隔实测正好
300s11:19:26 → 11:24:26 UTC)。
- **Retention 现在可用但刻意不设**:`retention_states` / `retention_events`
4.0.0)按间隔丢 chunk,留空 = 永久保留,符合本机定位(Scribe 是永久归档,
recorder 保留 365 天)。注意 retention 是**绕过** entry `data` 副本读取的
`from_entry_data=False`),所以删掉 YAML 行即撤销策略。`db_schema`
`enable_rollups``scribe.purge` 同样未用:图表走 `sensor_minute` +
`timescale_database_reader`(见 [hosts/pgdb.md](pgdb.md)),不吃 scribe 自己的
视图,配置里也没有任何 `scribe.query` 调用。`flush_interval` 仍是 entry
`data` 钉住的 5s——上游下一个版本把默认改成 30s,但 entry 值优先,要采用只能
在 YAML 显式写 `flush_interval: 30`
- 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` (15 `csg_*` template sensors, top-level **list**)
+ `quick_sensors.yaml` (`sensor.quick_wen_cha`, same top-level list).
**`!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 `.bak` / `.pre-*` backups in the
dir are ignored. UI helpers such as `binary_sensor.csg_summer_tariff` are not
in this directory. After adding sensors, count template-platform entities
against these files plus the UI helpers, not against the old "12".
- 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 that day (later 15 `csg_*` + `quick_wen_cha`),
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-09-23
`http://hass.windy.lan:8123/dashboard-quick`。视图 `type: sections``max_columns: 4`
分区宽用 `column_span`(缺省是 1 列,不要省)。卡片宽用 `grid_options.columns`
(写在卡片上的 `column_span` 会被忽略)。空调实体是 `kong_diao_*``kong_tiao` 是笔误,应为 0 处。
| 分区 | span | 现在有什么 |
|---|---|---|
| 灯与开关 | 4 | 2 列网格:卫生间灯、厨房灯、客厅长明灯、厨房远灯、Matter 开关「马桶换气电源」(`switch.wei_sheng_jian_ma_tong_huan_qi_dian_yuan`)。不放 `switch.0xa4c138a4c2fe4c8b`。徽章「换气功率」= `sensor.0xa4c138a4c2fe4c8b_power` |
| 实时功率 | 2 | apexcharts donut11 路当前功率(与下面两张功率图同一批)。末项「马桶换气」= 上面那颗 Zigbee 功率。图例在右,`chart.height` 510card-mod 把 `ha-card` 撑到 520px。徽章「插座合计」= `sensor.dang_qian_zong_gong_lu` |
| 人体感应 | 2 | 厨房 / 卫生间 / 厨房(电池)。`state_map` 011`downsample_method: last`。卡高 280card-mod 520px,和圆环并排。默认 12h |
| 峰值负载 | 2 | 厨房电器、空调、电水壶、咖啡机、电脑。`downsample_method: avg`,高 280,默认 6h。徽章:空调、电脑。不要把约 25 W 的马桶换气放这里 |
| 基础负载 | 2 | 主网络、冰箱、大房、水设备、无线AP、马桶换气(`sensor.0xa4c138a4c2fe4c8b_power`)。卡 `fill: tozeroy`;主网络和冰箱有 `fill_color`,其余 4 条 `fill: false`。同样 `avg`、高 280、默认 6h |
| 今日用电 | 4 | `energy_mode`,11 路 kWh。末项「马桶换气」= `sensor.0xa4c138a4c2fe4c8b_energy``total_increasing`)。高 320,默认 today |
| 温湿度 | 4 | `sensor.quick_wen_cha`(室内均温 − 室外)+ 湿度 4 条。两张卡 card-mod 520px,默认 24h。徽章:卫生间 / 客厅 / 卧室 / 室外温度 |
功率图档 `1h,3h,6h,12h,24h,3d,7d`。人体感应档 `3h,6h,12h,24h,3d,7d`。温湿度档 `6h,12h,24h,48h,7d,14d,30d`。今日用电档 `today,week,month,3mo,custom``sensor_minute` 最早只到 **2026-08-29**,长于约 15 天的档前半段是空的。
「插座合计」是 min_max **sum**entry `01M16N648QBFG13P07XQMESBSP``round_digits: 0`),含上面 11 路功率,不含灯。任一源掉线则 unknown。2026-09-23 读回 380 W,与 11 路之和一致。
**别按旧说明操作:**
- 功率图必须 `downsample_method: avg`。reader 对 `sensor_minute` 默认 `last`(桶末瞬时值),空调会画成停机。今日用电用累计表的 `last` 再 diff,不要改成 avg。人体感应保持 `last`
- 卡 JS 曾把 `7d` 解析成 7 小时。2026-09-21 已改用 `parseDurationToMs`,资源 URL 带 `?v=20260921`。换 JS 后要再 bump `?v=`。命名档只有 `today|week|month|3mo|6mo|year|years|custom`
- apexcharts 的 `chart.height` 含图例,timescale 的 `height` 只是绘图区。并排等高用 card-mod 的 `ha-card`,不要把两个数字写成一样。
- 保存后圆环可能缩在角落。配置没变,拖一下窗口让它重算。
- 无线 AP 曾有单点 976 W,会把基础负载的 y 轴顶飞。未查。
- `/energy` 的马桶换气仍是 Matter 积分 `sensor.wei_sheng_jian_ma_tong_huan_qi_dian_yuan_energy`,不是这颗 Zigbee 电量。能源面板也没有主网络、无线 AP。
**改法:** WS `lovelace/config` 读、`lovelace/config/save` 写。勿改 `.storage/`
用 [`scripts/ha-ws-client.py`](../scripts/ha-ws-client.py)supervisor 镜像里的 python3 + `SUPERVISOR_TOKEN`,不要 `-v /tmp`)。
min_max 在 HA 2026.9 没有 WS `config_entries/options/flow`,走 supervisor 的
`POST /core/api/config/config_entries/options/flow``scribe.yaml` 已是 `record_states: true`,新传感器不用改它。
`sensor.quick_wen_cha``templates/quick_sensors.yaml``template.reload` 即可,不用重启 Core。
**回滚:** 计量改动前
`.lovelace-backups/dashboard-quick-20260923-080213-pre-meter-plug.json`
SHA-256 `a30b200350923633ddde0f2f071f2b75f0f37b347da4dd9a688da295dd8c8f35`)。
布局和卡 JS`dashboard-quick-20260921-pre-titles.json` +
`timescale-plotly-card.js.bak-20260921-pre-dayfix`
还原 JSON 用 WS save。改过卡 JS 要把资源 `?v=` 再 bump。
### 地图仪表盘: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=<CARTO_KEY>`
(配 `tile_layer_options: {subdomains: abcd, maxZoom: 20}` + OSM/CARTO
attribution)。实体不变:2 person + 4 zonezone 用 `display: icon` +
`circle: auto`circle 读实体 `radius` 属性画半径圈)。
- **CARTO key 是 secret**: 只存在于服务端 lovelace 存储(dashboard `map`
的卡片配置)和用户本人处;勿写入本仓库或 Linear。
- **文件/资源:** `/homeassistant/www/community/ha-map-card/map-card.js`
root:root 644678554 Bsha256
`f30dfb606e858d2216d5198d8cf758ce956d127006ebd7d66d4329153a247ec2`);
Lovelace resourcestorageid `9d2b50b52c60420d89ebd041f722cf60`
`/hacsfiles/ha-map-card/map-card.js`type moduleWS
`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/javascriptWS 读回卡片配置(type/entities/key/
attribution/options)全部符合;HA 主机 `curl -4` 带 key 瓦片 200。
- **Follow-up:** Core 现为 2026.9.32026-09-23)。内置地图是否已切 OSMF
矢量瓦片没有复查;本仪表盘仍用 keyed CARTO 的 `custom:map-card`
## 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)).
## 马桶换气电源(Matter 插座,半计量)+ 电量估算 (2026-09-13)
Quick 从 2026-09-23 起不再用这颗插座的功率或积分电量,见上方 Quick 节。
下面的积分实体仍是 `/energy` 的第 9 个 grid 源(当日读回)。Matter 开关仍在「灯与开关」。
**设备**Matter `Smart Plug`SIXWGH`model_id 3596`hw 1.0 / sw 1.3.0),node 18
(0x12)`device_id 5ef1850953466d6e7a9c6b901fbebe1c`config entry
`01JF51VQ48PGJGXX3RNAG6MVAA`,区域**卫生间** (`wei_sheng_jian`)label `power`
2026-09-13 17:58 CST 配对。实体:
`switch.wei_sheng_jian_ma_tong_huan_qi_dian_yuan`(插座)、
`sensor.…_dian_yuan`(电源 W)、`sensor.…_dian_ya`(电压 V)、
`sensor.…_you_gong_dian_liu`(有功电流 A)、`sensor.…_dian_li`(电力 kWh
**永久 unknown**)。
**根因(实测 Matter 属性,node 18**:电量簇 0x0091 `FeatureMap = 13`
(IMPE|CUME|PERE,即**声明**支持导入/累计/周期电量),但
`CumulativeEnergyImported (0x0001)` 恒为 `null``PeriodicEnergyImported
(0x0003)` 带载也恒为 `{Energy: 0}``CumulativeEnergyExported (0x0002)`
不存在(EXPE 未声明,自洽)。HA 只用 `CumulativeEnergyImported` 建能量实体
`components/matter/sensor.py:1083``allow_none_value=True`)→ 该实体
**永远不会出数**。**功率计量本身正常**:0x0090 `FeatureMap = 2` (ALTC)
Voltage / ActiveCurrent / ActivePower 都随负载变化(实测 220.3 V / 118 mA /
24.7 WHA `电源` 0.0→24.9 W 有历史)。厂商 `update` 实体报无新固件。
**处理(方案 A:功率积分补电量)**
- 新建 **Integration (Riemann sum) 辅助元素**config entry
`01M2D53T188FW8WEC547ENHSVH`domain `integration`state `loaded`),
source `sensor.wei_sheng_jian_ma_tong_huan_qi_dian_yuan_dian_yuan`
`method: trapezoidal``unit_prefix: k``unit_time: h``round: 3`
`max_sub_interval: 60s`
- 实体 `sensor.wei_sheng_jian_ma_tong_huan_qi_dian_yuan_energy`(创建时 HA
自动生成 `…_dian_yuan_ma_tong_huan_qi_dian_yuan_dian_liang`,随后立即
`config/entity_registry/update` 改名为 `<插座>_energy` 以对齐约定;
该实体新建、无引用,改名安全),friendly name「马桶换气电源 电力」,
unit kWh、`device_class: energy`、**`state_class: total`**——能源仪表盘
允许 `TOTAL``TOTAL_INCREASING``components/energy/validate.py:279`)。
- **能源仪表盘** (`/energy`)grid 源 `[8]``…_dian_li` 改为 `…_energy`
其余 8 条插座源未动。注意这 9 条「插座」全部以 `type: grid` 注册,被当作
全屋用电代理;`switch` 卡片所在的 Grid 卡片此前第 9 行是空的,即本次修复点。
- **Quick(已过时):** 2026-09-13 曾把用电量图指到 `…_energy`,并单独加过开关分区。
现行 Quick 见上方。不要按本段把 Quick 改回去。
**口径警告**`…_energy` 是**估算值**Riemann 积分,只在 HA 运行期间累计、
非账单级),与另外 8 个原生计量插座的累计电量口径不同;功率传感器更新
间隔约 510 s(实测 24.9/24.8/25.0 W 抖动),加 `max_sub_interval: 60s`
保证静默时也继续累计。
**Agent 侧建辅助元素的方法(2026-09-13 实测)**HA 的 config flow 走
**REST**WS 只有 `config_entries/flow/progress|subscribe`,没有 start)。
经 supervisor 代理即可,无需 HA 长连接/长寿命 token:
```bash
# SUPERVISOR_TOKEN 由 sudo -n -i 提供
curl -s -X POST -H "Authorization: Bearer $SUPERVISOR_TOKEN" \
-H "Content-Type: application/json" -d '{"handler":"integration"}' \
http://supervisor/core/api/config/config_entries/flow # → {flow_id, step_id:"user", data_schema}
curl -s -X POST -H "Authorization: Bearer $SUPERVISOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"…","source":"sensor.x","method":"trapezoidal","round":3,
"unit_prefix":"k","unit_time":"h","max_sub_interval":{"minutes":1}}' \
http://supervisor/core/api/config/config_entries/flow/<flow_id> # → create_entry
```
`auth/long_lived_access_token` 在 supervisor 代理身份下**失败**
`unknown_error`),故无法用长寿命 token 开浏览器会话;`DurationSelector`
的值是 `{"minutes":1}` 形式(`cv.time_period`)。
**备份/回滚**`.lovelace-backups/dashboard-quick-20260913-181251-pre-ma-tong-plug.json`
(改动前原件)、`…-20260913-183210-pre-repoint.json`(改名/换源前);
`.ha-backups/energy-20260913-183135-pre-ma-tong-repoint.json`(能源 prefs)。
回滚能源估算 = 把能源 prefs 的源 [8] 指回 `…_dian_li`
不要用 2026-09-13 的 Quick JSON 覆盖现在的面板,那会撤掉后来的布局和 Zigbee 计量。
彻底放弃估算电量 = 删除 config entry `01M2D53T188FW8WEC547ENHSVH`
**验证 (2026-09-13 18:3x)**`…_energy` 0.002→0.003 kWh 且随 24.6 W 负载
增长(换气扇关掉后回落 0.0 W,累计值保留);`recorder/list_statistic_ids`
已含该实体;Quick 面板 WS 读回 7 分区、用电量图 11 项指向新实体、旧
`_dian_li` 引用 0 处;能源 prefs 读回 9 源、第 9 条为新实体(Quick 的这次读回只对 2026-09-13 有效;2026-09-23 起 Quick 改指 Zigbee`/energy` 第 9 源仍是该积分实体)。
**`energy/validate` 已全绿**9 源 0 issue):创建后 ~5 min 内曾报
`statistics_not_defined`(recorder 的统计任务周期是 5 min,`statistics_meta`
行由该任务建立),18:39 复核时已自动消失——建辅助元素后**不要**把这条
瞬时告警当作失败。
## 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