Files
vps/hosts/hass.windy.lan.md
T

59 KiB
Raw Blame History

[hosts/hass.windy.lan.md#8DF6]

hass.windy.lan — Home Assistant (HAOS)

Role and access

Item Value
Role Home Assistant automation hub
IPv4 192.168.55.11 (LAN55)
DNS hass.windy.lan (AdGuard rewrite on dns.windy.lan; legacy hass.local alias)
SSH ssh hassio@hass.windy.lan
Host x88 Pro physical box (HAOS bare-metal, machine: green; verified 2026-08-18)
Platform Home Assistant OS; kernel 6.1.115-haos (aarch64)
Web UI http://hass.windy.lan:8123 (LAN); WAN port-forward hass on gw → :8123

Use hassio for routine SSH inspection. Key-only login was verified on 2026-08-13 from the WSL client (BatchMode=yes).

The ha supervisor CLI (/usr/bin/ha) authenticates with SUPERVISOR_TOKEN. Interactive login works because ~hassio/.zprofile runs exec sudo -i, which loads a root environment carrying the supervisor API token. Non-interactive ssh hassio 'command' does not source .zprofile and fails with unauthorized: missing or invalid API token. Run ha non-interactively via:

ssh -o BatchMode=yes hassio@hass.windy.lan 'sudo -n -i ha core info'

Verified 2026-08-13 that sudo -n -i ha core info works from the WSL client. Never copy the supervisor token into this repository.

The current SSH ED25519 host-key fingerprint is SHA256:DMcMOgDzFsFTon1fndXowEP7jlyOK3/AX3PVK8BATvk (verified 2026-08-13). Verify a changed key out of band before accepting it.

Do not store Home Assistant long-lived tokens, integration credentials, or recovery codes in this repository.

Network

Interface Address / role
end0 IPv4 static 192.168.55.11/24 (gw .254, DNS 192.168.66.36); IPv6 SLAAC auto with GUA on the current PD-derived /64 (240e:3bd:235:1fb2:* at 2026-08-22; rotates on PPPoE redial); primary LAN55 NIC (interface name verified live 2026-08-22 — end1 does not exist)
wlan0 Supervisor disabled (verified 2026-08-14, W1N-104); IPv6 remains off on this RTL8821CS radio
wg0 10.13.13.2/32; WireGuard (add-on / integration tunnel)
hassio / docker0 internal HAOS Docker bridges (172.30.32.0/23, 172.30.232.0/23)

LAN55 clients reach the HTTP API on dns.windy.lan:80 for the AdGuard Home integration; see hosts/dns.windy.lan.md.

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.

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:

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

ssh -o BatchMode=yes hassio@hass.windy.lan 'hostname; ip -4 addr show end0'

From a LAN client, confirm DNS and UI reachability:

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.

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): HACS or 手动下载安装. 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:

TAG=v1.3.1
STAGE=/tmp/csg-${TAG}-deploy
mkdir -p "$STAGE"
gh api "repos/windyboy/china_southern_power_grid_stat/zipball/${TAG}" \
  > "$STAGE/src.zip"
unzip -q "$STAGE/src.zip" -d "$STAGE"
SRC=$(find "$STAGE" -type d -path '*/custom_components/china_southern_power_grid_stat' | head -1)
# expect .../custom_components/china_southern_power_grid_stat

ssh -o BatchMode=yes hassio@hass.windy.lan \
  'sudo -n -i mkdir -p /homeassistant/.csg-backups &&
   sudo -n -i cp -a /homeassistant/custom_components/china_southern_power_grid_stat \
     /homeassistant/.csg-backups/china_southern_power_grid_stat.bak-$(date +%Y%m%d)-manual'

rsync -a --delete \
  -e 'ssh -o BatchMode=yes' \
  "$SRC/" \
  hassio@hass.windy.lan:/homeassistant/custom_components/china_southern_power_grid_stat/

# --delete cannot remove Core-owned __pycache__; wipe as root, then restart
ssh -o BatchMode=yes hassio@hass.windy.lan \
  'sudo -n -i rm -rf /homeassistant/custom_components/china_southern_power_grid_stat/__pycache__ \
     /homeassistant/custom_components/china_southern_power_grid_stat/*/__pycache__ &&
   sudo -n -i ha core restart'

