2026-08-14 22:31:31 +08:00
[hosts/hass.windy.lan.md#8DF6 ]
2026-08-13 14:10:39 +08:00
# 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` |
2026-08-18 13:29:25 +08:00
| **Host** | **x88 Pro physical box** (HAOS bare-metal, `machine: green` ; verified 2026-08-18) |
2026-08-13 14:10:39 +08:00
| 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 17:54:33 +08:00
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.
2026-08-13 14:10:39 +08:00
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 |
|---|---|
2026-08-22 10:14:29 +08:00
| `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) |
2026-08-14 16:11:03 +08:00
| `wlan0` | Supervisor **disabled** (verified 2026-08-14, W1N-104); IPv6 remains off on this RTL8821CS radio |
2026-08-13 14:10:39 +08:00
| `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 ).
2026-08-13 17:20:49 +08:00
## 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.
2026-08-13 14:10:39 +08:00
## Safe verification
```bash
2026-08-22 10:14:29 +08:00
ssh -o BatchMode = yes hassio@hass.windy.lan 'hostname; ip -4 addr show end0'
2026-08-13 14:10:39 +08:00
```
From a LAN client, confirm DNS and UI reachability:
```bash
getent hosts hass.windy.lan
# expect 192.168.55.11
```
2026-08-13 19:48:18 +08:00
## Local patches (custom components)
2026-08-16 20:34:38 +08:00
2026-08-14 22:31:31 +08:00
### 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 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/<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
2026-08-18 13:29:25 +08:00
**v1.3.2** (`934f58c` , verified 2026-08-15, W1N-118): manual zipball of
2026-08-14 22:31:31 +08:00
GitHub release
2026-08-18 13:29:25 +08:00
[v1.3.2 ](https://github.com/windyboy/china_southern_power_grid_stat/releases/tag/v1.3.2 )
2026-08-14 22:31:31 +08:00
copied to `/config/custom_components/china_southern_power_grid_stat` .
2026-08-18 13:29:25 +08:00
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).
2026-08-14 22:31:31 +08:00
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.
2026-08-14 15:47:08 +08:00
2026-08-29 21:02:33 +08:00
** `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` )
2026-08-29 21:08:13 +08:00
updated 2026-08-29 (W1N-240 + W1N-242):** 「本月累计」gauge 对齐夏季阶梯:
2026-08-29 21:02:33 +08:00
`max:650` 、segments `0/260/600` (绿/橙/红 = 一/二/三档;冬季 11-01 需切
`max:450` 、`0/200/400` — **seasonal switch point** ,见下文)。「📊 统计
数据」卡新增本年/去年 4 行(原生传感器,口径标注「电费(账单)」、本年
2026-08-29 21:08:13 +08:00
「(至今)」)+ 本月日均/预测进度 2 行(`csg_this_month_daily_avg` /
`csg_prediction_progress` , W1N-242);面板共引用 **20** 个实体。改前备份:
2026-08-29 21:02:33 +08:00
`/homeassistant/.lovelace-backups/dashboard-unknown-power-monitor-20260829-204845.json`
2026-08-29 21:08:13 +08:00
( W1N-240)、`-20260829-210708.json` ( W1N-242)
2026-08-29 21:02:33 +08:00
(改法:WS `lovelace/config/save` ,参数 `url_path: dashboard-unknown` +
2026-08-29 21:08:13 +08:00
`config` ;勿直改 `.storage/` )。验证:WS 读回 18→20 实体 diff ✓、gauge
2026-08-29 21:02:33 +08:00
配置一致 ✓、URL `http://hass.windy.lan:8123/dashboard-unknown/power-monitor` 。
2026-08-29 21:08:13 +08:00
** `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)。
2026-09-04 21:44:43 +08:00
**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 错误。
2026-09-22 08:33:06 +08:00
**CSG 面板 Sections 重构 2026-09-22( VPS-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 自动回落堆叠。
2026-09-22 08:42:21 +08:00
外部实施审核收口时补齐预测不确定性标识:两张 Tile 分别命名为
「预计本月用电(估算)」/「预计本月电费(估算)」,且「本月概览」副标题明确
写明「预测按当前速率推算,月内会调整」;仅改展示文案,不改计算或实体。
2026-09-22 08:33:06 +08:00
季节 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 bytes, SHA-256
`f5e3e05ab29a4c13b870547b0a189e95b793d85e82c85c61190dedf66811a874` )。
首轮 v4 到布局/单位修正前的第二快照:
`/homeassistant/.lovelace-backups/dashboard-unknown-power-monitor-20260922-0825-pre-layout-unit-fix.json`
( SHA-256 `8b56e1303572405d7c850305ffd435cc5c7ca583330160fdd3f8b213473e1fcd` )。
2026-09-22 08:42:21 +08:00
外部审核文案修正前的第三快照:
`/homeassistant/.lovelace-backups/dashboard-unknown-power-monitor-20260922-0838-pre-estimate-copy-fix.json`
(文件 SHA-256 `b1c1e0c6946818ce7fb44d5d731e3e78aebe9c8d51ed465ce8505a0f1855c05c` )。
2026-09-22 08:33:06 +08:00
验证:`lovelace/config/save` 后规范化 JSON 哈希与 payload 一致;22 个引用实体全部
存在且无 unknown/unavailable;夏季 helper=`on` ;图表 30 条为 2026-08-22 至
2026-09-22 08:42:21 +08:00
2026-09-20,重算均值 9.15 kWh 与 `sensor.csg_30d_avg` 一致;最终文案修正后的
规范化配置哈希为
`c16d11c01353f90761da5837b06cc7ad1ea7b400f249c99cf291c03f4980a28d` 。浏览器没有 HA
2026-09-22 08:33:06 +08:00
登录态,390/768/桌面三宽度视觉验收仍待人工登录后完成。回滚时先把上述 JSON
经 WS `lovelace/config/save` 写回;再扫描 helper 引用,确认无消费者后才可删除
配置入口,勿直改 `.storage/` 。
2026-09-21 19:57:35 +08:00
**CSG 长期归档(W1N-243, 2026-08-29;任务改号 2026-09-21) :** scribe 库新增
`csg_history` 表(逐日 usage/cost/ladder/balance + 逐月累计;2026-07-01 起回填,
永久),由 TimescaleDB 每日任务 **1010** (原 1008, 2026-09-21 重建)
`csg_daily_snapshot()` ( **14:30 UTC = 22:30 Asia/Shanghai**; **TS job 非 pg_cron**)
upsert 维护;计费用 `csg_ladder_cost(kwh, month)` 。日行 usage 取集成
`yesterday_kwh` ,日费用在原生 `latest_day_cost` 缺失时回退 = 昨日用电 ×
当前档费率(模板 `csg_current_ladder_tariff` );月行由阶梯助手重算。**语义**:
day 行 usage/cost 为该日值,ladder/balance 为快照值。详见
[hosts/pgdb.md ](../hosts/pgdb.md ) § Known issues 2026-09-21(含原 1008 从未成功
的根因与回填)。
**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-19, 19 天,和 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` 预测速率 v2( 2026-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 元 |
| v2: K=7 | 14.6% | 16.81 元 | 5.65 元 |
| **v2: K=14(采用)** | **13.9%** | **15.33 元** | **4.94 元** |
| v2: K=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` OK; Core 重启两次
( 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 天)是回测选出来的单月参数,属弱证据,换季/换季后值得复看。
2026-08-30 13:19:05 +08:00
2026-08-29 21:02:33 +08:00
> **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`)。
2026-09-04 21:44:43 +08:00
> **提醒 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 无法
> 模板化,仍需人工改卡配置。
2026-08-29 21:02:33 +08:00
2026-08-14 16:11:03 +08:00
Home PPPoE IPv4 to CSG is still blackholed (`curl -4` to `218.19.148.218:443`
2026-08-22 10:14:29 +08:00
times out). `end0` IPv6 is enabled (`ipv6.method: auto` ); from HA,
2026-08-14 16:11:03 +08:00
`curl -6 https://95598.csg.cn` returns HTTP 200 via `240e:f9:8060::1:16` .
2026-08-14 15:47:08 +08:00
2026-08-13 19:48:18 +08:00
** `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` ).
2026-08-16 20:34:38 +08:00
2026-08-21 08:56:26 +08:00
### `matrix_e2ee` live tree (E2E Matrix bot, verified 2026-08-20)
2026-08-16 20:34:38 +08:00
2026-08-21 08:56:26 +08:00
**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` ).
2026-08-18 13:29:25 +08:00
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 ).
2026-08-16 20:34:38 +08:00
2026-08-21 08:56:26 +08:00
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` ).
2026-08-18 13:29:25 +08:00
- Bot `@hass:chans.xyz` reused (E2EE device `rO1R915ncu` ). Config Entry
`01M04D7C1M4T2GX5VPG7NVQ7GV` (`source: import` , `state: loaded` ). All
settings via **Settings → Devices & Services → Matrix E2EE → Configure** .
2026-08-16 20:34:38 +08:00
- Config Entry options: `allowed_rooms` `["!gidvAzpDzwtzfEDrqu:chans.xyz", "!boxfylDSzOvrWkcsyY:chans.xyz"]` ,
`allowed_users` `["@zhiqiang:chans.xyz"]` , `command_prefix` `"!"` .
2026-08-21 08:56:26 +08:00
** `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.
2026-08-18 13:29:25 +08:00
- Storage: `/config/.storage/matrix_e2ee_session.json` +
`/config/.storage/matrix_e2ee_store/` . Backups:
2026-08-21 08:56:26 +08:00
`/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` ,
2026-08-18 13:29:25 +08:00
`matrix_e2ee.bak-20260816-v0.2.9` , `matrix_e2ee.bak-20260816-v0.2.8` );
2026-08-16 20:34:38 +08:00
full HA backup slugs `3d9d36db` (pre-v0.1.4) + `9f223f35` (pre-v0.2.0).
2026-08-21 08:56:26 +08:00
- 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 ).
2026-08-18 13:29:25 +08:00
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` ).
2026-08-16 20:34:38 +08:00
- **SAS not yet completed:** every device requires explicit `confirm_verification` .
Encrypted-room commands stay fail-closed until `@zhiqiang` 's device is verified.
2026-08-21 08:56:26 +08:00
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
2026-08-16 20:34:38 +08:00
[docs/home-assistant-matrix.md § Device verification ](../docs/home-assistant-matrix.md ).
2026-09-13 19:34:04 +08:00
### Scribe long-term history (3.8.0 setup 2026-08-29; 4.4.0 verified 2026-09-13)
2026-08-29 14:51:20 +08:00
2026-09-13 19:34:04 +08:00
- **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
2026-08-29 20:25:53 +08:00
`/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
2026-08-29 14:51:20 +08:00
`01KC2VFJWEQ3XDHY6TQKHPDVRB` , `source: import` — UI "Configure → Advanced"
2026-08-29 20:25:53 +08:00
edits are overridden by the YAML on restart; treat YAML as authoritative.
2026-08-29 14:51:20 +08:00
- 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.
2026-09-13 19:34:04 +08:00
- **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 改动必须重启 Core, reload 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/warning; scribe Repairs 问题 0 条;传感器发布间隔实测正好
300s( 11: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` 。
2026-08-30 13:19:05 +08:00
- 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.
2026-08-29 14:51:20 +08:00
2026-08-29 20:25:53 +08:00
### 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.
2026-08-29 15:54:40 +08:00
### 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.
2026-08-29 19:54:35 +08:00
- **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: <n|"full">}` ( `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` )。
2026-08-29 15:54:40 +08:00
2026-09-13 19:09:54 +08:00
- **Quick 时间范围扩容 (2026-09-13, VPS-92)**: 用户反馈「48 小时不够」。
各 timescale 卡可选档上调——大功率电器/常驻负载 `…,24h` → `+3d,7d` ;
环境 `6h,12h,24h,48h` → `+7d,14d,30d` ;人体感应 `…,24h` → `+3d,7d` ;
用电量(按插座) `energy_time_ranges` `today,week,month,custom` → `+3mo` 。
2026-09-21 19:57:35 +08:00
**默认档未改** ( 6h / 6h / today / 24h / 12h)。备份
2026-09-13 19:09:54 +08:00
`.lovelace-backups/dashboard-quick-20260913-190912-pre-timerange.json` 。
2026-09-21 19:57:35 +08:00
⚠️ **本条的 `d` 档当天其实是坏的** ——见下方 2026-09-21 修复;`energy_mode`
命名档(today/week/month/3mo)当时就是好的。卡片命名档只能是
`today|week|month|3mo|6mo|year|years|custom` 。**数据下界注意**: scribe
`sensor_minute` 最早只到 **2026-08-29** ,所以 >15d 的档(14d 边缘、30d 明显)
前半段仍是空白,等归档继续累积才好看。
- **Quick 面板改造 + timescale 卡两个真 bug 修复 (2026-09-21)**:起因是用户
觉得「面板标题 + 统计方式」不对劲。只读审查查出三个问题并全部修掉:
** ① 卡 JS: `<n>d` 档实际渲染成 n 小时(VPS-92 的扩容其实没生效)**。
非 energy 档走 `const hours = parseInt(this._selectedRange) || 24;` ,
`parseInt("7d") === 7` 。全卡唯一能解析 `d` 的 `parseDurationToMs()`
(正则 `/^(\d+)(m|h|d)$/` ) **只被 `resolveEnergyWindow()` 调用一次**,
即只服务 energy 卡。后果:`3d` 比 `24h` 还短(3h),`14d` →14h、`30d` →30h。
**修复** :改用 `Math.max(1, parseDurationToMs(...)/3600000)` ;顺带把采样密度
目标 80 → 160 点(7d 档从 2.1h/桶降到 1.05h/桶)。备份
`.lovelace-backups/timescale-plotly-card.js.bak-20260921-pre-dayfix` 。
实测(本地 node 跑卡里的 `parseDurationToMs` ):`3d→72h / 7d→168h /
14d→336h / 30d→720h` ,各档均 ~160 点;`node --check` JS 语法 OK。
**资源 URL 已加 `?v=20260921` 强制浏览器重取** (原来无 hacstag,缓存问题会
让修复「看不见」)。
** ② 功率卡画的是瞬时采样而非统计量**。reader 对 `table=sensor_minute`
(时间列名 `minute` )默认 `downsample_method = "last"`
( `custom_components/timescale_database_reader/__init__.py` L385-389),
即每桶取「末一分钟的瞬时值」。实测空调 1 小时桶:03:00 真实均值 241.6 W /
峰值 1128 W,图上只画 157; 19:00 真实均值 97.6 / 峰值 676,图上画 **13**
(看起来像停机)。因 `sensor_minute` 已是均匀 1 分钟 LOCF 序列,`avg()`
才是正确时间均值。**修复**:两张功率卡加 `downsample_method: "avg"` 。
**用电量卡不用改** ——累计计数器配 `last()` 就是桶末读数,卡片再 diff,
本来就是对的;人体感应卡显式 `last` 对分类信号也对。
** ③ 标题与分组名不副实**。按 7d 实测(峰值/均值)重分组并改名:
| 位置 | 改前 | 改后 |
|---|---|---|
| 分区 | 大功率电器 | **峰值负载** (厨房电器 1839 / 空调 1128 / 电水壶 957 / 咖啡机 882 / 电脑 363 W) |
| 分区 | 常驻负载 | **基础负载** (主网络 75 / 冰箱 137 / 大房 290 / 水设备 171 / 无线AP 976* W) |
| 分区 | 用电 | **今日用电** |
| 分区 | 灯 + 开关(分开在最顶和最底) | **灯与开关** (合并置顶,可操作项集中) |
| 分区(新) | — | **此刻耗电** |
| 徽标 | 总功率 | **插座合计** (只是 9 个插座之和,不含灯/换气) |
| 实体 | 迷你 / mini数显 / 数显 | **卫生间 / 客厅 / 卧室** (HA area 本来就有,之前用的是设备型号名) |
| 实体 | 电池(移动) | **厨房(电池)** (该设备 area 也是厨房) |
删除 3 张与分区标题重复的卡 title(用电量(按插座) / 室内外温湿度 / 活动状态)。
\* 无线AP 7 天里只有 2 分钟 >150 W 却记到 976 W —— 单点毛刺,会把 y 轴顶飞,
待查。
** ④ 新展示**:新增「此刻耗电」区 = `custom:apexcharts-card` **donut** , 11 个
插座当前功率(`graph_span: 10min` + `update_interval: 1min` ),回答「现在什么
在吃电」;环境区拆成**室内外温差**(新模板传感器 `sensor.quick_wen_cha` =
3 个室内温度均值 − 室外,画零线上下 fill)+ **湿度** ( 4 条),4 个温度进徽标。
传感器加在 `templates/quick_sensors.yaml` (W1N-238 约定的落点,顶层列表,
`quick_` 前缀 + unique_id),非 trigger 型模板卡,**`template.reload` 即可生效,
无需重启 Core**(实测 `POST /core/api/services/template/reload` → 200,
`sensor.quick_wen_cha` = 3.9 °C)。备份
`templates/quick_sensors.yaml.bak-20260921-pre-wencha` 。
最终分区顺序(按「可变性」):灯与开关 → 此刻耗电 → 今日用电 →
峰值负载(2) → 基础负载(2) → 环境 → 人体感应。
** ⑤ Quick 布局重构 (2026-09-21 晚)**:用户反馈饼图(apexcharts donut)撑满
整行(span 4)且不满、马桶换气开关 tile `columns: "full"` 占满整行太宽、整体
布局需要重新思考。改动:
**布局重排** (阅读流:控制 → 此刻 → 趋势 → 今日 → 环境):
| 行 | 左 (span 2) | 右 (span 2) | 主题 |
|---|---|---|---|
| 1 | 灯与开关 (span 4) | | 可操作项置顶 |
| 2 | 实时功率 (donut) | 人体感应 | 「此刻状态」 |
| 3 | 峰值负载 | 基础负载 | 「功率趋势」(原配对保留) |
| 4 | 今日用电 (span 4) | | 11 条堆叠柱需要宽度 |
| 5 | 温湿度 (span 4) | | 温差+湿度两图并排 |
**标题改名** :此刻耗电→**实时功率**(与峰值负载对仗)、环境→**温湿度**
(更具体)。删饼图卡内冗余标题 "各插座当前功率 (W)"。
**开关收窄** :马桶换气电源 tile 从独立 `columns: "full"` ( 48 列全宽)→ 移入
灯的 2 列嵌套网格第 5 格,与灯卡等宽(半行)。
**饼图高度** : `apex_config.chart.height` 280→350( 11 项底部图例占 ~100px,
280 时饼图本体太扁)。
** ⑥ card-mod 等高 (2026-09-21 晚)**:不同卡类型(apexcharts vs timescale-plotly)
的 `height` 渲染比例不同(apexcharts 含图例/内边距,timescale 是纯绘图区),
直接对齐数字无法视觉等高。安装 **card-mod** ( HACS → card-mod + card-mod-studio)
后用 CSS 强制 `ha-card` 高度:
```yaml
card_mod:
style: "ha-card { height: 520px !important; }"
` ``
**最终高度**:饼图+人体感应 = **520px**,温湿度双图 = **520px**。其余图表
(峰值/基础负载 280、今日用电 320)保持各自原生高度,不强制统一——不同图表
类型适合不同高度,强行统一反而让简单图表显得太空。
**关键认知**: apexcharts ` chart.height` 包含图例区域,timescale-plotly ` height`
是纯绘图区。同一数字渲染出来 apexcharts 视觉上更矮。card-mod 直接控制
` ha-card` 容器高度,绕过内部渲染差异。
备份:` .lovelace-backups/dashboard-quick-20260921-pre-titles.json`(同日早期
备份,覆盖标题+布局全部改动的回滚)。
**验证**: ` ha core check` OK; WS 读回与写入 payload **逐字节相同**;
新配置引用的 **40 个实体全部存在**且无 unknown/unavailable;
卡 JS ` node --check` 通过、时长解析实测正确。
**改法(可复用)**:写 Lovelace 配置必须走 WS
` lovelace/config/save`( **勿直改 ` .storage/`**)。本机 python/HA 容器内经
supervisor 代理连 WS 会被 loop-prevention 拒;可行路径是
` docker run --rm -i --network host -e SUPERVISOR_TOKEN --entrypoint python3
r.hassbus.com/home-assistant/aarch64-hassio-supervisor:<ver> -c "..."`
跑本仓库的 [` scripts/ha-ws-client.py`](../scripts/ha-ws-client.py)(纯 stdlib,
不依赖 websockets/aiohttp,也不依赖 ` -v` 挂载——` -v /tmp/...` 会落到 Docker 宿主
而不是 SSH add-on 的 ` /tmp`)。读配置:` {"action":"get","url_path":"dashboard-quick"}`;
写:` {"action":"save",...,"config":{...}}`。备份
` .lovelace-backups/dashboard-quick-20260921-pre-titles.json`。
**回滚**:还原 ` dashboard-quick-20260921-pre-titles.json`( WS save) +
` timescale-plotly-card.js.bak-20260921-pre-dayfix` + 删 ` quick_wen_cha`
(还原 ` quick_sensors.yaml.bak-20260921-pre-wencha` + ` template.reload`)。
改了卡 JS 就要把资源 URL 的 ` ?v=` 再 bump 一次。
2026-09-13 19:09:54 +08:00
2026-08-30 13:39:42 +08:00
### 地图仪表盘: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 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 前无解。
2026-08-13 19:48:18 +08:00
## 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.
2026-08-21 08:56:26 +08:00
## 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.
2026-08-23 11:06:35 +08:00
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)).
2026-09-13 18:39:22 +08:00
## 马桶换气电源(Matter 插座,半计量)+ 电量估算 (2026-09-13)
**设备**: 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 W, HA ` 电源` 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 仪表盘**:「用电量(按插座)」图第 11 项由 ` …_dian_li` 改为
` …_energy`;新增 ` column_span: 2` 的「开关」区块(heading + tile
` switch.…` + ` toggle` feature + 功率徽标)→ 视图 6→7 分区。
**口径警告**: ` …_energy` 是**估算值**( Riemann 积分,只在 HA 运行期间累计、
非账单级),与另外 8 个原生计量插座的累计电量口径不同;功率传感器更新
间隔约 5– 10 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` + 还原 Quick 面板 JSON;
如需彻底放弃估算电量 = 删除 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 条为新实体。
**` energy/validate` 已全绿**( 9 源 0 issue):创建后 ~5 min 内曾报
` statistics_not_defined`( recorder 的统计任务周期是 5 min, ` statistics_meta`
行由该任务建立),18:39 复核时已自动消失——建辅助元素后**不要**把这条
瞬时告警当作失败。
2026-08-13 14:10:39 +08:00
## Related docs
2026-08-14 22:31:31 +08:00
- [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
2026-08-13 14:10:39 +08:00
- [docs/lan-overview.md](../docs/lan-overview.md) — LAN map and gw port-forward
2026-09-22 08:33:06 +08:00
- [hosts/dns.windy.lan.md](dns.windy.lan.md) — ` hass.windy.lan` / ` hass.local` rewrites