854 lines
54 KiB
Markdown
854 lines
54 KiB
Markdown
[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 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
|
||
|
||
**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-04(VPS-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-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 自动回落堆叠。
|
||
外部实施审核收口时补齐预测不确定性标识:两张 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 bytes,SHA-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-21(1008 从未成功的根因与回填)与 **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-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 天)是回测选出来的单月参数,属弱证据,换季/换季后值得复看。
|
||
|
||
> **季节 gauge:** 不要手改 max/segments。`binary_sensor.csg_summer_tariff`
|
||
> (`on` = 5–10 月)切换夏季 `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 改动必须重启 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`。
|
||
- 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 donut,11 路当前功率(与下面两张功率图同一批)。末项「马桶换气」= 上面那颗 Zigbee 功率。图例在右,`chart.height` 510;card-mod 把 `ha-card` 撑到 520px。徽章「插座合计」= `sensor.dang_qian_zong_gong_lu` |
|
||
| 人体感应 | 2 | 厨房 / 卫生间 / 厨房(电池)。`state_map` 0–11,`downsample_method: last`。卡高 280,card-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 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.3(2026-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 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(已过时):** 2026-09-13 曾把用电量图指到 `…_energy`,并单独加过开关分区。
|
||
现行 Quick 见上方。不要按本段把 Quick 改回去。
|
||
|
||
**口径警告**:`…_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`。
|
||
不要用 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
|