Wait until Core is up (ha core info returns, typically 12 min; this CLI build does not print a state: field). Then:

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.

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 copied to /config/custom_components/china_southern_power_grid_stat. Earlier trees: v1.3.1/55a293fc (W1N-107), v1.3.0/69f13c90 (W1N-106), a433e8c (W1N-105), de01914 (W1N-103), eb8b174 (W1N-102). Backups: /homeassistant/.csg-backups/ (w1n102/104/105/106/107/118).

v1.3.0 crashed the coordinator on first refresh (TypeError: cannot pickle 'mappingproxy' object in copy.deepcopy(self._config) under Python 3.14 / HA 2026.8.1). v1.3.1 wraps those deepcopy calls with dict(...). Post-restart 22:13 CST: entry loaded, no pickle traceback. Native this-month sensors filled after reloading entry 01KGCQDSZCF523A9X6SV3BZ1B9 (ip_family: ipv4). Native cost/ladder sensors can stay unknown because CSG get_month_daily_cost_detail returns a marketing-system SQL error; the dashboard uses template ladder/cost entities instead. Do not change templates/csg_sensors.yaml or the 电力监控 dashboard for an install.

templates/csg_sensors.yaml hardened 2026-08-29 (W1N-239): added availability templates to all 12 csg_* sensors (numeric sensors can't render unknown/unavailable in state; availability suppresses rendering instead — native CSG down ⇒ derived sensors show unavailable, no more fake zeros / "一档" / 0%). csg_yesterday_kwh now falls back to last_month_by_day's last entry when this_month_by_day is empty (month start); ladder constants (t1/t2/p1/p2/p3) deduped into per-block variables: (Block B + Block D); csg_mom_change parses date defensively. Backup: /homeassistant/.csg-backups/csg_sensors.yaml.bak-20260829-w1n239. Verified: ha core check OK; Core restart required (trigger-based template blocks don't settle on template.reload — W1N-114 precedent); post-restart all 12 entities numeric & consistent (302.47 kWh→180.28 元, 324.03 kWh→194.06 元, mom_change -3.6%, yesterday 7.66 kWh/2026-08-28), no template errors in Core logs.

csg_sensors.yaml off-by-one fixed 2026-08-29 (W1N-241): CSG data lags 1 day (sum(this_month_by_day) == this_month_total_usage, data stops at yesterday), but templates used now().day as "days elapsed" → csg_predicted_usage underestimated ~1 daily avg (~3%) and csg_mom_change compared this-month 28 days vs last-month 29 days (-3.6% vs true -0.3%). Both now derive the day number from this_month_by_day[-1].date (fallback now().day when empty). Added sensor.csg_this_month_daily_avg (month-to-date avg, 302.47/28=10.8) and sensor.csg_prediction_progress (usage/predicted %, 90.3) in Block C (trigger adds csg_predicted_usage). Backup: /homeassistant/.csg-backups/csg_sensors.yaml.bak-20260829-w1n241. Verified (8/29): predicted 324.03→334.81, mom_change -3.6→-0.3, daily_avg 10.8, progress 90.3, predicted_cost 194.06→200.94 (334.81 kWh ladder), ladder cost 180.28 unchanged, ha core check OK after restart, no template errors; 14 csg_* entities total.

电力监控面板(lovelace.dashboard_unknown / view power-monitor updated 2026-08-29 (W1N-240 + W1N-242): 「本月累计」gauge 对齐夏季阶梯: max:650、segments 0/260/600(绿/橙/红 = 一/二/三档;冬季 11-01 需切 max:4500/200/400seasonal switch point,见下文)。「📊 统计 数据」卡新增本年/去年 4 行(原生传感器,口径标注「电费(账单)」、本年 「(至今)」)+ 本月日均/预测进度 2 行(csg_this_month_daily_avg / csg_prediction_progressW1N-242);面板共引用 20 个实体。改前备份: /homeassistant/.lovelace-backups/dashboard-unknown-power-monitor-20260829-204845.json W1N-240)、-20260829-210708.jsonW1N-242 (改法:WS lovelace/config/save,参数 url_path: dashboard-unknown + config;勿直改 .storage/)。验证:WS 读回 18→20 实体 diff ✓、gauge 配置一致 ✓、URL http://hass.windy.lan:8123/dashboard-unknown/power-monitor

csg_sensors.yaml W1N-242: csg_predicted_usage / csg_mom_change / csg_this_month_daily_avg 三处取 days[-1] 前补 sort(attribute='date')(与 csg_yesterday_kwh 一致,防上游乱序取错 数据日)。备份 csg_sensors.yaml.bak-20260829-w1n242。验证:Core restart 后回归值不变(334.81 / -0.3 / 10.8 / 90.3 / 200.94 / 180.28)。

CSG 面板重构 2026-09-04VPS-90,先核对计价后展示层改动): 核对 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_xing10-25 09:00automation.csg_mian_ban_qie_xia_ji_dang_ti_xing4-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_TOKENsupervisor 镜像 aarch64-hassio-supervisor:2026.08.0)连 ws://172.30.32.2/core/websocket 命令名 lovelace/config(读)+ lovelace/config/save(写), lovelace/config/get 已不存在(unknown_command)。验证:新实体 0.589 元/kWh、15 个 csg_* 数值齐全、回归值不变(14.09/198.65/331.22/ 304.99/181.89)、automations on、ha core check OK、日志无 template 错误。

