ha-maintenance.sh --restart-core --yes exited 1 in <1s without restarting Core. Empty output is ssh failure hidden by 2>/dev/null + pipefail, not a MOTD-strip after a successful restart. Direct `ha core restart` is the working path.
13 KiB
[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 | PVE VM 180 (haos) — not a separate physical host (verified 2026-08-09) |
| 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 |
|---|---|
end1 |
IPv4 static 192.168.55.11/24 (gw .254, DNS 192.168.66.36); IPv6 SLAAC auto with GUA on 240e:3bd:231:2082::/64; primary LAN55 NIC |
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.404on/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 end1'
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):
- Download Source code (zip) from the GitHub Release.
- Extract. Copy only the inner
custom_components/china_southern_power_grid_stat/tree — not the repo root, not a nested extra folder. - Place it at
/config/custom_components/china_southern_power_grid_stat/. - Restart Core (Settings → System → Restart).
- 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 1–2 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 withNo module named '...bak-YYYYMMDD-...'. - Do not install this fork via HACS on this host. HACS still tracks
CubicPill/china_southern_power_grid_statv1.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
unknownwhile last-month filled, reload the config entry (UI: integration → Reload, or supervisorPOST /core/api/config/config_entries/entry/<id>/reload). runbooks/scripts/ha-maintenance.sh --restart-core --yescan print nothing and exit 1 in under a second without restarting Core. The wrapper's ssh line discards stderr (2>/dev/null); withpipefail, 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) andha core info. Preferssh -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.1 (55a293fc, verified 2026-08-14, W1N-107): manual zipball of
GitHub release
v1.3.1
copied to /config/custom_components/china_southern_power_grid_stat.
Earlier trees: v1.3.0/69f13c90 (W1N-106), a433e8c (W1N-105), de01914
(W1N-103), eb8b174 (W1N-102). Backups:
/homeassistant/.csg-backups/ (w1n102/104/105/106/107).
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.
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).
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.
Related docs
- runbooks/home-assistant-maintenance.md —
haCLI maintenance runbook + script; custom-component zip install is §7 - docs/lan-overview.md — LAN map and gw port-forward
- hosts/dns.windy.lan.md —
hass.windy.lan/hass.localrewrites