CSG 面板 Sections 重构 2026-09-22VPS-90 v4: dashboard-unknown/power-monitor 从单列 Masonry 改为原生 Sections(最多 2 列、 关闭 dense placement),并新增子视图 power-details。主页按「本月概览 → 阶梯 电价 → 最新数据/季节累计 → 近 30 日趋势」组织:概览只保留本月用电/阶梯电费 与明确标为「预计」的用电/电费;余额因口径未独立验证不展示;欠费仅在 sensor.0800041935246530_arrears > 0 时显示。详情页承载上月、近 30 日、月日均、 环比及本年/去年账单统计;移除无决策价值的 csg_prediction_progress 展示。 趋势图使用 custom:apexcharts-card 读取 sensor.csg_history.history_data 最近 30 条 (该属性来源是上月 + 本月逐日明细),画日柱和 30 日均线,不接 Timescale reader 两条 series 均显式覆盖 unit: kWh,避免继承 sensor.csg_history 的记录数单位 「条」。用户首轮目视反馈后,「最新数据与本月进度」改为跨两栏:最新日用电与 本月日均各占桌面半栏,当前季节 gauge 全宽,窄屏由 Sections 自动回落堆叠。 外部实施审核收口时补齐预测不确定性标识:两张 Tile 分别命名为 「预计本月用电(估算)」/「预计本月电费(估算)」,且「本月概览」副标题明确 写明「预测按当前速率推算,月内会调整」;仅改展示文案,不改计算或实体。

季节 gauge 由 UI Template Helper 自动切换:配置入口 01M337A71HTAEBF8AB769J02WFdomain templatetitle CSG Summer Tariffsource userstate loaded),实体 binary_sensor.csg_summer_tariff,状态模板 {{ 5 <= now().month <= 10 }}on 显示夏季 max 650 / 260 / 600off 显示冬季 max 450 / 200 / 400;状态既非 on 也非 off 时显示警告。现有 4-25 / 10-25 Matrix 提醒保留,不改 automation;该 helper 当前只被此 dashboard 引用。

写入前 WS 快照: /homeassistant/.lovelace-backups/dashboard-unknown-power-monitor-20260922-0815-pre-sections-v4.json 4953 bytesSHA-256 f5e3e05ab29a4c13b870547b0a189e95b793d85e82c85c61190dedf66811a874)。 首轮 v4 到布局/单位修正前的第二快照: /homeassistant/.lovelace-backups/dashboard-unknown-power-monitor-20260922-0825-pre-layout-unit-fix.json SHA-256 8b56e1303572405d7c850305ffd435cc5c7ca583330160fdd3f8b213473e1fcd)。 外部审核文案修正前的第三快照: /homeassistant/.lovelace-backups/dashboard-unknown-power-monitor-20260922-0838-pre-estimate-copy-fix.json (文件 SHA-256 b1c1e0c6946818ce7fb44d5d731e3e78aebe9c8d51ed465ce8505a0f1855c05c)。 验证:lovelace/config/save 后规范化 JSON 哈希与 payload 一致;22 个引用实体全部 存在且无 unknown/unavailable;夏季 helper=on;图表 30 条为 2026-08-22 至 2026-09-20,重算均值 9.15 kWh 与 sensor.csg_30d_avg 一致;最终文案修正后的 规范化配置哈希为 c16d11c01353f90761da5837b06cc7ad1ea7b400f249c99cf291c03f4980a28d。浏览器没有 HA 登录态,390/768/桌面三宽度视觉验收仍待人工登录后完成。回滚时先把上述 JSON 经 WS lovelace/config/save 写回;再扫描 helper 引用,确认无消费者后才可删除 配置入口,勿直改 .storage/

CSG 长期归档(W1N-243, 2026-08-29;任务改号 2026-09-21: scribe 库新增 csg_history 表(逐日 usage/cost/ladder/balance + 逐月累计;2026-07-01 起回填, 永久),由 TimescaleDB 每日任务 1010(原 10082026-09-21 重建) csg_daily_snapshot()14:30 UTC = 22:30 Asia/ShanghaiTS 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 § 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_day09-01..09-1919 天,和 168.68)与 states_rawyesterday_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 起恒为 unknownstates_raw 无一条有值),故面板上所有「元」 均为模板阶梯估算值,从未与账单口径对账。

csg_sensors.yaml 预测速率 v22026-09-21,已实施): 只改 csg_predicted_usage 的速率项,csg_30d_avg 语义不动(面板「近30日平均」照旧)。 新公式:avg = mtd × w + recent7 × (1 w),其中 mtd = 本月累计 / 已过天数recent7 = history_data 末 7 条均值、w = min(已过天数, 14) / 14;两者都取不到 才退回 csg_30d_avg动机:本月至今速率按天数爬升可信度,避免月初被上月污染; 近 7 日速率兜底,因为月初唯一的新数据就是上月末。Block C 触发器加 sensor.csg_history19 天回测(基准 = 第 19 天本月至今速率外推,逐日重放 集成 this_month_by_day + last_month_by_day):

速率方案 预测电费摆动 最大偏差 平均绝对偏差
旧:30 日均值 21.1% 35.99 元 14.04 元
v2K=7 14.6% 16.81 元 5.65 元
v2K=14(采用) 13.9% 15.33 元 4.94 元
v2K=14 但兜底用 30 日均值 23.3% 33.12 元 6.28 元

(末行证实病根就是那个跨月的 30 日均值兜底。)备份 /homeassistant/.csg-backups/csg_sensors.yaml.bak-20260921-pre-predictor (改前)与 …-pre-k14K=7 中间态)。验证ha core check OKCore 重启两次 trigger 模板块不吃 template.reloadW1N-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 天)是回测选出来的单月参数,属弱证据,换季/换季后值得复看。

Seasonal gauge switch (W1N-240 已知事项): 每年 11-01power-monitor 视图「本月累计」gauge 切到冬季 max:450 / 0/200/4005-01 切回夏季 max:650 / 0/260/600(与模板 now().month 季节逻辑对齐;模板常量在 Block B/D variables)。 提醒 automation2026-09-04 起,VPS-90: automation.csg_mian_ban_qie_dong_ji_dang_ti_xing10-25)与 automation.csg_mian_ban_qie_xia_ji_dang_ti_xing4-2509:00 经 matrix_e2ee.send_message 发操作步骤提醒;gauge 的 max/segments 无法 模板化,仍需人工改卡配置。

Home PPPoE IPv4 to CSG is still blackholed (curl -4 to 218.19.148.218:443 times out). end0 IPv6 is enabled (ipv6.method: auto); from HA, curl -6 https://95598.csg.cn returns HTTP 200 via 240e:f9:8060::1:16.

tianqi weather recorder patch (verified 2026-08-13, W1N-75): /config/custom_components/tianqi/weather.py has a local patch adding _unrecorded_attributes = frozenset({"hourly_temperature", "hourly_skycon", "hourly_cloudrate", "hourly_precipitation"}) to the WeatherEntity class. Without it, weather.guangzhou's state attributes (~19 KB, dominated by the 4 hourly_* arrays of up to 48 entries) exceed the recorder 16384-byte limit, so the recorder drops all attributes for the entity and logs Recorder.db_schema: State attributes for weather.guangzhou exceed maximum size of 16384 bytes. The patch excludes only the 4 arrays from recording (live state unchanged; other attributes still stored; ~6.3 KB payload). Backup at weather.py.bak-w1n75. Re-apply after any tianqi component update. The _unrecorded_attributes mechanism exists in Core 2026.8.1 (Entity.__init_subclass__state_info["unrecorded_attributes"], consumed by recorder shared_attrs_bytes_from_event).

matrix_e2ee live tree (E2E Matrix bot, verified 2026-08-20)

v0.3.12 (tag v0.3.12; feat — Matrix activity events matrix_e2ee_message_received / matrix_e2ee_verification_done + push diagnostics; v0.3.9 added Connection health binary sensor, SAS/command allowlist split, URL normalization, single-entry enforcement): source copy from /home/windy/project/ha-matrix-e2ee ea421ed (tag v0.3.12) deployed 2026-08-20 via SSH rsync from workstation (upgraded from v0.3.2, backup matrix_e2ee.bak-20260820-v0.3.2). Custom matrix_e2ee integration — Config Flow (UI). See docs/home-assistant-matrix.md. Update runbook: 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.

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). Database re-initialized 2026-08-29 14:06 CST (user-handled; earlier relation "entities" does not exist errors resolved). Health: binary_sensor.scribe_database_connection.
  • 2026-08-29 config applied (backup /homeassistant/configuration.yaml.bak-20260829-scribe):
    • record_events: true with include_events whitelist: automation_triggered, matrix_e2ee_command, matrix_e2ee_message_received, matrix_e2ee_verification_done, script_started, tag_scanned, mobile_app_notification_action, homeassistant_start, homeassistant_stop.
    • State noise trimmed: exclude_domains update/button; glob sensor.zigbee2mqtt_bridge_*; 4 hassio cpu/mem-percent entities.
    • Global exclude_attributes drops tianqi hourly_* arrays (~19 KB/state — the recorder-side _unrecorded_attributes patch does not apply to Scribe).
    • enable_stats_io + enable_stats_size on → 14 sensor.scribe_* stats entities (scribe_states_written, scribe_events_written, rates, sizes).
    • Verified post-restart 14:23 CST: writer started, scribe_events_written=1 (homeassistant_start), states ~110/min, buffer 3, no scribe log errors.
  • 4.x upgrade核对 2026-09-13(只读 + 一处配置变更)live manifest.json = 4.4.0。两个 4.0 breaking change 在本机都不需要动作——数据库是 3.x 结构 (states_raw PK (metadata_id, time) 在,4.2.0 的启动态去重因此可用), TimescaleDB 2.29.2 已装。4.1.0 修了 db_url 优先级,YAML 里的 !secret scribe_url 现在是权威。scribe.yaml 现有键在 4.4.0 全部仍然合法 (未知键被忽略,extra=vol.ALLOW_EXTRA)。配置优先级 YAML > entry options > entry data > 默认值,而 _resolve_settings 读的是 hass.data[DOMAIN]["yaml_config"](只有 async_setup 会写),所以 YAML 改动必须重启 Corereload config entry 不重读 YAML。
  • stats_io_interval: 3002026-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;重启 88sScribeWriter started successfully 无 scribe error/warningscribe Repairs 问题 0 条;传感器发布间隔实测正好 300s11:19:26 → 11:24:26 UTC)。
  • Retention 现在可用但刻意不设retention_states / retention_events 4.0.0)按间隔丢 chunk,留空 = 永久保留,符合本机定位(Scribe 是永久归档, recorder 保留 365 天)。注意 retention 是绕过 entry data 副本读取的 from_entry_data=False),所以删掉 YAML 行即撤销策略。db_schemaenable_rollupsscribe.purge 同样未用:图表走 sensor_minute + timescale_database_reader(见 hosts/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 (12 template sensors, top-level list)
    • quick_sensors.yaml (scaffold for Quick-derived quick_* sensors, empty list with convention header). !include_dir_merge_list merges per-file lists; non-list files are silently skipped — every file in templates/ must be a top-level list (- sensor: blocks). Directory include only picks up *.yaml, so the .bak / .pre-* backups in the dir are ignored. After adding sensors, verify template-platform entity count = 12 + N (entity registry platform: template).
  • Convention (per review + W1N-233): pure sums/averages stay min_max helpers (e.g. sensor.dang_qian_zong_gong_lu); only template-logic derivations (ladder pricing, cross-entity conditions) go into quick_sensors.yaml.
  • Post-change verification 20:19 CST: ha core check ok, 92 s restart (2026.8.3), binary_sensor.scribe_database_connection on, scribe_states_written 18581→19426 growing, template entities still 12, csg sensors numeric, no scribe/template log errors.

Timescale Plotly card + database reader (verified 2026-08-29)

Chart stack over the Scribe TimescaleDB archive. Upstream pair (no HACS; manual copies): reader remmob/timescale_database_reader v1.1.0 (main bb8776a) + card remmob/timescale-plotly-card 2.2.0 (main 217961d).

  • Reader integration: /homeassistant/custom_components/timescale_database_reader/. Config entry 01M165P77QT1FQEAVPNZHDT82W ("Scribe", source: user): connects hass@192.168.55.15:5432/scribe (credentials = secrets.yaml scribe_url), table: sensor_minute. Exposes no entities/services — it serves WS command timescale/query (window ≤ 365 d, ≤ 50 000 rows, downsample bucket seconds). Benign startup warning Error executing test query: column "time" does not exist: the self-test SQL assumes the LTSS column name; the scribe table uses minute — real queries work (verified: 70 rows for a live power sensor).

  • Card: /homeassistant/www/community/timescale-plotly-card/timescale-plotly-card.js (root-owned, same convention as HACS dirs). Lovelace resource (storage) id 2e360d17b5aa4ce59c2fd13c43b51215/hacsfiles/timescale-plotly-card/timescale-plotly-card.js, type module. Card config matches the entry by database: scribe (name from the reader entry). Updates: replace the file, resource URL unchanged — browsers need a hard refresh or a bumped ?v= query on the resource URL.

  • pgdb side (sensor_minute_aggregate cagg + sensor_minute hypertable + every-minute refresh job): see hosts/pgdb.md § Databases.

  • Agent-side HA WebSocket without a long-lived token (verified 2026-08-29): connect ws://supervisor/core/websocket with header Authorization: Bearer $SUPERVISOR_TOKEN, then send {"type":"auth","access_token":"$SUPERVISOR_TOKEN"} — the Supervisor proxy swaps it for a core token (works as the internal Supervisor admin user). Note lovelace/resources/create in HA 2026.8 takes res_type (NOT resource_type).

  • Scribe stores numeric sensor values in states_raw.value with state NULL, so sensor_minute.state shows '0' for numeric sensors; the card plots avg_state (from value) — expected, not a bug.

  • Quick 仪表盘(dashboard-quick)图表套件2026-08-29 创建,经 WS lovelace/config/save 写入;W1N-230 修复 + W1N-231 round-2 改进): 5 张 timescale 卡——大功率电器/常驻负载功率(按量级拆图,避免尖峰压扁 <70 W 基线)、按插座用电量(energy_mode + cumulative/diff,数据质量前提 见 pgdb 的 refresh 过程补丁)、室内外温湿度(温度左轴/湿度右轴,4 位置同色 配对)、人体感应活动状态(3 个 motion_statebanded state_map none/small/medium/large → 0-11per-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_lumin_max sum helperround_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-190256round-2 改进: 20260829-194040)。

  • 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默认档未改6h / 6h / today / 24h / 12h)。备份 .lovelace-backups/dashboard-quick-20260913-190912-pre-timerange.json⚠️ 本条的 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。全卡唯一能解析 dparseDurationToMs() (正则 /^(\d+)(m|h|d)$/只被 resolveEnergyWindow() 调用一次 即只服务 energy 卡。后果:3d24h 还短(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,图上只画 15719: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 donut11 个 插座当前功率(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→35011 项底部图例占 ~100px, 280 时饼图本体太扁)。

    ⑥ card-mod 等高 (2026-09-21 晚):不同卡类型(apexcharts vs timescale-plotlyheight 渲染比例不同(apexcharts 含图例/内边距,timescale 是纯绘图区), 直接对齐数字无法视觉等高。安装 card-modHACS → card-mod + card-mod-studio 后用 CSS 强制 ha-card 高度:

    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 OKWS 读回与写入 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(纯 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.jsonWS 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 一次。

地图仪表盘: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 mapstorage)唯一 map 卡替换为 custom:map-cardnathan-gs/ha-map-card v1.16.0,手动安装非 HACS):tile_layer_url = https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png?key=<CARTO_KEY> (配 tile_layer_options: {subdomains: abcd, maxZoom: 20} + OSM/CARTO attribution)。实体不变:2 person + 4 zonezone 用 display: icon + circle: autocircle 读实体 radius 属性画半径圈)。
  • CARTO key 是 secret: 只存在于服务端 lovelace 存储(dashboard map 的卡片配置)和用户本人处;勿写入本仓库或 Linear。
  • 文件/资源: /homeassistant/www/community/ha-map-card/map-card.js root:root 644678554 Bsha256 f30dfb606e858d2216d5198d8cf758ce956d127006ebd7d66d4329153a247ec2); Lovelace resourcestorageid 9d2b50b52c60420d89ebd041f722cf60/hacsfiles/ha-map-card/map-card.jstype moduleWS lovelace/resources/create2026.8 参数名 res_type)。升级 = 手动替换 该文件(不在 HACS 管理下,浏览器需强刷)。
  • 备份: /homeassistant/.lovelace-backups/dashboard-map-map-20260830-133714.json (还原 = 把备份里的 views[0].cards[0] 写回后再 WS lovelace/config/save url_path map)。
  • 验证 8/30: 同瓦片无 key=水印 / 带 key=干净(256×256 PNG 视觉对比); resource HTTP 200 text/javascriptWS 读回卡片配置(type/entities/key/ attribution/options)全部符合;HA 主机 curl -4 带 key 瓦片 200。
  • Follow-up: Core 升 2026.9.0 stable 后内置地图/zone 编辑器自动切 OSMF 矢量瓦片;届时可保留 custom 卡(继续 keyed CARTO)或用备份还原 内置卡。zone 编辑器等其余内置地图的水印在 2026.9 前无解。

Known issues

Bluetooth hci0 instability — RTL8821CS (verified 2026-08-13, W1N-74): The local Bluetooth controller hci0 is an RTL8821CS combo chip on the x88 Pro board. Kernel logs show recurring hci0: hardware error 0x00, Opcode 0x200c tx timeout (HCI_LE_Set_Scan_Parameters), Unable to disable scanning: -110, Peer device has reset — the chip hardware-stalls during active scanning. HA's bluetooth_auto_recovery power-cycle then times out after 5 s and retries every ~2 min: bluetooth_auto_recovery.recover: Could not reset the power state of the Bluetooth adapter hci0 ... due to timeout after 5 seconds. The HAOS image already ships custom systemd units to cope (x88-bt-hci-recovery.service and a "Patch HA Bluetooth scanner mode for x88 RTL8821CS" service, visible in host journal). No user impact: there are no BLE entities in HA (xiaomi_ble / bthome / led_ble / bluetooth / esphome domains are all empty; platforms merely load from stray advertisements). Real IoT devices are Zigbee (via Zigbee2MQTT) or WiFi/MQTT/cloud. An ESPHome Bluetooth-proxy ESP32 (/config/esphome/bluetooth.yaml, bluetooth_proxy: active, WiFi ubnt-haas) is configured but currently offline (ESPHome add-on stopped, port 6053 unreachable) and produced no entities. Follow-up (optional): disable the local adapter and rely on the ESPHome proxy, or stop the bluetooth integration entirely.

eMMC disk lifetime 10% (verified 2026-08-13, W1N-76): ha host info reports disk_life_time: 10 — the boot eMMC (/dev/mmcblk2, CJTD4R 0xacacc064, 64 GB) has ~10% life left. disk_free: 40.2/56.4 GB. Full backup pre-maintenance-20260813 (slug 411a4ba5, 144.26 MB) taken 2026-08-13 covers current config; monitor disk_life_time on each health snapshot and plan a disk replacement / data-disk migration before the eMMC fails.

Matter Server (verified 2026-08-21)

  • Add-on core_matter_server (homeassistant/aarch64-addon-matter-server) runs the Matter commissioner on this host (host networking; add-on container app_core_matter_server).
  • After the ISP PD prefix rotates (PPPoE redial), the add-on can cache a stale IPv6 GUA in its mDNS advertisement — clients trying that dead address make Matter commissioning/connection fail. Fix: restart the add-on so it re-enumerates addresses: ssh hassio@hass.windy.lan 'sudo -n -i ha apps restart core_matter_server' (ha addons restart ... also works; "addons" is deprecated in favor of "apps").
  • Verified 2026-08-21 (W1N-207): stale 240e:3bd:234:2f22:* AAAA in mDNS removed by restart; advertisement now carries only current GUA 240e:3bd:235:1fb2:* + link-local; CASE sessions with Aqara M3 / SmartThings hubs resumed over IPv6 link-local.

Open items (2026-08-21, W1N-207): a phone on LAN55 was querying five known _matter._tcp instances of which only HA answered — the other Matter nodes are offline / not announcing (device-side; user to confirm power/Wi-Fi). HA's IPv6 default route via NetworkManager was observed missing once (curl -6 intermittent, while ping6 and curl -6 --noproxy work) — not the Matter root cause; re-check on the next health snapshot.

Verified 2026-08-23 (read-only, W1N-207): add-on started, version 9.0.4, no update pending; current GUA 240e:3bd:238:4812:* (PD rotated again since 08-22) advertised correctly over v4+v6. Both ESP32-C2 bulbs now announce _matter._tcp (multi-fabric, including this host's fabric DCE86145C137AF0E) — but they refuse TCP 5540 on IPv4 and IPv6, so matter-server holds zero established :5540 sessions (device-side failure mode C; no errors logged — see docs/matter-pairing-troubleshoot.md §8).

马桶换气电源(Matter 插座,半计量)+ 电量估算 (2026-09-13)

设备Matter Smart PlugSIXWGHmodel_id 3596hw 1.0 / sw 1.3.0),node 18 (0x12)device_id 5ef1850953466d6e7a9c6b901fbebe1cconfig 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) 恒为 nullPeriodicEnergyImported (0x0003) 带载也恒为 {Energy: 0}CumulativeEnergyExported (0x0002) 不存在(EXPE 未声明,自洽)。HA 只用 CumulativeEnergyImported 建能量实体 components/matter/sensor.py:1083allow_none_value=True)→ 该实体 永远不会出数功率计量本身正常0x0090 FeatureMap = 2 (ALTC) Voltage / ActiveCurrent / ActivePower 都随负载变化(实测 220.3 V / 118 mA / 24.7 WHA 电源 0.0→24.9 W 有历史)。厂商 update 实体报无新固件。

处理(方案 A:功率积分补电量)

  • 新建 Integration (Riemann sum) 辅助元素config entry 01M2D53T188FW8WEC547ENHSVHdomain integrationstate loaded), source sensor.wei_sheng_jian_ma_tong_huan_qi_dian_yuan_dian_yuan method: trapezoidalunit_prefix: kunit_time: hround: 3max_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: energystate_class: total——能源仪表盘 允许 TOTALTOTAL_INCREASINGcomponents/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 走 RESTWS 只有 config_entries/flow/progress|subscribe,没有 start)。 经 supervisor 代理即可,无需 HA 长连接/长寿命 token:

# 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 复核时已自动消失——建辅助元素后不要把这条 瞬时告警当作失败。