Home Assistant integration
claude-cowork · confidence medium · status active
tags: home-assistant iot camera power
Now
2026-10-02 08:55-09:15 AEST (renaming the pool filter pump plug): sensor.home_entertainment_* was never an entertainment plug. It is the pool filter (circulation) pump, and the entity ids now say so. The Tuya device 3d2d163c83a9864b48d885d21cbc57b7 (PC191HA, manufacturer Tuya) has always been named "Pool pump" in the device registry; only the entity ids carried the wrong label, inherited from an earlier use of the plug. Renamed via config/entity_registry/update over the WebSocket API (Dwayne's 2026-10-02 instruction, "home entertainment is actually the new pool filter pump, update the homeassistant dashboards and entities to reflect this"):
sensor.home_entertainment_power->sensor.pool_filter_pump_power(friendly name set to "Pool filter pump")sensor.home_entertainment_current->sensor.pool_filter_pump_currentsensor.home_entertainment_voltage->sensor.pool_filter_pump_voltagesensor.robot_vacuum_energy_total_energy->sensor.pool_filter_pump_total_energy(the device's native kWh counter; its id had been left over from a different device entirely)switch.home_entertainment_socket_1->switch.pool_filter_pump_socket_1select.home_entertainment_power_on_behavior->select.pool_filter_pump_power_on_behavior
new_entity_id on that call needs the full domain-qualified id; a bare slug returns invalid_info: Invalid entity ID. Renaming an entity id rewrites dashboard references automatically, 6 storage dashboards took the new ids with no dashboard edit, including the Load breakdown now card on arcade-power and energy-ops, whose loop now prints "Pool Filter Pump". Two dashboard strings were changed by hand: the arcade-media history card retitled to "Pool filter pump power · 24 hours", and the power-tile label to "Pool filter pump".
Do NOT rename the Entertainment Room area or the headings derived from it. entertainment_room is a real HA area (the TV room, media_player.entertainment_room_tv, sensor.entertainment_room_be65pro_*). A blanket string replace for "Entertainment" corrupts it, one such replace hit the dashboard-areas section title and was reverted within the same session. Match entity ids, never the word.
Other layers updated to match: configuration.yaml (customize block, the pool-water-temp trigger, Power Tracked Devices, the pump term in the pool sampling template), packages/dashboard_compatibility.yaml (Monthly Consumption), and the scripts ha_power_known_loads.py, power_baseline_monitor.py, pool_dp_window.py, turn_on_entertainment_hub.py, ha_anomaly_detector.py, clawd/scripts/event-chains.py (two dict entries). Cron fbec9d2caf17 renamed to pool-filter-pump-on-morning; its script field still names the old filename, which was edited in place.
Known leftover, deliberate: sensor.home_entertainment_total_energy remains live. It is a platform: integration counter defined in configuration.yaml, and it is the one entity in this family whose state the Monthly Consumption template consumed. See the Log entry of the same date for why it cannot be renamed without a restart, and why the template was repointed at the plug's native counter instead. Backups: ~/.homeassistant/backups/pool-filter-pump-rename-20261002-085428/ (config + scripts) and ~/.homeassistant/backups/pool-filter-pump-rename-dashboards/configs.json (all 26 storage dashboards, read over the API before the write).
Read live 2026-10-02 09:15 AEST: sensor.pool_filter_pump_power 259.3 W, _current 1.26 A, _voltage 241.2 V, _total_energy 2.1 kWh, switch.pool_filter_pump_socket_1 on. Power Tracked Devices 600 W + Power Untracked 502 W = 1102 W against CT1 1102.06 W. Sources: live HA /api/states and ws://127.0.0.1:8123/api/websocket, ~/.homeassistant/configuration.yaml, ~/.homeassistant/.storage/core.entity_registry, ~/.hermes/cron/jobs.json., hermes.
2026-09-29 06:22-06:45 AEST (codex vault daily audit, pool sampling boundary): the pool water temp sensor samples the wrong first reading each day. configuration.yaml:197 defines pool_water_temperature_sampled, a trigger-based template sensor that samples only when 10 <= now().hour < 14 and holds this.state otherwise (built from Dwayne's 2026-09-27 18:03 instruction, "the pool water temp should only be sampled between 10am and 2pm"). Eight dashboards use the sampled entity (climate_energy, command_centre, dashboard_areas, energy_ops, floor_heating, home_daily, home_dashboard, maintenance_ops) and none use the raw probe. The window opens at the same minute the circulation pump starts. sensor.home_entertainment_power on 2026-09-28 reads 0.0 W at 09:00, 0 to 1365 W across the 10:00 hour, 1365 to 1384 W at 11:00; the raw probe sensor.pool_heat_pump_water_temperature reads 15 at 09:00, spans 15 to 21 across 10:00, 21 to 22 across 12:00, 22 at 13:00. So the 10:00 sample is stagnant pipe water, and exec_dashboard_data.json records 2026-09-28 pool_temp_min: 15.0 against in-window 21-22. The hold works as designed: the sampled sensor ran 22.0 flat from 14:00 to the next morning. Routes, none applied because the window is Dwayne's stated number: keep 10:00, move the start to about 10:30, or require two consecutive rising in-window samples. Also this window: the d3 dashboard had its invented solar supply removed (www/d3_dashboard.html mtime 2026-09-28 02:54:24, backup d3_dashboard.html.bak-nosolar-20260928-025018; hardcoded 1955 W and 1210 W defaults replaced by — under a new liveDataSeen guard, roof polygon relabelled No Solar Installed), and lovelace.arr_stack switched to tailscale IPs (7 100.80.168.13 URLs, 0 192.168.). The "free solar" wording survives on lovelace.floor_heating, lovelace.energy_ops and automations.yaml (cts_floor_drying_schedule, cts_offline_alert) though it is a free-power tariff window, not generation. Pool card rebuilt 2026-09-29 00:30 as pool metered 11.6 kWh (from 7.6), source sensor.pool_heat_pump_energy; the circulation pump meter reads 193.24 kWh and is on no pool tile. Sources: ~/.homeassistant/configuration.yaml, ~/.homeassistant/recorder/home-assistant_v2.db hourly statistics, ~/.homeassistant/exec_dashboard/exec_dashboard_data.json, ~/.homeassistant/www/d3_dashboard.html, ~/.homeassistant/.storage/lovelace.*, live HA /api/states, read 2026-09-29 06:22-06:45 AEST., hermes.
2026-09-28 10:08-12:30 AEST (camera vision sweep, driveway recovery): the Driveway TC41 camera is back after 14 days frozen, with no physical intervention. The TP-Link entry 01M1P28GK90EW3TX966A41WACZ re-set up at 10:08:24 AEST (12 Setting up tplink.* lines, one full domain setup) and its 600 s not ready yet retry loop stopped after the 09:58:22 line, 60 lines for the day and none after 10:00. switch.driveway left unavailable for on and camera.driveway_live_view for idle at 2026-09-28T00:08:24Z. Live /api/camera_proxy/camera.driveway_live_view now returns changing frames: 81342 B (watermark 12:03:56 PM), 87541 B (12:18:29 PM), 110611 B (12:26:42 PM), each byte-stable across three reads 30-45 s apart. That supersedes the frozen still camera.driveway_real_still.jpg sha256 06ce21c4, watermark 2026-09-14 12:49:17 PM, unchanged across 29 sweeps. The previous next action, a physical power-cycle of the TC41, is now superseded 2026-09-28: the unit recovered on an HA-side integration re-setup, so no power-cycle was needed and none was performed. Pool camera is back on target: published_pool.jpg frames the pool and its deck framing again from 2026-09-27 12:03 onward, after the off-target no-pool frames of 2026-09-24 through 2026-09-26 12:00, so that viewpoint fault is also resolved without intervention. The 12:03:48 AEST sweep itself: 14 cameras, 18 captures, 3 failed, 0 actionable, all three the 2689-byte Nest dark placeholders (camera.lounge_display, camera.home_home, camera.entryway_front_stairs). The driveway slug reported stale_real in that sweep because its snapshot timed out at 12:03, which the later fresh publishes supersede; treat the slug status as stale evidence when the proxy returns live frames. sensor.camera_vision_driveway is still unavailable (last 2026-09-26T09:26:34Z) and sensor.camera_vision_summary reads 6/7 cameras analyzed at 11:43, so the driveway vision analysis has not resumed yet; that is the remaining item and it needs no device work. Also visible in the fresh driveway frames: the front-yard trampoline lies on its side, the same as in the 2026-09-14 frozen frame and the 2026-09-24 front-door frame, so it is a standing condition rather than a new one. Sources: ~/.hermes/camera_sweeps/20260928-120348/manifest.json and published_*.jpg, ~/.homeassistant/www/camera-stills/, live HA /api/states, /api/camera_proxy/camera.driveway_live_view and /api/config/config_entries/entry, ~/.homeassistant/home-assistant.log, ~/.hermes/logs/ha_expose_camera_stills.last.json, read 2026-09-28 12:03-12:30 AEST., hermes.
2026-09-28 05:12-05:25 AEST: fifth ha_health_check.py false-positive family, gated and fixed. The hourly job alerted on 1 ERROR line in its 70-minute window: Received invalid command: {'type': 'lovelace/config', 'url_path': 'home-dashboard'} from homeassistant.components.websocket_api.http.connection. This is a client protocol error, not an HA fault. Verified live against 2026.8.2 on ws://127.0.0.1:8123/api/websocket: {"id":1,"type":"lovelace/config","url_path":"home-dashboard"} returns the config (success:true), {"id":2,"type":"lovelace/config"} returns config_not_found, and the same message with no id returns invalid_format and is what HA logs as Received invalid command. The id must be a non-zero integer; a string id such as "id": "home-dashboard" fails the same way. Measured in the current log: 25 such lines on 2026-09-27/28 in five batches (02:35:48 x17 enumerating all 17 dashboards, 03:15:42 x3, 03:19:06 x2, 03:22:24 x2, 04:22:40 x1, 05:21:01 x1), every one from a local ad-hoc dashboard read. The class is self-sustaining: each hourly ha-health-check run read a dashboard, its own malformed read landed in the log, and the following run alerted on it. Fixed with ws_client_protocol_noise-style gate WS_CLIENT_PROTOCOL_TAG + WS_CLIENT_PROTOCOL_MARKS in ha_health_check.py, applied before the *arr and coordinator gates. Filtered on the tag-AND-phrase pair only, so any other websocket_api.http.connection error still alerts. Verification: python3 -m pytest tests/test_ha_health_check.py 47 passed (44 pre-existing plus 3 new: invalid-command suppressed, unknown-command suppressed, other websocket_api error still alerts); --verbose reports all probes passed; a bare run now prints [SILENT] with exit 0. HA itself was never unwell across the window: /api/ 200 API running., /api/config state=RUNNING, version 2026.8.2, home-assistant.log.fault unchanged at 314022 bytes (last write 2026-08-25 00:02). The same lesson is recorded as a pitfall in the lovelace-dashboard-management skill: send {"id": <int>, ...} from the first websocket message. Sources: live websocket probe and HA REST reads, ~/.homeassistant/home-assistant.log, ~/.hermes/scripts/ha_health_check.py, ~/.hermes/tests/test_ha_health_check.py, read 2026-09-28 05:12-05:25 AEST., hermes.
2026-09-27 15:30-15:50 AEST (weekly retune): the 2026-09-24 restore-time failure class is unchanged after three days. Live ha-tag-status reads 209/255 ⚠️ 🏠 and a direct /api/states read counts 46 unavailable/unknown across the counted domains. The 10 Tuya lights (light.corridor_led, light.kitchen_led_1..5, light.laundry_led, light.guest_bathroom_guest_led_a), light.garden and light.guest_led_b were all still unavailable, and climate.inverter_pool_heat_pump reads unknown. New evidence on cause: the Tuya entry 01KKZT8SVT8TGCYWYM63GS1M45 is loaded, but the Tuya cloud device list marks Corridor LED, Laundry LED, Kitchen LED 1..5, Bed LED and Fish LED "online": False with stale device IPs (159.196.168.50) against the working bulbs at 125.168.201.211. So the Tuya LEDs are offline at the device/cloud layer, not an HA restore fault, and a reload_config_entry (HTTP 200, []) did not restore them. Lifx is genuinely a retry loop: entries Garden, Guest LED A and Guest LED B all sit in setup_retry with the same get_hostfirmware, get_version, get_group timed out after 5 attempts. New and actionable: switch.driveway and its five companion entities are unavailable because the TP-Link Driveway TC41 entry (01M1P28GK90EW3TX966A41WACZ) cannot reach 192.168.68.55:9999; ICMP to .55 succeeds (11.8 ms) while the Kasa port is closed, and arp shows .58 (Roborock) as incomplete. binary_sensor.arr_sabnzbd_warnings unavailable is expected (ARR runs 00:00-05:00 only). Sources: live ~/.hermes/bin/ha-tag-status and HA /api/states, /api/config/config_entries/entry, ~/.homeassistant/home-assistant.log, ~/.homeassistant/.storage/core.entity_registry, Tuya cloud device dump in the HA log, ping/arp to 192.168.68.55 and .58, read 2026-09-27 15:30-15:50 AEST., hermes.
2026-09-26 07:15 AEST: ha_health_check.py alerted on a single Open-Meteo weather fetch error, and the fourth false-positive family is now gated in the script. Full detail in the Log entry below; the script change and its tests are the only change to the file. Nothing else in the window, and no live fault: weather.home reads sunny with data refreshed 2026-09-26 07:03 AEST, HA state=RUNNING, 675 states with 143 unavailable against threshold 150, home-assistant.log.fault unchanged at 314022 bytes (last write 2026-08-25 00:02).
2026-09-25 20:57 AEST (evening review): HA reads 204/250 ⚠️ 🏠 live, 46 counted entities unavailable. New finding: 38 of the 46 share last_changed 2026-09-23T21:01:23Z, which is exactly the 2026-09-24 07:01:23 AEST core start. These entities did not restore when HA came back after the 24 Sep restart and have stayed unavailable for two days: 10 lifx/Tuya lights (light.corridor_led, light.garden, light.guest_bathroom_guest_led_a, light.guest_led_b, light.kitchen_led_1..5, light.laundry_led), 7 plex media_player clients, 5 driveway entities, 9 roborock binary_sensors plus its vacuum/battery/child_lock/DND/off_peak switches (roborock is with the repairer, 2026-09-24 dated suppression stands), 2 device_tracker phones, light.garden. The shared restore timestamp is the useful signal: this is one restore-time failure class, not 38 independent faults.
lifx: reload is now exhausted. Three of the five lifx config entries sit permanently in setup_retry: Garden (01KN60MAZJYP3E73797WYTTYPF, unit 192.168.68.127), Guest LED A (01KZ8NE5WDBB91J9JQ8VFYKS04, .101), Guest LED B (01KZ8NEBCKE5S78DCWDG6F92KC, .94). Each logged 121 retries on 2026-09-25, 147 on 2026-09-24, 141 on 2026-09-23, every one the same get_hostfirmware, get_version, get_group timed out after 5 attempts. config_entry.reload was called on all three at 20:45 (HTTP 200 {"require_restart":false} for each); 12 minutes later all three were back in setup_retry with the same timeout line and all 10 light entities still unavailable. .101 and .94 answer ICMP (3/3, 0% loss) while the integration cannot complete its LIFX handshake, so this is integration or device-side state, not a dead LAN path. .127 does not answer ping at all. Next step is a full HA restart at a quiet time, or delete and re-add the three entries; both are live-service changes and were NOT done.
Self-inflicted and recovered (reported for completeness): a config_entry.reload on the tuya entry 01KKZT8SVT8TGCYWYM63GS1M45 at 20:51 timed out client-side. HA logged Setup of config entry ... for tuya integration cancelled at 20:52:19 and the entry fell to setup_error, dropping counted health to 165/250 for roughly ten minutes. A second reload at 20:54 returned HTTP 200 and the entry is loaded again with health back at 204/250. Lesson: the tuya entry takes longer than the 30 s client timeout on reload, and a client-side abort leaves the entry in a worse state than before.
Sources: live HA /api/states, /api/config/config_entries/entry, /api/config/config_entries/entry/<id>/reload, /api/history/period, ~/.homeassistant/home-assistant.log and .log.1, ~/.homeassistant/.storage/core.entity_registry, ping to 192.168.68.127/.101/.94, ~/.hermes/bin/ha-tag-status. Reading taken 2026-09-25 20:41-20:57 AEST., hermes.
2026-09-25 06:14 AEST: ha_health_check.py reported 1 health problem, one ERROR line in its 70-minute window (05:02-06:12). It is the standing low-rate Panasonic cloud family and it self-cleared; nothing is live.
2026-09-25 05:19:41.768 ERROR (MainThread) [aio_panasonic_comfort_cloud.helpers] Error in get_status, with response body{"code":4008,"message":"Bad request"}against expected HTTP 200. The same second the client logged[aio_panasonic_comfort_cloud.apiclient._ac] Failed to get live status for device CS-Z71XKRW+E011917675 switching to cached data.- The device recovered on the next poll:
sensor.panasonic_inside_temperatureread 20 withlast_updated2026-09-24T20:12:01Z (2026-09-25 06:12:01 AEST, read at 06:13-06:14), andsensor.entertainment_room_panasonic_connection_statusreadsconnectedwithconsecutive_failures0.climate.panasonicreadsheat. - Cadence for this line is unchanged and low: 1 line on 2026-09-25, 2 on 2026-09-24 (both at 07:39:29), and 3 in
home-assistant.log.1(2026-09-21 13:26:47, 2026-09-22 11:51:46, 2026-09-22 18:59:45). Matches the 4-line day recorded on 2026-09-17. No action. - No other ERROR line in the window. HA
state=RUNNINGon 2026.8.2, 675 states with 141unavailableagainst threshold 150,home-assistant.log.faultunchanged at 314022 bytes, last write 2026-08-25 00:02. - Fixed in
ha_health_check.pyat 06:20 AEST, same doctrine as the Shelly and SABnzbd gates: a newPANASONIC_TRANSIENT_MARKpluspanasonic_serving(states)drops the line only whilesensor.entertainment_room_panasonic_connection_statusreadsconnectedwithconsecutive_failures: 0. A real cloud or device outage drives that counter up and the line stays visible, and the mark is deliberately kept out ofERROR_ALLOWLISTso it can never be suppressed permanently. Verification (16/16, one ad-hoc script under$TMPDIR/hermes-verify-panasonic-gate-*.py, cleaned up after the run): healthy device suppresses;consecutive_failures: 3does not;disconnecteddoes not; missing entity does not; the mark matches the real log line but not an unrelated ipp error; the mark is absent from the allowlist; no unrelated ERROR is hidden;check()stays clean on the real live states and the CLI prints the bare[SILENT]with exit 0. Falsified: with the gate armed the window holds 0 ERROR lines, withstateswithheld the same line reappears, with a failing connection sensor injected through a stubbed HTTP layercheck()alerts again, and the CLI prints the bare[SILENT]with exit 0 and empty stderr. Unwind: deletePANASONIC_TRANSIENT_MARKand its two call sites if the cadence ever stops being single-poll. These are ad-hoc checks, not a project suite;~/.hermes/scriptshas no canonical test runner.python3 -m py_compileclean and--verbosenow reports all probes passed. - Source:
/Users/dwayne/.homeassistant/home-assistant.logandhome-assistant.log.1, live HA/api/states,/api/configand/api/history/periodreads,~/.hermes/scripts/ha_health_check.py, 2026-09-25 06:12-06:20 AEST., hermes.
2026-09-24 12:15 AEST: HA healthy on 2026.8.2. The camera sweep at 12:03 captured 17 stills with 4 non-actionable failures and no intruder, delivery or property-damage finding. Two faults need physical action.
- Driveway TC41 (
192.168.68.55) camera still frozen, 9 days now. 14 driveway entities unavailable (camera.driveway_live_view,switch.driveway, the pan/tilt buttons and numbers,sensor.driveway_signal_level,sensor.camera_vision_driveway). Network path is fine (ping 2/2, ARP on en5, TCP 443/554 open) andhomeassistant.reload_config_entryonentry_id=01M1P28GK90EW3TX966A41WACZdid not clear it, so reloads are exhausted. Next action: physical power-cycle of the TC41. - Pool camera (
camera.garden_live_view) viewpoint moved by 2026-09-24 12:03 AEST and no longer frames the pool: the still shows the rear courtyard, a ladder and a hand truck, with a neighbouring verandah. Sibling cameras unaffected; the first view shift of this kind was 2026-09-23 12:03 -> 18:03. Next action: check the Pool camera mount or pan/tilt home position. - Roborock Qrevo MaxV still off the LAN since 2026-09-18 08:41 AEST, unchanged; physical dock power and Wi-Fi check still stands.
- The hourly
ha_health_check.pySABnzbd alert was a false positive and is fixed: see the 2026-09-24 01:35 Log entry.--verbosenow reports all probes passed.
Source: live HA /api/states and /api/config, ~/.homeassistant/home-assistant.log, ~/.hermes/scripts/ha_health_check.py, 2026-09-24 01:12-01:35 AEST. - hermes.
2026-09-22 06:12 AEST: HA healthy on 2026.8.2, state=RUNNING, 677 states with 192 unavailable/unknown (14 of them the driveway camera family). Two standing faults, both unchanged in kind and both needing physical action.
- Driveway TC41 (
192.168.68.55) camera frozen for 8 days.published_driveway.jpgsha25606ce21c4is byte-identical across all 7 sweeps from 2026-09-15 06:01 to 2026-09-22 06:03, its embedded watermark reads 2026-09-14 12:49:17 PM, and it is the only sweep slug whose hash never changes. The network path is fine: ping 2/2 at 7.4 ms, ARP98:ba:5f:47:d3:79on en5, TCP 443 and 554 open. HA logsUnable to communicate with the device connect: ('Unable to query the device: 192.168.68.55: ', TimeoutError())every 600 s (246 lines in the current log, last 06:11:31).homeassistant.reload_config_entrywithentry_id=01M1P28GK90EW3TX966A41WACZreturned HTTP 200 at 06:11 and did not clear it, so reloads are exhausted. Next action: physical power-cycle of the TC41. - Roborock Qrevo MaxV still off the LAN since 2026-09-18 08:41 AEST, unchanged; physical dock power and Wi-Fi check still stands.
Source: ~/.hermes/camera_sweeps/20260922-060348/manifest.json, live HA /api/states and /api/config, ~/.homeassistant/home-assistant.log, sha256 comparison across sweeps, 2026-09-22 06:03-06:12 AEST. - hermes.
2026-09-20 07:16 AEST: ha_health_check.py reported 1 health problem: 1 ERROR line in its 70-minute window (06:06-07:16), the standing Shelly EM transient fetch failure. Not a live fault; the only other standing item, the roborock, is off the LAN for a third day.
[homeassistant.components.shelly] Error fetching Shelly EM data: An error occurred while retrieving data from Shelly EMat 06:36:59.845. Same transient family as the 09-16 to 09-18 entries; the device is healthy. Livecurl http://192.168.68.78/statusreturned HTTP 200 in 1.11 s withwifi_sta.rssi-73 dBm,cloud.connectedtrue, bothemeters[].is_validtrue (channel 1 1257.57 W, channel 2 12.55 W).ping3/3, 0.0% loss, 7.635-12.845 ms. Metering served live:sensor.shelly_em_channel_1_power_21257.18 W,sensor.shelly_em_channel_2_power12.71,sensor.shelly_em_channel_1_energy48151.9633, all last updated 2026-09-19T21:16:11Z-21:16:26Z.- The Tuya
light.lounge_led_2write rejection of the 05:13 run did not recur in this window; no other ERROR line appeared, so the run carried no second transient to mask. - Unavailable load stays under threshold: 140 of 678 entities read
unavailable(threshold 150), plus 51unknown, so 191 unavailable/unknown against the 148 recorded on 2026-09-17. Leading unavailable domains: sensor 65, button 20, binary_sensor 12, light 10, switch 8, select 5. - Roborock Qrevo MaxV remains off the LAN, unchanged from the 2026-09-18 09:30 entry:
vacuum.roborock_qrevo_maxvunavailablewithlast_changed2026-09-18T20:38:49Z,ping 192.168.68.582/2 lost, 100%. Unchanged in the last three runs; the roborock integration logs at INFO so this check cannot surface it, and the physical dock power and Wi-Fi action still stands. home-assistant.log.faultstill last changed 2026-08-25 00:02, no growth.- Shelly EM error cadence: 3 today (02:50:44, 04:51:34, 06:36:59) against 10 for the 2026-09-19 portion of the current
home-assistant.log. - Source:
/Users/dwayne/.homeassistant/home-assistant.log, livecurl http://192.168.68.78/status,ping, live HA/api/statesreads, 2026-09-20 07:16 AEST., hermes.
2026-09-18 14:13 AEST: ha_health_check.py reported 1 health problem, 2 ERROR lines in its 70-minute window (13:03-14:13). Both are the known Shelly EM transient fetch failure; the device answered live probes and metering is serving.
[homeassistant.components.shelly] Error fetching Shelly EM data: An error occurred while retrieving data from Shelly EMat 13:21:20 and 13:54:28. No other ERROR line in the window, nohome-assistant.log.faultgrowth,state=RUNNINGon 2026.8.2, unavailable count within threshold 150.- Device is healthy, not failing:
GET http://192.168.68.78/statusreturned HTTP 200 in 0.078 s withwifi_sta.rssi-70 dBm,cloud.connectedtrue,emeters[].is_validtrue (channel 1 power 507.86 W, channel 2 6.38 W).ping2/2, 0.0% loss, 11.7-23.3 ms. - Metering kept serving:
sensor.shelly_em_channel_1_power_2read 485.53 W andsensor.shelly_em_channel_2_power6.07 W at 2026-09-18T04:13:00Z. - Cadence is falling, not worsening: 5 Shelly EM fetch errors on 2026-09-18 so far, against 14 for all of 2026-09-17 and 19 for 2026-09-16. The -70 dBm link at the switchboard remains the standing hypothesis and the standing next action: physical antenna/AP check, not taken because a monitor run takes no side effects.
vacuum.roborock_qrevo_maxvis stillunavailable,last_changed2026-09-17T22:41:20Z, unchanged from the 09:30 entry. The roborock integration logs at INFO so this check does not surface it; the earlier next action (physical dock power and Wi-Fi check) still stands.- Source:
/Users/dwayne/.homeassistant/home-assistant.log, livecurl http://192.168.68.78/status,ping, live HA/api/statesreads, 2026-09-18 14:13 AEST., hermes.
2026-09-18 09:30 AEST: ha_health_check.py reported 1 health problem, 2 ERROR lines in its 70-minute window (08:02-09:12). Both are known transient families, but a separate, genuine fault was found alongside them: the Roborock Qrevo MaxV has been offline for 50 minutes.
[homeassistant.components.shelly] Error fetching Shelly EM dataat 08:12:28 and[homeassistant.core] Error executing service: <ServiceCall light.turn_on (c:01M2RSJB29:al:INUX:ntrv:NL)>forlight.lounge_led_1at 09:00:01. The Tuya chain is unchanged from the 2026-09-15 16:12 entry:adaptive_lighting/hass_utils.py:88->components/tuya/light.py->tuya_sharing/customerapi.py:119,ApiRequestException: network error:(-9999999) Remote api run unknown failed. The write landed anyway: all five adaptive-lighting targets (light.lounge_led_1/_3/_4/_5,light.entrance_led) readon, brightness 255,color_temp_kelvin6500,last_updated2026-09-17T21:36:47Z.- New fault, not in the ERROR window because the roborock integration logs at INFO:
vacuum.roborock_qrevo_maxvwentunavailableat 2026-09-17T22:41:20Z (2026-09-18 08:41 AEST) and is still unavailable at 09:27. 52 of 53 roborock entities are unavailable. - The device is off the LAN.
arp -anmaps 192.168.68.58 (itsnetworkInfo.ipin.storage/roborock/01J7JYDCYG5RBH45VAS65M4C90, MACb0:4a:39:b0:5f:07) to(incomplete), andping -c 2 -t 2 192.168.68.58returns "Host is down", 100% loss, against 44 populated ARP entries onen5. A /24 sweep found no ARP entry for the MAC. - The cloud side is up, so the stall is local transport only:
roborock.mqtt.roborock_sessiondisconnects and reconnects cleanly (09:17:14 -> 09:17:26 "MQTT Session connected"), the last "Connected to local channel successfully" was 08:28:22, androborock.devices.devicethen loggedFailed to connect (retry N): Command timed out after 10.0sfrom 08:44:04 onward, backoff climbing to 576.65 before resetting and climbing again (latest 09:27:38, retry 22.5). - This is the residual the 2026-09-16 11:42 sentinel entry predicted: every prior unavailable span in the 09-14 to 09-17 history lasted under 3 minutes 7 seconds, so a sustained stall is a real integration fault, not a map-update gap.
- Metering kept serving through both transient lines:
sensor.shelly_em_channel_1_power_2read 1483.12 W at 2026-09-17T23:27:47Z, and/api/historyfor the entity holds 7319 points across the last 6 hours with a maximum gap of 62.6 s. - HA healthy:
state=RUNNINGon 2026.8.2. Unavailable count 139-145 of 684 against threshold 150, no breach but the roborock family is 52 of that count.sensor.colima_memory_*flickered unavailable at 2026-09-17T23:11:03Z and recovered on the 09:21:13 publish;collector.logholds 28TimeoutExpiredoncolima ssh ... cat /proc/meminfobut the run completed. - Next useful action, not taken because a monitor run takes no side effects: physical check of the Qrevo MaxV's dock power and Wi-Fi (its last known SSID is
MyRepublic DCAF, same band as the rest of the 192.168.68 /24). - Source:
/Users/dwayne/.homeassistant/home-assistant.log,.storage/roborock/01J7JYDCYG5RBH45VAS65M4C90,colima_monitor/collector.log, live HA/api/states,/api/configand/api/history/periodreads,arp -an,ping, 2026-09-18 09:12-09:30 AEST., hermes.
2026-09-18 07:20 AEST: ha_health_check.py reported 1 health problem, 6 ERROR lines in its 70-minute window (06:02-07:12). Five are a single Tuya write rejection and one is an ipp poll timeout that recovered in 60 seconds. No fault is live.
- All five
[homeassistant.core] Error executing servicelines fire inside 07:00:00.556-07:00:00.804 and carry contextc:01M2RJPK75:al:INUX:ntrv:NA, one adaptive-lighting group write tolight.lounge_led_3,light.entrance_led,light.lounge_led_1,light.lounge_led_5andlight.lounge_led_4atbrightness=255, color_temp_kelvin=6025. Each traceback endstuya_sharing.exceptions.ApiRequestException: network error:(-9999999) Remote api run unknown failedattuya_sharing/customerapi.py:119. Same chain as the 2026-09-15 16:12 entry. - The writes landed anyway. Live HA at 07:15 reads all five lights
onatbrightness 255,color_temp_kelvin 6234,last_updated 2026-09-17T21:15:19-21:15:20Z. - Tuya service-error count by day across
home-assistant.logplus.1: 5 on 09-14, 5 on 09-15, 2 on 09-16, 26 on 09-17, 5 today by 07:12. The 09-17 figure is the outlier and 24 of it sits in one 08:58-09:00 burst of 8 contexts x 3 retried calls. Today's 5 match the 09-14 and 09-15 baseline. - The ipp line at 06:10:58.892,
Error fetching ipp data: Invalid response from API: Timeout occurred while connecting to IPP server., pairs withFetching ipp data recoveredat 06:11:58.945. Same slow-poll shape recorded on 2026-09-17 18:20. - HA is healthy:
state=RUNNINGon 2026.8.2, 87 of 684 entities unavailable against threshold 150. Last ERROR before the window was a lifxPergola LED (192.168.68.120)timeout at 04:00:54. - Standing next action, unchanged and not taken because a monitor run takes no side effects: physical Wi-Fi check of the Pergola LED pair and a DHCP reservation for the guest LED pair.
- Source:
/Users/dwayne/.homeassistant/home-assistant.logandhome-assistant.log.1, live HA/api/statesand/api/configreads, 2026-09-18 07:12-07:20 AEST., hermes.
2026-09-17 18:20 AEST: the Pergola LED lifx flapper has moved from 192.168.68.122 to 192.168.68.120 inside the same pair. ha_health_check.py reported 5 ERROR lines in the 70-minute window ending 18:10, four of them .120 lifx timeouts (17:41:23, 17:45:53, 18:06:55, 18:09:13) plus one ipp.coordinator line at 17:49:38; all five recovered on the next poll (lifx 17:41:53, 17:46:03, 18:07:22, 18:09:40; ipp 17:50:44).
- Address-to-unit mapping from
core.config_entries: entry01KVWWKX2AAVEWBDN388QR7VXRholdshost: 192.168.68.120(MACd0:73:d5:76:5e:2c); entry01KYAYAXA691M1NB2CRRD4JWCAholdshost: 192.168.68.122(MACd0:73:d5:76:8d:67). Both entries carry the title "Pergola LED" and the log line carries only the IP, so the alert text cannot tell the two apart. - Daily lifx timeout counts:
.1229 on 09-15, 42 on 09-16, 8 on 09-17 (last 15:57:29);.1201 on 09-15, 0 on 09-16, 6 on 09-17. The heavy load moved between the two addresses. - Live host pings 2026-09-17 18:15-18:20 AEST:
.120lost 23 of 60 packets (61.7% loss, round-trip min/avg/max 34.381/217.535/418.064 ms) across three runs;.122lost 0 of 10 on both runs;.121(Garden),.94,.111and the gateway all answered 10 of 10..92has no ARP entry (incompleteon en5) and is unreachable. ARP maps.120tod0:73:d5:76:5e:2dand.122tod0:73:d5:76:8d:68, one above each device unique_id MAC. - Entities match a slow-poll fault, not a dead unit:
light.pergola_ledandlight.pergola_led_2both readoff;button.pergola_led_restartandbutton.pergola_led_identifyreadunknown. - HA healthy:
state=RUNNINGon 2026.8.2, 90 of 684 entities unavailable against threshold 150. - Next useful action, not taken because a monitor run takes no side effects: physical check of both Pergola LED units' Wi-Fi (same AP path) and a DHCP reservation for the pair.
2026-09-17 16:13 AEST: ha_health_check.py reported 8 ERROR lines in its 70-minute window (15:02-16:13). Seven belong to known transient families; the eighth is a python-kasa KLAP 403 pair traced here for the first time, and its impact is nil because every entity on those plugs is disabled.
- Window lines in order:
shellyfetch error 15:57:00; lifxTimeout fetching Pergola LED (192.168.68.122)15:57:29;ipp.coordinator15:59:30 and 16:11:45, both paired withFetching ipp data recoveredat 16:00:31 and 16:12:46;aio_panasonic_comfort_cloud.helpers Error in get_status15:59:54;[homeassistant] Error doing job: Task exception was never retrieved16:07:17;kasa.transports.klaptransport403 on 192.168.68.67 at 16:08:27 and on 192.168.68.68 at 16:11:49. - New family, first traced here:
Query failed after successful authentication ... Response status is 403. Upstream explanation is python-kasa issue #784: a stale KLAP handshake session returns 403 on send, a retry succeeds, and HA does not enter its reauth flow. Daily counts: 3 on 2026-09-12 (first line 14:29:03), 4 on 09-13, 2 on 09-14, 1 on 09-15, 3 on 09-16, 7 on 09-17. Source for the upstream behaviour: https://github.com/python-kasa/python-kasa/issues/784. - Impact is nil on the current registry. All 20 entities on each of
Washing Macine P110(192.168.68.66),Heat pump dryer P110(192.168.68.67),Data centre P110M(192.168.68.109) andData centre 2 P110(192.168.68.68) carrydisabled_by: user, so no state depends on those polls. The integration still polls the devices, which is why the lines keep appearing.Meir Towel heater P110M(192.168.68.110) has 15 of its 20 entities enabled and logged no 403. - The 16:07:17
Task exceptionis the camera haffmpeg pattern already recorded on 2026-09-15 02:20:haffmpeg Timeout reading imageat 16:07:17.738,Timeout while waiting of FFmpegat .739, thenRuntimeError: read() called while another coroutine is already waiting for incoming datainasyncio.subprocess.communicate.components.tplink.entityloggedUnable to read data for <DeviceType.Camera at 192.168.68.61 - Pergolux (C510W)>22 seconds earlier, at 16:06:22. Daily counts: 2 today (10:35:07, 16:07:17) against 5 on 09-12, 11 on 09-14, 14 on 09-15, 1 on 09-16. - Metering kept serving.
sensor.shelly_em_channel_1_power_2read 1659.72 W at 2026-09-17T06:20:18Z and/api/historyholds 3390 points from 2026-09-16T19:10:00Z with no gap. Today's shelly fetch-error count is 9 (05:31:31, 06:30:38, 06:33:23, 08:57:43, 11:15:54, 11:49:13, 14:12:48, 14:41:05, 15:57:00), against 19 for the whole of 09-16. - Pergola LED eased: 10 timeouts today against 42 on 09-16.
ping -c 3 -i 0.3 192.168.68.122at 0% loss, round-trip min/avg/max 32.933/70.559/99.867 ms.light.pergola_ledreadsoff. - Unchanged knowns: HA
state=RUNNINGon 2026.8.2 with 75 of 684 entities unavailable against threshold 150; Driveway TC41 still logsnot ready yet(97 lines today, last at 16:14:50) with all 15 of its entities unavailable since 2026-09-14T23:29:28; PanasonicError in get_status4 lines today (00:21:55, 02:36:55, 07:59:25, 15:59:54), device CS-Z71XKRW+E011917675 switches to cached data,climate.panasonicreadsoff. - Next useful action, unchanged and not taken because a monitor run takes no side effects: physical power-cycle of the Driveway TC41. The kasa 403 lines need no action while those four entries stay disabled; if any of their entities are re-enabled, confirm the retry-on-403 behaviour still holds before treating populated states as reliable.
- Source:
/Users/dwayne/.homeassistant/home-assistant.logandhome-assistant.log.1,.storage/core.config_entriesand.storage/core.entity_registry, live HA/api/states,/api/configand/api/historyreads,pingprobes, 2026-09-17 16:13-16:21 AEST., hermes.
2026-09-17 06:12 AEST: ha_health_check.py reported 1 ERROR line in its 70-minute window, [homeassistant.components.shelly] Error fetching Shelly EM data at 05:31:31. Same transient fetch pattern as the recent entries; metering did not stop.
- Live checks at 06:12-06:15 AEST:
GET http://192.168.68.78/statusreturned HTTP 200 in 0.034 s,wifi_sta.rssi-69 dBm,cloud.connectedtrue, bothemeters[].is_validtrue with power 3732.43 W and 2391.89 W. ping -c 10 -i 0.4 192.168.68.78returned 9 of 10 replies, 10.0% loss, round-trip min/avg/max/stddev 7.138/19.752/84.173/23.191 ms. Weak but usable link, unchanged from the 2026-09-16 reading.- Metering served without a gap:
/api/historyforsensor.shelly_em_channel_1_power_2holds 4214 points from 2026-09-16T08:30:00Z to 2026-09-16T20:13:30Z. The entity read 3657.07 W withlast_changed2026-09-16T20:13:37Z. - Daily counts for this error line: 5 on 2026-09-15, 19 on 2026-09-16, 1 on 2026-09-17 by 05:31. The 09-16 total is 6 above the 13 recorded at 17:13 that day, so the afternoon rate stayed elevated.
- Next useful action, unchanged and still not taken because a monitor run takes no side effects: physical check of the Shelly EM antenna position at the switchboard, or an AP/relocation check, to fix the -69 dBm link.
- Source:
/Users/dwayne/.homeassistant/home-assistant.log, live HA/api/statesand/api/historyreads,GET http://192.168.68.78/status,pingprobe 2026-09-17 06:12-06:15 AEST., hermes.
2026-09-16 17:13 AEST: ha_health_check.py reported 2 ERROR lines in its 70-minute window, both [homeassistant.components.shelly] Error fetching Shelly EM data at 16:07:51 and 17:11:29. Both are transient fetch failures, not a device fault: metering kept serving through both.
- The Shelly EM (192.168.68.78, entry
01KZ8EP8PF9JQAT3MV0EY3ZD2N, serialBCFF4DFD00E9) answeredGET /statuswith HTTP 200 five times in a row at 17:15-17:16 AEST, 0.045-0.619 s each; one earlier probe timed out at 8 s.wifi_sta.rssi-70 dBm,cloud.connectedtrue, bothemeters[].is_validtrue. ping -c 10 -i 0.4 192.168.68.78returned 9 of 10 replies, 10.0% loss, round-trip min/avg/max 8.158/16.766/38.039 ms, so the unit is on the LAN with a weak but usable link.- Metering never stopped:
sensor.shelly_em_channel_1_power_2read 3757.22 W at 17:13 AEST (last_changed 2026-09-16T07:13:26Z), and/api/historyfor the entity holds 695 points from 06:00Z to 07:17Z with no gap. Channel 2 read 1026.55 W andsensor.shelly_em_channel_2_energy18419.6476 kWh at the same read. - No
Error fetching Shelly EM dataline pairs with arecoveredline in this log, unlike the lifx and ipp coordinators. The integration logs the failure only; the entity values resume on the next successful poll. - Today's count is 13 through 17:11 (07:25:46, 07:36:43, 09:11:27, six between 12:27:28 and 12:36:00, 12:56:43, 12:57:27, 16:07:51, 17:11:29). The 09:13 entry below recorded 3 for the day; the afternoon added 10, with a six-line burst inside 12:27-12:36. Prior days for comparison: 17 on 2026-09-12, 4 on 2026-09-13, 22 on 2026-09-14, 5 on 2026-09-15.
- Sensor entities added by the integration are absent, not broken:
sensor.shelly_em_signal_strength,sensor.shelly_em_uptime,update.shelly_em_firmwareall return no state from/api/states, whilesensor.shelly_em_channel_1_energy_2is likewise missing. The core emeter entities are all present. - House state at 17:13-17:16 AEST: HA
state=RUNNING(2026.8.2), 109 of 703 entities unavailable against threshold 150, no other ERROR family in the window. The 17:00:16-17:00:31 radarr/sonarrCannot connect to host localhostlines sit in the same window but are the documented 05:00-24:00 *arr down-window noise and the script filtered them correctly. - Next useful action, for a later working session: the unit's rssi of -70 dBm and the recurring coordinator fetch failures on a 13-per-day cadence point at Wi-Fi coverage at the switchboard rather than a dead device. A physical check of the Shelly EM antenna position, or an AP/relocation check, is the useful step. Not taken here because a monitor run takes no side effects.
- Superseded claim: the 09:13 entry below reads "the third today, down from 20-plus on 2026-09-14". The first half held at 09:13 only; the day closed the afternoon at 13 and the rate climbed back.
- Source:
/Users/dwayne/.homeassistant/home-assistant.log, live HA/api/statesand/api/historyreads,GET http://192.168.68.78/status,pingprobes 2026-09-16 17:13-17:17 AEST., hermes.
2026-09-16 09:13 AEST: the hourly health window still returns 6 ERROR lines, the same count as the 04:16 and 00:35 runs, but the mix changed. Only two of the six are Pergola LED (192.168.68.122); the guest LED pair is back in the window.
- Lines in the 70-minute window ending 09:13: lifx timeouts at 08:29:37 for
Guest LED A (192.168.68.92), 08:29:43 forGuest LED B (192.168.68.101), 08:48:30 and 09:12:09 forPergola LED (192.168.68.122);ipp.coordinatorat 08:34:20;shellyat 09:11:27. Four lifx, one ipp, one shelly. 192.168.68.122now answersping -c 10 -i 0.4with 10 of 10 replies, 0.0% loss, round-trip avg 161.836 ms, max 427.216 ms, so the Wi-Fi fault recorded at 04:16 has eased since then. Its timeout count for 2026-09-16 is 30, against 10 for the whole of 2026-09-15, so the day is still running above yesterday.- Superseded claim: the 04:16 entry named
.122as the single failing unit. That is no longer the only one. Guest LED A(192.168.68.92) andGuest LED B(192.168.68.101) each return 0 of 5 pings, andarp -nshows(incomplete)for both, so no MAC resolves. Both entities readunavailable. Their recorded addresses moved again: on 2026-09-15 the pair held.101(Guest LED A) and.94(Guest LED B). DHCP lease churn stays the better explanation than dead hardware, and a reservation or power cycle is still the unresolved fix from 2026-09-15 10:15.- The ipp printer timeout recovers on the next poll: 08:34:20 error, 08:35:20
Fetching ipp data recovered.sensor.canon_ts8300_seriesreadsidleatipp://192.168.68.111/ipp/print. This is the sixth ipp line today, all paired with a recovery. Shelly EMfetch error at 09:11:27 is the third today (07:25:46, 07:36:43, 09:11:27), down from 20-plus on 2026-09-14. Metering kept serving:sensor.shelly_em_channel_1_power1409.24 W at the same read.- Next useful action for a later working session, not taken here because a monitor run takes no side effects: physical check of the guest LED pair and a check of
Pergola LED (192.168.68.122)signal strength while its loss rate is low. - Source:
/Users/dwayne/.homeassistant/home-assistant.log, live HA state reads,pingandarpprobes 2026-09-16 09:12-09:14 AEST., hermes.
2026-09-16 04:16 AEST: the Pergola LED (192.168.68.122) lifx timeouts that ha_health_check.py keeps surfacing are a failing Wi-Fi link on the unit itself, and the rate is climbing. Same 70-minute-window alert shape as 00:35, different cause.
- Link evidence at 04:12-04:17 AEST:
ping -c 30 -i 1 192.168.68.122returned 13 of 30 replies, 56.7% loss, round-trip avg 185.240 ms, max 520.202 ms. A second run-c 20 -i 0.3returned 6 of 20, 70.0% loss, avg 1209.567 ms, max 2655.017 ms (2 replies past wait). A-c 2probe at 04:12:21 returned 2 of 2 at 194 ms average, so the loss is intermittent and bursty, not a dead interface.arp -n 192.168.68.122resolves to MACd0:73:d5:76:8d:68onen5, one digit from the entry'sunique_idd0:73:d5:76:8d:67, so the replies are the light itself and not a DHCP takeover. - Peers on the same LAN segment are clean at the same moment:
192.168.68.120(the otherPergola LED, config entry01KVWWKX2AAVEWBDN388QR7VXR) and192.168.68.123each returned 10 of 10, 0% loss..120logged only 3 lifx timeouts in the same window pair;.122logged 34. - Timeout trend across
home-assistant.logplus.1(34 lines total for.122): 1 on 2026-09-12, 1 on 2026-09-13, 11 on 2026-09-14, 11 on 2026-09-15, 14 on 2026-09-16 by 04:16. The 09-16 figure covers about 4.3 hours, so the hourly rate roughly quadrupled. HA recovers every time: 41Fetching Pergola LED (192.168.68.122) data recoveredlines, pairing each timeout within 10 s to 4 minutes. - Superseded claim: the 2026-09-16 00:35 entry above recorded "the IP answers ping 0% loss" and called this a slow-poll fault. That reading does not hold now.
light.pergola_led(entry01KYAYAXA691M1NB2CRRD4JWCA) flaps betweenoffandunavailable; at 04:12:37 AEST it readoffwithlast_changed2026-09-15T18:12:37.332406+00:00, exactly matching that minute's recovery line. ha_health_check.pyreported exactly 4 ERROR lines in its 70-minute window, all four the.122lifx timeout, with no other ERROR family present. HA itself is healthy.- Next useful action: physical check of the Pergola LED at 192.168.68.122 (Wi-Fi signal, obstruction, distance to AP). HA exposes
button.pergola_led_restarton entry01KYAYAXA691M1NB2CRRD4JWCAas a remote power-cycle; at 04:16 AEST it readunknown(last_changed 2026-09-15T18:15:59.705575+00:00) whilebutton.pergola_led_identifyreadunavailable, so the button surface is not reliably reachable either. Not pressed here, because a monitor run takes no side effects. - Source:
/Users/dwayne/.homeassistant/home-assistant.logand.1,.storage/core.config_entries,.storage/core.entity_registry, live HA state reads,pingandarpprobes 2026-09-16 04:12-04:17 AEST., hermes.
2026-09-16 00:35 AEST: fixed the midnight-boundary false alarm in ha_health_check.py, the third filter bug of the same class in a week. The 00:13 run reported 10 ERROR lines; 6 were real and 4 were schedule noise the filter was written to drop.
- Root cause:
arr_down_window_noise()was applied bycheck()against the run hour (datetime.now().hour), not each log line's own timestamp. The *arr containers stop at 05:00 and start at 00:00, so the 23:11-23:51 SABnzbd connection errors sit in the 70-minute window of a 00:13 run while the run hour reads 0, outside the5 <= hour < 24guard. Observed lines: SABnzbd connections at 23:11, 23:21, 23:31, 23:41, 23:51 (five), plus[homeassistant.components.sabnzbd.coordinator] Error fetching SABnzbd data: Error while fetching dataat 00:05:43 after the 00:00 start. - Fix 1: the window test moved into
arr_down_window_noise(line, when), judged on the parsed log-line timestamp. Callers passwhenfromrecent_error_lines(). - Fix 2: new
ARR_BOOT_GRACE_MIN = 20covers the 00:00 start pass. SABnzbd's API answered at 00:06 (containerUp 13 minutesat 00:13,/api?mode=versionreturned5.1.2, root/returned HTTP 200) and its coordinator loggedrecoveredat 00:06:13, so a poll landing in the first minutes is startup, not a fault. Inside 00:00-05:00 and outside the grace the containers must serve, so the same marks still alert. - Rejected fix, caught by an ad-hoc check before delivery: adding
Unexpected error updating listenertoARR_BY_DESIGN_MARKS. That line is HA's own lidarr sensor crashing (components/lidarr/sensor.py:29 get_space,AttributeError: 'LidarrRootFolder' object has no attribute 'freeSpace') because the sensor reads rootfolder data before the API fillsfreeSpace. It happens to coincide with an *arr start (2026-09-13 19:50:17 manual, 2026-09-16 00:05:46 scheduled), but the mark would have hidden a real HA component fault for the whole 05:00-00:00 down window, not just at boot. It still alerts in every window. A separate ad-hoc check asserting that direction failed on the first build, which is how the over-suppression surfaced. - What alerts after the fix, all genuine: 3
lifxtimeouts forPergola LED (192.168.68.122)at 23:51:25 and 23:56:09 plus one earlier, and the 00:05:46 lidarr listener crash.light.pergola_ledreadsoffand the IP answers ping 0% loss, so it is a slow-poll fault, not a dead unit; the siblingsGarden(192.168.68.127),Guest LED A(.101) andGuest LED B(.94) do not answer ping at all and situnavailable. - Verification:
~/.hermes/tests/test_ha_health_check.pygrew 15 -> 21 tests, all passing underhermes-agent/venv/bin/python -m pytest(21 passed in 0.17s). Falsified: 9 of 21 fail against a reconstructed pre-fix build (hermes-verify-old-ha.py, 12,312 chars vs the 14,036 new), includingtest_shutdown_noise_is_dropped_end_to_end. Live run at 00:34 returns 4 lines, not 10. - Script sha256
782066581f1a2e588521776373d6420ff515b2cd69454c670b1fa3f124ecca4a; test sha2562e6546648a9a5104d7d0dd7ce5b02fc136e8e11082dec88d036b2cb6288b538e. Pre-fix copy of the 09-16 00:20 intermediate build at~/.hermes/backups/ha-health/ha_health_check.py.20260916-002006.bak. Both paths are gitignored (.gitignore:2:*), so this entry is the durable record. - Note for the sibling filter:
hermes_home_integration_sentinel.pyschedule_noise_free_log()carries the same run-hour shape, so it will mislabel the first tick after 00:00 the same way. Not changed here; the hourly checker was the alerting surface. - House state at 00:22: HA RUNNING, 78 of 703 entities unavailable (threshold 150, baseline 106 recorded Aug 2026), so no breach. All six *arr containers up 13 minutes with every published port answering; Plex up 15 hours on 32400.
2026-09-15 16:12 AEST: the light.turn_on service error that ha_health_check.py keeps surfacing is the Tuya cloud rejecting the write, not a local HA fault. Traced the 2026-09-15 15:10:00.906 line end to end: adaptive_lighting/hass_utils.py:88 service proxy, then components/light/__init__.py:521, then components/tuya/light.py:530 async_turn_on, then tuya_sharing/device.py:213 send_commands -> tuya_sharing/customerapi.py:119, raising tuya_sharing.exceptions.ApiRequestException: network error:(-9999999) Remote api run unknown failed.
- Recurrence is sporadic, not continuous: 5 occurrences on 2026-09-14 at 11:11:47 (
light.lounge_led_6), 11:35:31 (light.lounge_led_3), 13:31:06 (light.lounge_led_3), 13:52:04 (light.lounge_led_6), 15:20:02 (light.lounge_led_6), and 1 on 2026-09-15 at 15:10:00 (light.lounge_led_1). All are Tuya cloud lights in the lounge group. - The device recovers on its own.
light.lounge_led_1read stateon, brightness 255, color_temp_kelvin 6385 at 16:12 AEST after the 15:10 failure, so the command landed late or the next command succeeded. No unavailable entity. - Distinguish from the neighbouring error families in the same log: the 38
light.gardenservice errors arelifxTimeoutErroronLight.set_power, a separate LAN-transport fault, and thelight.guest_led_b/light.guest_bathroom_guest_led_aunavailability is the DHCP-lease churn noted above. Only the 6 lines above carry the Tuya cloud mark. - Unavailable entities 107 of 703, threshold 150, no breach. HA state RUNNING,
Starting Home Assistant 2026.8.2at 09:29:28./System/Volumes/Dataat 93% with 15 GiB free. - No durable local fix available: the Tuya cloud API is the failing hop, and the
tuyaintegration is cloud-only here (the locallocaltuyaentry is inert). Treat as upstream flakiness unless the frequency climbs.
2026-09-15 10:15 AEST: fixed three silent filter bugs in ha_health_check.py that made the hourly job report 18 ERROR lines when only 2 were real. All three were the same class: a filter written to match a shape HA never emits, so it matched nothing and never errored.
- SABnzbd: the integration logs its coordinator failures under
[homeassistant.core], not[homeassistant.components.sabnzbd].arr_down_window_noise()gated onARR_BY_DESIGN_COMPONENTSfirst, soSABNZBD_BY_DESIGN_MARKcould never be reached. 11 lines per run were documented 05:00-24:00 downtime noise. Now the mark matches on its own; it is unique to SABnzbd. - Allowlist bracket match:
any(f"[{a}]" in line ...)required a literal closing bracket, so[homeassistant.components.homekit.util]and[homeassistant.components.automation.cts_floor_drying_schedule]never matched the entrieshomeassistant.components.homekit/homeassistant.components.automation. Replaced withallowlisted(), a prefix match on[{a}. - Cost of the bugs: 2 HomeKit lines alerted every hour and 3
automation.cts_floor_drying_schedulelines (10:00:00 on 2026-09-15 and 2026-09-14) alerted on a tag the filter was written to drop. - Verified:
~/.hermes/tests/test_ha_health_check.pygrew 9 -> 15 tests, all passing underhermes-agent/venv/bin/python -m pytest. Falsified against a reconstructed pre-patch script (12,154 chars vs 12,599 new): 4 of the 6 new tests fail on it (test_sabnzbd_core_line_is_suppressed_in_down_window,test_allowlist_matches_log_tag_as_prefix,test_allowlist_keeps_real_faults,test_end_to_end_keeps_only_real_faults); the other 2 assert the not-suppressed direction and pass on both builds, as intended. Live run now reports 2 lines, not 18. - Script sha256
31bd0346ff6baee2b06d6e128e4860bffe978a54b8f344c6aac9946e81f490ac; test sha256ff7780a93ce37fb202d6e2c649da522600ba3f56386670583ff07ca57fff5058. Both paths are gitignored (.gitignore:2:*), so this entry is the durable record. - The remaining 2 lines are a real fault, not noise:
light.guest_led_b(192.168.68.94) andlight.guest_bathroom_guest_led_a(192.168.68.101) timed out at 09:58 and stay unavailable. Same pair failed 2026-09-14 18:46 and 22:46 and 2026-09-15 06:32, and their addresses move between runs (Guest LED A at 192.168.68.127, then .125, now .101), which points at DHCP lease churn rather than dead hardware. Fix is a DHCP reservation or a power cycle. - Unavailable entities 109 of 703, threshold 150, no breach. HA state RUNNING.
2026-09-13: ha_health_check.py no longer alerts on localtuya's malformed-UDP-packet callback error. custom_components/localtuya/discovery.py datagram_received slices an arbitrary broadcast, decrypts it, and on failure falls back to data.decode(); a malformed packet raises ValueError: The length of the provided data is not a multiple of the block length then UnicodeDecodeError: 'utf-8' codec can't decode byte 0xac, which HA logs as a generic job exception. Observed exactly twice: 2026-09-12 16:11:08 and 2026-09-13 08:22:36 AEST.
- The trap: HA logs the generic header
Error doing job: Exception in callback _SelectorDatagramTransport._read_ready() (task: None)with[homeassistant], not the component name. A filter keyed onlocaltuyacan never match the collected line, because the collector stores only the header. Verified: of 399 collected ERROR lines, zero mention localtuya. - New
localtuya_bad_packet_noise(line, context)requires the datagram header and all three traceback marks (custom_components/localtuya/discovery.py, the block-length ValueError, and UnicodeDecodeError).recent_error_lines()now keeps the following non-timestamped lines as context so the component can be identified. A datagram error from any other component, any other localtuya error, and a bare header with no traceback all still alert. - Verified safe to suppress: the localtuya config entry has
devices: {}andno_cloud: true, the device registry holds 0 localtuya devices, and the entity registry holds 0 entities on thelocaltuyaplatform. The 65 tuya-family entities are all on the separate cloudtuyaplatform. There is no entity path for this handler to affect. - Proven on the live log with the window widened to 30 days: old code collected both lines, new code collects zero. Exactly 2 lines removed, no collateral suppression (399 to 397),
--verboseand--simulate-breachstill behave correctly. - Regression test
~/.hermes/tests/test_ha_health_check.py, 9 tests. Falsified: 8 of 9 fail against the reconstructed pre-patch script (/tmp/falsify/old_ha_health_check.py, 9,813 chars vs 11,899 new). - Script sha256
bde0e56f3a5617a6c8f271805a59da79c788f41a5c8c2b7f8bb0f1bbf2f0e7ab; test sha25666d5f355d63571d7af62839b79e5bb30e5db8f4aa49281c5f0305d316130e3cd. Both paths are gitignored (.gitignore:2:*), so this entry is the durable record. - Note: patching the HACS component itself is not durable, since a component update overwrites it. The health-check filter is the durable fix for the alert; the underlying bad-packet handling in
discovery.pyremains unfixed upstream.
2026-09-12 18:15 AEST: ha_health_check.py no longer alerts on *arr integration retries. The 17:00 AEST launchd stop pass (com.dwayne.arr-schedule apply, 1/0 exit) leaves all six containers down until 00:00, and HA then logs one connection error per integration per poll. The hourly checker only filtered SABnzbd's "Connection to SABnzbd API failed" wording, so the 17:21:35 radarr line (Request exception for 'http://localhost:17878/api/v3/calendar' ... Cannot connect to host localhost:17878) raised a false alarm.
- New
arr_down_window_noise()drops connection and timeout errors for the six *arr components (radarr, sonarr, lidarr, sabnzbd, overseerr, prowlarr) inside 05:00-00:00 AEST, matching three wordings: "Connection to SABnzbd API failed", "Error while fetching data", "Cannot connect to host localhost", "Request timeout for 'http://localhost". - A non-downtime failure from the same components still alerts:
HTTP 401 Unauthorizedandinvalid API keyboth survive the filter (verified by direct call). - Verified against the live log: the single in-window line at 17:21:35 is filtered, leaving zero, so the script printed
[SILENT].--verboseand--simulate-breachpaths still print correctly. - Script sha256
6f9187a20525210e0d251f7768f2a50edc17ee706023c67058e8b55676c8c8ad. Copy at~/.hermes/backups/ha_health_check.py.20260912-181556.bak.
2026-09-12 17:14 AEST: the recurring network_attention card on sensor.camera_vision_hub_max is closed and the sentinel is clean (result noop, before 0 / after 0). Two structural fixes landed in hermes_home_integration_sentinel.py:
network_indicatednow evaluates a schedule-filtered log tail.schedule_noise_free_log()dropslocalhost:17878/localhost:18989/localhost:18686connection errors during 05:00-00:00 AEST, when the *arr containers are scheduled down bycom.dwayne.arr-scheduleand their HA integrations retry every poll. Before this, 194 matching lines sat in the 500-line tail and every tick was labellednetwork_attentionwith a bogus "repair the network path" instruction. A real non-*arr host fault still sets the flag (verified with a syntheticCannot connect to host 10.0.0.5line, which survives the filter).bad_attention_entities()now excludessensor.camera_vision_*outright. These are script-published analysis derivatives, not integration health. The camera entity is the authoritative check, already routed throughHEALTH_ALIASES.- Hub Max stream recovered on its own:
ha_camera_vision_analysis.py --camera hub_maxat 17:08:17 AEST returned ok,sensor.camera_vision_hub_maxstateActive(description "Empty room with a white paneled ceiling and large glass windows showing an overcast backyard and neighboring houses."),vision_analysis.jsonstatuscomplete. Backup of the pre-fix script:~/.hermes/scripts/hermes_home_integration_sentinel.py.bak-pre-netfilter-20260912-171238. - Residual:
sensor.camera_vision_hub_maxremains motion-triggered only, so it will still read stale betweenevent.lounge_display_motionevents. That no longer pages. Confirmed--forcerun at 17:14:38 AEST wrotenoopto~/.hermes/ops/state/home_integration_sentinel.json. Source: sentinel source, live HA state read,/Users/dwayne/.homeassistant/www/camera-stills/,/Users/dwayne/ARR/arr-schedule.log, 2026-09-12.
superseded 2026-09-12 17:14 AEST: 2026-09-12 09:32 AEST: the Hub Max recovery of 2026-09-11 did not hold. hermes_home_integration_sentinel.py reports network_attention, before 1 / after 1 / resolved 0 / actions 0, still affected sensor.camera_vision_hub_max. House status at 09:32: 561/689 entities available, 128 unavailable/unknown, Power 3701W (sensor.shelly_em_channel_1_power_2), climate heat with room 23C against a 22C target. The fault is the Nest Hub Max device, not the HA container or Colima path that the sentinel's "Next" line points at:
Superseded detail from that 09:32 reading:
camera.lounge_displayreports statestreaming, but/api/camera_proxy/camera.lounge_displayreturns the 2689-byte 640x480 greyscale Nest placeholder PNG (sha25639b5d8fd9977db379fcceef2398d89b17e3ece8c0a2dd6e6b8292761b330e75e), byte-identical to the stored placeholder.hub_max.41405.[redacted-id].candidate.jpg. No real frame reaches HA.hub_max.jpgis stale from 2026-09-11 20:17 AEST;sensor.camera_vision_hub_maxlast changed 2026-09-11T20:07:08Z and readsAnalysis unavailable: no fresh camera frame (within 5 minutes).media_player.kitchen_displayunavailable since 2026-09-11T23:04:00Z (09:04 AEST 2026-09-12). Ping to 192.168.68.79 is 100% loss (3/3); ARP still showse8:ea:6a:82:d7:1con en5.- Manual recovery attempt at 2026-09-12 09:37 AEST (
ha_camera_vision_analysis.py --camera hub_max) returnedunavailable, unlike the 2026-09-11 20:08 attempt that succeeded once.sensor.camera_vision_summaryreads6/7 cameras analyzed at 09:24.
Only a manual power-cycle of the Hub Max restores the stream; the sensor self-clears on the next successful sweep. The sentinel suggests no action of its own (actions 0) because the entity is unmapped with no config entry to reload. Host disk pressure that was aborting sweeps has eased: /System/Volumes/Data is at 86% with 30 GiB free, versus 98% with 4.7 GiB on 2026-09-11. Source: ~/.hermes/ops/state/home_integration_sentinel.json, live HA state and camera proxy read, ping/arp, ~/.homeassistant/www/camera-stills/, df -h, 2026-09-12.
2026-09-11 20:13 AEST: sensor.camera_vision_hub_max is recovered and the sentinel is clean. hermes_home_integration_sentinel.py --force returned noop, before_bad [], after_bad [], house 529/686 entities available, 157 unavailable/unknown. Recovery action: ran ha_camera_vision_analysis.py --camera hub_max at 20:08:59 AEST, which returned ok and set the sensor to Active; sensor.camera_vision_summary then read 7/7 cameras analyzed at 20:08. Source: live HA state read, /Users/dwayne/.homeassistant/camera-vision.log, /Users/dwayne/.hermes/ops/state/home_integration_sentinel.json.
Two structural faults behind that incident, both verified 2026-09-11:
- The sentinel's
network_attentionlabel was a false signal.NETWORK_FAULT_PATTERNSmatchedhomeassistant.components.radarrand.sonarrlines at 17:00-17:02 AEST that sit inside the 500-line log tail. Those integrations pollhttp://localhost:17878(radarr) andhttp://localhost:18989(sonarr); neither port listens on the host, anddocker port radarr/docker port sonarrreturn nothing, so the containers publish no host port. The containers themselves are up (radarr and sonarr started 2026-09-10T21:23 UTC, up 13 hours). This is an integration endpoint mismatch, not a Colima or HA network fault. Source:~/.hermes/scripts/hermes_home_integration_sentinel.pylines 83-87 and 239-244,grepover/Users/dwayne/.homeassistant/home-assistant.log,docker ps,lsof -nP -iTCP -sTCP:LISTEN. sensor.camera_vision_hub_maxis motion-triggered only. The only automation that analyzes it isautomation.ai_camera_analysis_hub_max_motion, which fires onevent.lounge_display_motion. That event last changed 2026-09-11T03:33:03Z (13:33 AEST). Between motion events the published still goes stale, the script's 5-minute freshness test fails, and the sensor falls back toAnalysis unavailable: no fresh camera frame (within 5 minutes).The 300-second launchd jobcom.dwayne.ha-expose-camera-stillsrefreshes the still files but does not update this sensor. The device itself was streaming (61647-bytehub_max.jpg,camera.lounge_displaystatestreaming), unlike the 2026-09-09 Hub Max outage above. Result: the sentinel re-flags an artifact that is stale by design. Source:/Users/dwayne/.homeassistant/packages/camera_ai_analysis.yamllines 58-80,/Users/dwayne/.hermes/scripts/ha_camera_vision_analysis.pylines 193-258, launchd plistcom.dwayne.ha-expose-camera-stills.
Host disk pressure threatens this same pipeline, observed 2026-09-11: /System/Volumes/Data at 98% used with 4.7 GiB free (233 GiB volume). camera-vision.log line 127 records OSError: [Errno 28] No space left on device on www/camera-stills/vision_analysis.tmp at the 15:59:41 AEST sweep, which aborted that sweep. Largest consumers: /Users/dwayne/.homeassistant/backups 9.5G (three automatic backups of about 3.2G, newest 2026-09-11 05:41), /Users/dwayne/.hermes/backups 5.7G (pre-update-2026-09-10-122306.zip 3.3G and pre-update-2026-08-27-024100.zip 2.4G), /Users/dwayne/Library/Caches 7.0G, /Users/dwayne/.colima 9.5G. Source: df -h, du -sh, /Users/dwayne/.homeassistant/camera-vision.log.
HA is reachable at http://100.80.168.13:8123 (macbook-pro-3, Tailscale 100.80.168.13). Previously ran on Pro-2 (100.97.49.102) via ai.openclaw.ha-tunnel launchd; that host is now retired. All cron scripts referencing the old HA URL were updated on 2026-08-02. Source: live HA reachability check (HTTP 401 = alive, needs token), script audit, 2026-08-02. Tokens live as names only: ~/.ha-token, ~/.openclaw/.ha-token, ~/.openclaw/credentials/homeassistant.json, ~/.homeassistant_tokens.
2026-09-09: Dwayne requested removal of X sensor entities. Removed 35 sensor/binary_sensor registry entries across the old X and current dodgers mobile-app instances. Preserved device trackers and notification entities. Verified zero target registry rows and live states, and sensor.x_camera_stream returns 404. Sentinel dry-run initially saw the registry's delayed disk save, then returned silent after persistence. This supersedes the X camera incident below. Source: /Users/dwayne/.hermes/ops/ha-x-retirement-20260908T212501Z/receipt.json, direct HA state read, and sentinel dry-run. Mobile-app instances remain installed; future app sensor re-registration is not ruled out.
Current TP-Link camera incident, verified 2026-07-17 19:05 AEST:
- HA/container restart is not indicated for the persistent camera cluster.
hermes_home_integration_sentinel.py --dry-runreportsnetwork_attention,restart_indicated=false,network_indicated=true, before/after 10 affected entities. - Driveway
192.168.68.55and Roof192.168.68.61are reachable on TCP 443 from the host and HA container, but HA logs show Driveway credential/auth challenge mismatch and Roof TP-Link query timeout. - Entrance
192.168.68.84and Pool/Garden192.168.68.93are not present at their known MACs (98:ba:5f:47:d0:f9,3c:64:cf:c7:6b:1d) in host ARP after a LAN sweep; host ARP remains incomplete and HA container gets connection refused on TCP 443. - Do not spend generic HA/Colima/gateway/router/mesh reboot loops for this symptom set. Next useful action is physical/device-network recovery for
.84/.93, Roof protocol investigation for.61, and exact approval before any Driveway TP-Link credential/auth metadata repair.
Shelly EM + ONVIF camera cluster outage, observed 2026-08-22 to 2026-08-24 (source: ~/.hermes/ops/state/home_integration_sentinel.json, kg daily-air-fryer-cost-report):
- 2026-08-22 10:00
daily-air-fryer-cost-reportblocked with "HA state fetch failed" (now:kwhdropped -1199.71). 2026-08-23 10:00 it recovered via baseline reset to 1202.896 kWh. hermes_home_integration_sentinelreportsnetwork_attention,restart_indicated=false,network_indicated=false(note: differs from the older TP-Link incident where network_indicated=true). Reload actions onshellyandonvifdomains return HTTP 200 but resolve 0 entities.- 19 persisted bad entities (2026-08-24 19:14): the full Shelly EM set (12:
binary_sensor.shelly_em_overpowering,sensor.shelly_em_channel_1_energy,_energy_returned,_power_2,_power_factor,_voltage_2,sensor.shelly_em_channel_2_energy,_energy_returned,_power,_power_factor,_voltage,switch.shelly_em) plus 3 ONVIF cameras (camera.driveway_tc41_onvif_mainstream,camera.entrance_tc41_onvif_mainstream,camera.pool_c510w_onvif_mainstream). 4 unmapped aliases (sensor.shelly_em_channel_1_energy_cost,_energy_cost_2,_power,_voltage). - House status 2026-08-24 19:14: HA 489/642 entities available, 153 unavailable/unknown, Power unavailable. Climate/weather/lights healthy.
- This is a distinct cluster from the TP-Link camera incident above; the sentinel reload loop is not resolving it. Next useful action is device/network recovery for the Shelly EM and the three ONVIF cameras, not further reloads.
Camera cluster status 2026-09-05 (source: ~/.hermes/ops/state/home_integration_sentinel.json): the Shelly EM + ONVIF cluster has resolved. Sentinel now reports a single persisted bad entity sensor.x_camera_stream (mobile_app domain, title "dodgers"), network_attention, network_indicated=true (back to true, matching the older TP-Link incident), restart_indicated=false. Reload action on mobile_app returns HTTP 200 but resolves 0 entities. House status 2026-09-05 21:52 AEST: HA 509/640 entities available, 131 unavailable/unknown, Power back at 498W (sensor.shelly_em_channel_1_power_2), climate/weather/lights healthy. The remaining sensor.x_camera_stream is a distinct, smaller issue from the resolved Shelly/ONVIF cluster.
Nest Hub Max camera feed outage, verified 2026-09-09 19:20 AEST (source: ~/.hermes/ops/state/home_integration_sentinel.json, live probes, session cron_3681ce3a337e_20260909_185256): sentinel flags sensor.camera_vision_hub_max (script-published AI vision output from ha_camera_vision_analysis.py, unmapped, no config entry to reload). Root cause is the Hub Max device itself, not the HA container/Colima path: Nest WebRTC returns 400 FAILED_PRECONDITION "The camera is not available for streaming" (retested 19:16-19:20 AEST), camera.lounge_display proxy returns the 2689-byte Nest placeholder, hub_max.jpg frozen since 09:01 AEST and byte-identical (sha256 f4b5056f) to the 18:01 sweep capture, media_player.kitchen_display unavailable since 2026-09-09 09:50 AEST, and ping to 192.168.68.79 (ARP entry e8:ea:6a:82:d7:1c on en5) is 100% lost. Other Nest devices (front door, front stairs) stream fine through the same helper. The sentinel's network_attention label came from unrelated DNS/OverKiz log noise. Recovery requires a manual power-cycle of the Hub Max; sensor self-clears on the next successful vision sweep once the device streams again.
Automation surface (scripts in ~/.hermes/scripts/, observed 2026-07-09):
- ha-entity-guard cron 6×/day (07:17 etc.), missing-entity guard, silent when OK.
- Power: ha_power_summary / ha_power_known_loads ("unavailable ≠ zero" fix 2026-06-12), reports to
~/.hermes/reports/ha-power/; anomaly detector db~/.hermes/ops/anomaly_detector.db; battery-low sweep. - Cameras: camera_sweep_collect 06/12/18:00 → OpenClaw analysis 03/06/12/18:10; sweeps stored
~/.hermes/camera_sweeps/<ts>/; driveway real-still patch 2026-06-21. - Climate/energy: globird-ac-optimizer (tariff-window AC control, deliberate hours 07/11/13/14/23), air-fryer cost + done-watch.
- cross-status
ha=field counts available/total entities (211/261 with warn flag at 02:35 2026-07-09, investigate threshold before alarming).
Log
- 2026-10-04 (weekly synthesis, the CoreBluetooth crash class recurs): Home Assistant crashed and relaunched again on 2026-10-04. A fresh crash report
~/Library/Logs/DiagnosticReports/python3.14-2026-10-04-015825.ips(89670 bytes, written 01:58:25) recordsSIGSEGV/EXC_BAD_ACCESSat 0x0 undercoalitionName com.dwayne.homeassistant, with the stack passing throughlibffiand-[CBCentralManager handlePeripheralDiscovered:]and_objcbefore it dies. This is the same signature as the 2026-09-20 and 2026-09-29 crashes already logged above, so the fault was never closed. The live log confirms an unplanned restart:~/.homeassistant/home-assistant.logopens at 2026-10-04 01:58:19 with the custom-integration warnings and reachesStarting Home Assistant 2026.8.2at 01:59:07; the process (PID 9357,/Users/dwayne/.homeassistant-venv/bin/python3.14 .../hass) has been up since 01:58 and was healthy at read time. Only one crash report survives in the DiagnosticReports directory, so the 01:58 event is the one on record for the week, but it is a recurrence of a known open defect rather than a new one: anysensor.camera_vision_*or other runtime-published entity that readsno dataright now may simply be missing its republish after this restart. Sources:~/Library/Logs/DiagnosticReports/python3.14-2026-10-04-015825.ips,~/.homeassistant/home-assistant.log, liveps, read 2026-10-04 07:10-07:20 AEDT., hermes.
2026-10-02 09:20-09:40 AEST (power-baseline report: two false statements corrected): the power-baseline box (cron 6abbadb58319, schedule 5 8,17 * * *, script power_baseline_monitor.py) carried a note that contradicted its own output. It read "Only the fridge and towel heater are metered directly", while the same box printed Measured: 528W computed from measured_values = fridge, dishwasher, pool_filter_pump, pool_heat_pump. The towel heater is not in the map at all. The note now builds its list from result['measured_values'] at runtime, so it cannot drift again.
The second claim was tested rather than assumed: a 5-day recorder analysis of sensor.power_untracked_after_known conditioned on sensor.pool_filter_pump_power and sensor.shelly_em_channel_2_power both below 50 W (n=3837, the pool and aircon genuinely off) gives a median residual of 337 W, p10 214, p90 427. So "a standing base near 350 W" is real; the note now says 340 W and cites the analysis and date.
Also superseded in the same file's comment: it claimed "no W-unit sensor exists for those loads" about six retired names removed 2026-09-27. As of 2026-10-02 sensor.washing_macine_current_consumption (70.1 W, ~2.1 kW heating) and sensor.heat_pump_dryer_current_consumption (1.6 W, ~640 W running) ARE live W sensors, because their TP-Link P110 plugs were re-enabled 2026-10-01. They remain out of the measured map deliberately, pending Dwayne's approval, because adding them changes the figure his twice-daily report reads. The comment now states this.
Method note for this class of fault: a note or comment that describes what a script does must be read against what the script actually computes. Both false statements here were invisible to every structural check (the script ran, exit 0, numbers reconciled to CT1) and survived because the text was never cross-checked against the data structure it described. Render the output and read it against the code. Sources: ~/.hermes/scripts/power_baseline_monitor.py, ~/.hermes/cron/jobs.json (job 6abbadb58319), ~/.homeassistant/recorder/home-assistant_v2.db read-only, live HA /api/states, 2026-10-02 09:20-09:40 AEST., hermes.
2026-10-02 08:55-09:15 AEST (supersede: the pump-plug rename): a platform: integration counter cannot be renamed without an HA restart, and repointing a template at its new name silently zeroes the term. While renaming the pool filter pump plug this session, configuration.yaml's integration block was edited from source: sensor.home_entertainment_power, name: "Home Entertainment Total Energy" to source: sensor.pool_filter_pump_power, name: "Pool Filter Pump Energy", then homeassistant.reload_core_config was called twice. The old entity sensor.home_entertainment_total_energy kept running (245.96 kWh at 08:54:46Z) and no sensor.pool_filter_pump_energy ever appeared, only one Setting up integration.sensor line was logged for the whole reload, not the four the block defines.
Cause, verified rather than assumed: the integration sensors are a legacy YAML platform with no config_entry_id and no entry in core.entity_registry at all (checked directly: registry holds 860 entities and contains zero with platform: integration matching this block, and zero matching home_entertainment). Their entity_id is derived from name only when there is no registry record to honour, so the id is fixed for the life of the process. config/entity_registry/update on the orphan answers not_found: Entity not found, and the id does not appear in core.restore_state. The running instance was still the pre-edit config at that point (integration.sensor set up once, at 08:54:50), so the edit had not been read; after reload_core_config the new name still did not appear.
Decision, and why: rather than leave a term reading states('...') | float(0) against an entity that does not exist, which fails silently to 0 and understates Monthly Consumption with no error anywhere, the integration block entry for the pump was removed, and the template repointed at the device's own native counter sensor.pool_filter_pump_total_energy (2.1 kWh, total_increasing, Tuya). That counter is the real meter; the integration counter was a derived duplicate of the same physical load, so dropping the derivation removes a redundancy rather than a measurement. Verified after template.reload: sensor.monthly_consumption 2.62 kWh, no stale id in any live config, and sensor.home_entertainment_total_energy is the only old id left live, documented in ## Now as a deliberate leftover. If the integration counter is wanted back under the new name, it needs an HA restart (a gated action) and will start from 0.
Also settled this session, same root cause class: search_files and find over ~/.homeassistant time out (>200 s, exit 124) because the recorder DB and log are large; scope the probe or use the API/registry files directly. template.reload is template/reload, not homeassistant/template_reload, the latter 404s. And reload_core_config is the only reload that re-reads configuration.yaml. Sources: live HA /api/states, home-assistant.log line 121260, ~/.homeassistant/.storage/core.entity_registry, ~/.homeassistant/.storage/core.restore_state, ~/.homeassistant/configuration.yaml, read 2026-10-02 08:55-09:15 AEST., hermes.
-
2026-09-30 (18:12-18:22 AEST, Kasa KLAP 403 burst, and a new *arr false-positive variant): the 18:12
ha_health_check.pyrun reported 5 ERROR lines in its 70-minute window (17:02-18:12): fourkasa.transports.klaptransportQuery failed after successful authentication ... Response status is 403lines (192.168.68.67 18:10:25, .66 18:10:27, .68 18:10:28, .66 18:10:34) and one[homeassistant] Error doing job: Task exception was never retrievedat 18:06:58. The burst kept running after the run: 20 kasa lines between 18:10:25 and 18:14:38, 23 for the day (1 at 00:36, 1 at 02:18, 1 at 15:05, 20 in the evening burst), against a prior recorded daily maximum of 7 (2026-09-17). Hosts cycled .66 (8 lines), .68 (7), .67 (5); none recurred after 18:14:38. -
Impact is not nil, superseding the 2026-09-17 16:13 entry below.
core.entity_registryread 2026-09-30:Washing Macine P110(192.168.68.66) andHeat pump dryer P110(192.168.68.67) each have 17 of 20 entities disabled, so three are enabled per entry (sensor.*_current_consumption,sensor.*_voltage,switch.*).Data centre P110M(192.168.68.109) andData centre 2 P110(192.168.68.68) are still 20/20 disabled. The enabled telemetry kept serving through the burst:/api/historyover 3 h holds 1705 points forsensor.heat_pump_dryer_current_consumptionand 1644 forsensor.washing_macine_voltagewith no gap over 300 s, and both read live at 18:20 AEST (241.7 V, 1.3 A). -
All three hosts are on the LAN:
ping -c 3 -i 0.3at 18:18 AEST returned 3/3 packets with 0.0% loss, round-trip average 7.4 ms (.66), 8.4 ms (.67), 5.3 ms (.68). The KLAP 403 stays the documented stale-handshake transient (python-kasa issue #784), retrying within seconds. -
The 18:06:58
Task exceptionis a new variant of the *arr down-window noise: the traceback isaiopyarr/request_client.pytoaiohttpending inConnectionRefusedError: [Errno 61] Connect call failed ('127.0.0.1', 17878)(Radarr) under the log tag[homeassistant], soarr_down_window_noise()does not match it (it tests forhomeassistant.components.radarrin the line) and it alerts while the container is deliberately down outside 00:00-05:00. The same variant was logged at 2026-09-29 23:24:08. Candidate gate for a future script pass, not applied by this monitor run. -
HA itself is healthy:
/api/200API running.,/api/configstate=RUNNINGversion 2026.8.2, 729 states with 123unavailableagainst threshold 150,home-assistant.log.faultunchanged at 314022 bytes (last write 2026-08-25 00:02). -
Source:
~/.homeassistant/home-assistant.log,.storage/core.entity_registry, live HA/api/statesand/api/historyreads,pingprobes, 2026-09-30 18:16-18:22 AEST., hermes. -
2026-09-29 (23:36-23:50 AEST, camera still refresh: unbounded read caused a 5-hour hung run and a cron timeout): The
ha-exposed-camera-stills-refreshcron job (835ebfa7498e,*/30 * * * *,ha_expose_camera_stills_quiet.sh) reportedScript timed out after 300sat 23:30. Root cause was not the cron cap. An earlier run of the same wrapper, started by the launchd twincom.dwayne.ha-expose-camera-stills(StartInterval 300) at 18:30:30, was still alive at 23:36 after 5h06m, parked inrecvinsideurllibwith 9.2 GB physical footprint and swap 18.5 GB of 19.4 GB.sample 5186showed the stack in_buffered_readline->sock_recv_into->internal_select->poll. The mechanism:ha_request()usedurllib.urlopen(timeout=6).read(), and a socket timeout resets on every recv, so HA's camera proxy drip-feeding bytes keeps the call alive forever. Theread()also blocks until it has the full buffer, so a deadline check placed around it never runs. Fix, inha_expose_camera_stills.py:ha_request()takes an absolutedeadlineand reads the body withresp.read1(65536)in a loop, checkingtime.monotonic()each pass and capping the body at 32 MiB; the run is bounded byRUN_BUDGET_S = 240and each camera byCAMERA_BUDGET_S = 30, both clamped so a stalled camera reportsskipped_budgetand keeps its previous still rather than stalling the run. The wrapper now holds a/usr/bin/lockf -t 0single-instance lock so the cron and launchd twins cannot stack (a held lock exits 75, which the wrapper treats as success). A second wrapper bug surfaced during verification: underset -euo pipefail,tail_msg=$(grep -v '^lockf:' "$ERR" | tail -c 360 ...)aborted the whole failure branch when grep matched nothing, so a failure with empty stderr reported nothing at all; the command substitution now carries|| true. Verification: the drip-feed case is reproduced in-process against a local HTTP server that writes one byte per second; the unbounded build hangs indefinitely (killed at a 40 s outer limit, then at 120 s) while the fixed build raisesTimeoutErrorat the deadline (5.8 s for a 5 s budget, 9.0 s for 8 s) and a normal 5002-byte body still returns intact. A 33 MiB body is rejected by the cap in 0.1 s. 15/15 ad-hoc checks passed against an isolated copy of the wrapper (held lock is an instant silent no-op, stale stderr cleared, real failure reports compact text with exit 0, silent failure still reports, success path is silent); the wrapper checks needed the isolated copy because a live launchd run holds the real lock every few minutes. Wrapper run by hand, 24-45 s, exit 0. Status at 23:41:42: 5 of 7 current, 2 stale_real (drivewayserving its previous still after a failed snapshot,hub_maxon the standing Nest "stream not available"). The cron's 23:30runningrow and the 23:00 killed run both completed the work before the kill, so the stills were never actually stale for long. Sources:ps/sampleof PID 5186,~/.hermes/scripts/ha_expose_camera_stills.py,~/.hermes/scripts/ha_expose_camera_stills_quiet.sh,~/.hermes/cron/executions.db,~/.homeassistant/www/camera-stills/status.json, read 2026-09-29 23:36-23:50 AEST., hermes. -
2026-09-27 (weekly synthesis): The week's camera evidence, read from the two 06:00/06:01 sweeps rather than inferred. The 2026-09-27 06:00 sweep captured 6 of 6 snapshots with 6 changed reads: stills current for 4, stale for 2, failed 0. Front Door, Front Stairs, Garden and Pool returned usable frames; the Driveway served a stale still with no vision summary, so that frame is unknown rather than clear. The companion 06:01 sweep reported 7 of 7 cameras captured, 0 failed, 4 frames current and 3 stale (
Front Stairsafter a Nest API 500 Internal error,Hub Maxafter a 429 rate limit onExecuteDeviceCommand,Drivewayafter a 404 on the snapshot endpoint), exposure normal across all frames (luma 110-137, so no camera is blind or washed out), and no frame flagged as a Nest placeholder. Its verdict was attention, not failure: three of seven frames are not from this sweep, so treat those three as unverified for the 06:01 window, and a repeat of the 429 points at the live WebRTC pull being throttled at sweep cadence rather than at the cameras. Standing faults unchanged in kind: the Driveway TC41 still serves the known superseded still, and the Pool camera viewpoint is still the off-target fault recorded on 2026-09-24 and 2026-09-26. Neither was actioned, which is correct for a monitor run. Sources:~/.hermes/cron/output/b40827332b0a/2026-09-27_06-01-26.md,~/.hermes/cron/output/531efd75cf09/2026-09-27_06-01-11.md, read 2026-09-27 06:00-06:10 AEST., hermes. -
2026-09-26 (06:12-07:20 AEST, Open-Meteo weather false positive fixed in
ha_health_check.py): The 07:12 hourly run alerted on 1 ERROR line,[homeassistant.components.open_meteo] Error fetching open_meteo_zone.home data: Open-Meteo API communication errorat 06:03:03.116. Not a live fault. The coordinator polls the public Open-Meteo API every 30 minutes (SCAN_INTERVALinhomeassistant/components/open_meteo/const.py, confirmed in the running venv atminiforge3/envs/ha314/lib/python3.14/site-packages/homeassistant/components/open_meteo/), so a single lost poll is followed byFetching open_meteo_zone.home data recoveredroughly 30 minutes later: 06:33:04.327 here, 30 min 1.2 s after the error. The current log holds exactly one open_meteo error and one recovery,home-assistant.log.1andhome-assistant.log.faulthold none, andweather.homekept serving through it (sunny,last_updated2026-09-26T21:03:05Z, read 07:13 AEST). Fix: reuse the generalisedcoordinator_transient_fetch_noisehelper with a newOPEN_METEO_COORDINATOR_TAG/OPEN_METEO_TRANSIENT_FETCH_MARK/OPEN_METEO_RECOVERY_MARKand a 45-minuteOPEN_METEO_RECOVERY_WINDOW. The window is deliberately wider than the 30-minute poll interval, otherwise a single lost poll could never match its own recovery line; it is still narrow enough that an outage lasting more than two polls keeps the error visible. No entity-state gate is used, matching IPP. The marks stay out ofERROR_ALLOWLIST, so an unrecovered API outage still alerts. Verification:tests/test_ha_health_check.pygrew 36 -> 44 tests, all passing underhermes-agent/venv/bin/python -m pytest(44 passed in 0.21 s), covering suppress-on-recovery, no-recovery still alerts, recovery past the window still alerts, repeated errors before recovery still alert, window wider than the poll interval, the mark not matching the SABnzbd/IPP/Shelly lines, not allowlisted, and both end-to-end paths. Falsified: replayed against the pre-fix build (ha_health_check.py.bak-pre-ipp-gate-20260926copied to /tmp) the unresolved-line count is 29 versus 22 with the gate armed; disarming only this gate (OPEN_METEO_RECOVERY_WINDOW = 0) puts the open_meteo line back and hides nothing else; the live 70-minute run prints the bare[SILENT]with exit 0 and--verbosereports all probes passed. Unwind: delete the fourOPEN_METEO_*constants and the one call site if the cadence ever stops being single-poll. Sources:~/.homeassistant/home-assistant.log(and.log.1,.log.fault), live HA/api/statesand/api/states/weather.home,~/.hermes/scripts/ha_health_check.py, 2026-09-26 06:12-07:20 AEST., hermes. -
2026-09-26 (05:12-05:30 AEST, IPP printer false positive fixed in
ha_health_check.py): The 05:12 hourly run alerted on 2 ERROR lines, both the same line:[homeassistant.components.ipp.coordinator] Error fetching ipp data: Invalid response from API: Timeout occurred while connecting to IPP server.Not a live fault. The coordinator polls the Canon TS8300 atipp://192.168.68.91/ipp/printevery 60 s; a poll lost to the printer sleeping or answering slowly is followed byFetching ipp data recoveredon the very next cycle. The current log holds exactly 4 ipp errors (2026-09-25 21:11:08, 2026-09-26 00:26:17, 04:04:24, 04:19:32), every one with a recovery 60 s later, andhome-assistant.log.1holds none. The device proves itself healthy on the entity path:sensor.canon_ts8300_seriesreadsidle(last_changed2026-09-25T18:20:33Z) and no ipp entity isunavailable. Fix: the SABnzbd helper's recovery-proof logic is generalised intocoordinator_transient_fetch_noise(line, when, tail, idx, fetch_mark, recovery_mark, coordinator_tag, window), withsabnzbd_transient_fetch_noisenow a thin wrapper, and the IPP coordinator getsIPP_TRANSIENT_FETCH_MARK/IPP_RECOVERY_MARK/IPP_COORDINATOR_TAG/IPP_RECOVERY_WINDOW(180 s). The line drops only when arecoveredline for the same coordinator follows inside the window; a second ERROR from that coordinator before any recovery, or a recovery past the window, keeps it visible. There is deliberately no entity-state gate here (unlike Shelly and Panasonic): the printer's six ink sensors readunknownpermanently and cannot prove health, so the log's own recovery line is the whole guard. The marks are absent fromERROR_ALLOWLIST. Verification:tests/test_ha_health_check.pygrew 29 -> 36 tests, all passing underhermes-agent/venv/bin/python -m pytest(36 passed in 0.19 s), covering suppress-on-recovery, no-recovery still alerts, recovery past the window still alerts, repeated errors before recovery still alert, the mark does not match the SABnzbd or Shelly lines, and two end-to-end pipeline cases. Falsified: the pre-patch build (backupha_health_check.py.bak-pre-ipp-gate-20260926, sha256ee272db17014c3e8eb41affbdc2fc388fba4830d47fbba12f6ccbbccdb60779c) run against the live log still prints the 1-problem alert, while the patched build prints the bare[SILENT]with exit 0 and--verbosereports all probes passed.py_compileclean. Also run as ad-hoc verification (15/15,hermes-verify-ipp-gate.pyunder$TMPDIR, cleaned up after the run): both marks match the real log lines, the window is 180 s, the transient is suppressed, no-recovery / late-recovery / repeated-error cases still alert, the mark does not match the SABnzbd line, the SABnzbd wrapper still works after the refactor, patchedcheck()returns clean on the live log while the pre-patch build reports the ipp problem, and both CLIs behave as expected. These are ad-hoc checks, not a project suite. Unwind: delete the threeIPP_*mark constants and their call site if the cadence ever stops being single-poll. Residual: the generic helper is now used by two coordinators; a future third caller inherits the same "recovery inside the window" proof and must not be givenERROR_ALLOWLISTtreatment. Source:/Users/dwayne/.homeassistant/home-assistant.log(+.1), live HA/api/states,/Users/dwayne/.hermes/scripts/ha_health_check.py,~/.hermes/tests/test_ha_health_check.py, 2026-09-26 05:12-05:30 AEST., hermes. -
2026-09-25 (18:03 AEST, camera vision sweep): 14 cameras, 17 captures, 4 failures, 0 actionable, the same standing non-actionable set as 2026-09-24: three 2689-byte Nest dark placeholders (
camera.lounge_display,camera.home_home,camera.entryway_front_stairs,nest_webrtc_no_server_side_snapshot_dark_placeholder) andcamera.driveway_live_view(404,source_superseded_by_real_still). Fresh frames 18:00-18:02 AEST for garden (18:00:31), roof (18:00:34), pool (18:00:53), front stairs and hub max via WebRTC live; front door served its 18:00:58 real still after a 25 s WebRTC timeout. No people, parcels, vehicles or damage in the garden, front door, front stairs or hub max frames; overcast, no precipitation visible. Roof/pergola remains an active jobsite: corrugated metal sheeting partly laid over the metal joists, exposed battens with gaps, blue tarp, stacked concrete blocks along the fence, lumber and a wheelbarrow, no workers in frame, no storm damage. Driveway TC41 now frozen 11 days:published_driveway.jpgmd53749c7879ed4e6e29665120c5757fc98is byte-identical across 21 consecutive sweep directories (20260919-080650 to 20260925-180334), the newest distinct driveway still on disk is20260914-060040_camera_driveway_real_still.jpg, and the embedded watermark still reads2026-09-14 12:49:17 PM. Live re-probe of192.168.68.55: ping 3/3 received, 12.0 ms avg, TCP 443 and 554 both open, HTTPS GET returns code 000.camera.driveway_live_viewremainsunavailable(last_updated 2026-09-23T21:01:23Z). Same port-open-but-silent wedge as 2026-09-15, 2026-09-20 and 2026-09-21;homeassistant.reload_config_entryis exhausted, so the fix is a physical power-cycle of the TC41. Pool camera view is still not on target and not stable: the 18:00:53 frame (camera.garden_live_view→ slugpool) shows a side/rear courtyard, deck with a flat pallet and a dark object, a bare-branched tree, a gas grill, a trampoline frame with no mat, stacked lumber, a dark plank fence and a neighbouring verandah, with no pool and no water anywhere in frame. The 12:02 frame is a different courtyard (paved area, wooden extension ladder flat on the ground, hand truck, lumber, again no pool). Frame-to-frame 16x16 average-hash distance between those two is 72 of 256 and 06:00 → 12:03 is 79 of 256, above the 8-33 band the healthy Pool camera normally sits in. The current view is internally consistent, so this is not a data fault: the 18:00:53 sweep file, the 18:02ha_expose_camera_stillspublish (~/.homeassistant/www/camera-stills/pool.jpg, md501adab8974) and a direct read of the live HA proxy/api/camera_proxy/camera.garden_live_viewall match the sweep frame within 10 of 256. Next action is still a check of the Pool camera's physical mount or pan/tilt home position. Source:~/.hermes/camera_sweeps/20260925-180334/manifest.jsonandpublished_*.jpg,~/.homeassistant/www/camera-stills/status.json, md5 and aHash comparison across~/.hermes/camera_sweeps/2026*, liveping/nc/curlagainst 192.168.68.55 and .84, live HA/api/statesand camera proxies. - hermes. -
2026-09-24 (14:15 AEST, Roborock away for repair, dated suppression): The Roborock Qrevo MaxV is with the repairer (Dwayne, 2026-09-24), so all 52 of its entities are
unavailableby design (vacuumlast_changed2026-09-23T21:01:23Z). The home-integration sentinel (hermes_home_integration_sentinel.py, cron3681ce3a337e, every 10 min 07:00-21:00) was paging about it every tick: a typical card read "32 entities were affected before it, 32 remain ... another reload is unlikely to help. A Home Assistant restart may be required." That advice was wrong twice over, the integration is not degraded, the hardware is absent, and no reload or HA restart can change it. Fix: a newEXPECTED_ABSENT_INTEGRATIONSset in the sentinel. Each entry is{domain: {until, reason}}, whereuntilis an ISO date. Entities whose config entry belongs to an active domain are dropped from the page list, and the reason is surfaced in the report JSON asexpected_absent. Why dated rather than a bare exclusion: suppression must not outlive the repair, so the entry expires on its own date and normal reporting resumes; a malformed date is treated as expired (fails loud to stderr) so a typo cannot silently hide a real outage. Why domain-scoped, not entity-scoped: a whole integration that is physically away is unavailable across all of its entities, so keying on the 52 individual entity IDs would be unmaintainable, whileplugins.disabled-style per-entity suppression would still page ~30 times a day. Verification (10/10,$TMPDIR/hermes-verify-roborock-absent.pyon/usr/local/bin/python3, the interpreter cron uses): window active; 52 live roborock entities unavailable; zero roborock entities in the page list; a genuinely broken non-roborock entity (sensor.power_consumption_total) still pages, so there is no over-suppression; an expireduntilstops suppressing; a malformeduntilstops suppressing; the report carriesexpected_absent; the real--dry-runreturnsresult=noop, before_bad=0. Falsified: with the mechanism disabled, 32 roborock entities immediately reappear in the page list, so the suppression is doing real work rather than passing vacuously. Script backed up tohermes_home_integration_sentinel.py.bak-pre-roborock-repair-20260923(sha2564e23c8277ad40c7c7f7bb38ff9f14cc263ef844d03e6fcf8a34375f7773f953a); final hash4a91d19b428e341cbfb0d8e1f5544ba1162b152990792dab0afd9c667eccb05e(the earlierb3962dac…was pre-tidy: a first pass printed an "expired" line to stderr on every tick after expiry, which is the log-noise class this fix exists to remove; expiry is now silent while a malformeduntilstill warns on every tick). Verification re-run after the tidy: 12/12, including "expiry writes no stderr noise" and "malformed until warns loudly". A clean run writes nothing to stderr and prints[SILENT], while--jsonstill recordsexpected_absent: ['roborock']for audit. Unwind: delete theroborockentry fromEXPECTED_ABSENT_INTEGRATIONSwhen the robot comes back, or let it lapse on 2026-10-14. Residual: the sentinel's earlier standing advice ("a HA restart may be required") is produced byRESTART_REQUIRED_PATTERNSmatching the HA log, not by the absent integration; it did not fire here, but it remains a path that could mislead on an unrelated log line. Source: live HA/api/states,core.config_entries(roborockentry01J7JYDCYG5RBH45VAS65M4C90, titledwaynehelena@gmail.com, 58 entities), live--dry-run --json, temp-home falsification., hermes. -
2026-09-24 (12:03 AEST, camera vision sweep): 14 cameras, 17 captures, 4 failures, 0 actionable. The four failures are the standing non-actionable set: three 2689-byte Nest dark placeholders (
camera.lounge_display,camera.home_home,camera.entryway_front_stairs,nest_webrtc_no_server_side_snapshot_dark_placeholder) andcamera.driveway_live_view(404,source_superseded_by_real_still). The Pool camera changed viewpoint.published_pool.jpg(camera.garden_live_view) shows a paved rear courtyard with a wooden extension ladder flat on the ground, a metal hand truck, stacked lumber, potted plants, a clothesline and a neighbouring verandah - no pool in frame. Frame-to-frame view distance is the test: consecutive Pool sweeps normally sit at 8-33 of 256 on a 16x16 average-hash grid, and 2026-09-24 06:00 -> 12:03 jumped to 134, the same signature as 2026-09-23 12:03 -> 18:03 (149). The view is stable at 3 and 0 respectively across three independent reads twenty minutes apart: the 12:03 sweep file, the 12:07ha_expose_camera_stillspublish (~/.homeassistant/www/camera-stills/pool.jpg, md5 a22b731b) and a direct read of the live HA camera proxy/api/camera_proxy/camera.garden_live_viewat 12:15 (md5 4e3e5d35). This is the camera having moved, not a construction change and not a data fault: the other cameras on the same host and bus are unaffected, and the siblingcamera.garden_pergolux_roof_camera(last sweep frame 108886 bytes) still reads the deck-joist jobsite and does show a curved pool edge at frame left. Next action: check the Pool camera's physical mount or pan/tilt home position. Driveway TC41 remains frozen, now 17 consecutive sweeps:camera.driveway_real_still.jpgis byte-identical (md53749c7879ed4e6e29665120c5757fc98) across every retained sweep from 2026-09-19 08:06 to 2026-09-24 12:03, andcamera.driveway_live_viewstill readsunavailable(last_updated 2026-09-23T21:01:23Z, 2026-09-24 07:01 AEST); physical power-cycle of the TC41 still stands. Other fresh stills (12:03-12:07): front door clear and sunny with three parked vehicles (white sedan, blue SUV, white pickup), no people, no parcels, yellow-lid and red-lid wheelie bins at the kerb; front stairs shows the brick path, orange flowers and the deck, no people or vehicles; hub max shows an empty lounge looking out to the pool and patio; garden shows parked vehicles along the street. No people, parcels or animals in any 12:03 still except the 08:46 front-door frame already recorded invision_analysis.json. The threeOSError: [Errno 28] No space left on devicelines inha_expose_camera_stills.launchd.outare historical, from 2026-09-11 01:21 and 01:26, not this run;/System/Volumes/Dataholds 16 GiB free at 40% capacity on/. Source:~/.hermes/camera_sweeps/20260924-120329/manifest.jsonand published_*.jpg,~/.homeassistant/www/camera-stills/{status.json,pool.jpg}, live HA/api/statesand/api/camera_proxy/camera.garden_live_view, md5 comparison across~/.hermes/camera_sweeps/2026*/*driveway_real_still.jpg,df -h, 2026-09-24 12:03-12:15 AEST. - hermes. -
2026-09-24 (01:35 AEST, SABnzbd false-positive fixed in
ha_health_check.py): The 01:12 run's single ERROR line was a false positive. Root cause: HA's SABnzbd coordinator polls every 30 s (~/.homeassistant-venv/lib/python3.14/site-packages/homeassistant/components/sabnzbd/coordinator.py:37), andpysabnzbd.refresh_data()makes two calls per poll (get_queuethenget_history) each with a 5 s client timeout (pysabnzbd/__init__.py:7,41); a poll that loses its response raisesSabnzbdApiExceptionand HA logsError fetching SABnzbd data: Error while fetching data. The two conditions that trigger it inside the 00:00-05:00 up window are SABnzbd's own post-processing (unrar/par2/deobfuscate/move across the virtiofs share) blocking/api?mode=queue, and the 05:00 stop pass disconnecting in-flight polls. Evidence the container is healthy: 30 direct queue+history poll pairs against127.0.0.1:18090ran p50 5 ms, worst pair 18 ms;/api?mode=versionreturned{"version":"5.1.2"};sensor.arr_sabnzbd_statusreadsIdlewith no SABnzbd entityunavailable; the container isUp About an hourwithrestarts=0. Every ERROR line in the current log is followed byFetching SABnzbd data recovered: 2026-09-24 00:05:44 -> 00:06:14 (30 s), 00:08:50 -> 00:09:20 (30 s), 00:32:26 -> 00:32:56 (30 s), and 2026-09-23 00:27:29 -> 00:28:35 (66 s). Each sits beside a postproc burst (Finished unpack_magic,Queue finished) at 00:32:02. The 05:00 errors recover at 00:00 next day (68,402 s), which is the container being deliberately down, not a fault. Fix: newsabnzbd_transient_fetch_noise()suppresses the coordinator line only when (a) aFetching SABnzbd data recoveredline for the same coordinator follows insideSABNZBD_RECOVERY_WINDOW(120 s) and (b)sensor.arr_sabnzbd_statusis notunavailable/unknown; a second SABnzbd ERROR or any recovery past the window returns False, so an outage still alerts. Deliberately NOT added toERROR_ALLOWLIST, which would hide a dead SABnzbd permanently.tests/test_ha_health_check.pygrew 21 -> 29 tests, all passing underhermes-agent/venv/bin/python -m pytest; falsified against the pre-patch build, wheretest_sabnzbd_transient_dropped_end_to_endfails (2 hits, not 1) and the real live log still prints the alert, while the patched build prints[SILENT]and--verbosereports all probes passed. Source:~/.homeassistant/home-assistant.log,~/.homeassistant-venv/.../sabnzbd/coordinator.py,pysabnzbd/__init__.py, livedocker logs sabnzbd, 30-timed poll benchmark, live HA/api/states, 2026-09-24 01:12-01:35 AEST. - hermes. -
2026-09-22 (06:03 AEST, camera vision sweep): 14 cameras, 17 captures, 4 failures, 0 actionable, same shape as 2026-09-21. Fresh stills at 06:03 AEST for pool (watermark 06:03:26AM), roof (06:03:27AM), garden (06:03:23AM), front door, front stairs and hub max; every one byte-distinct from the 2026-09-21 06:00 set (garden 70965736, pool d0bf2b84, roof afd0242a, front door 773d08aa, front stairs 7de5e625, hub max 59c8aa02, all sha256-prefix). Driveway camera now a full 8 days frozen:
published_driveway.jpgsha25606ce21c4be7a86edf44adf8460097bdb7eaa5a059e914785d0814d42a16393c1, byte-identical across all 7 sweeps from 2026-09-15 06:01 to 2026-09-22 06:03, watermark reads 2026-09-14 12:49:17 PM, and the whole scene differs from today's other stills (it is the only slug whose hash never changes). Live re-probe of192.168.68.55: ping 2/2, 7.4 ms avg, ARP98:ba:5f:47:d3:79on en5, TCP 443 and 554 both open, sibling cameras.84/.61/.108up. HA tplink integration logsConfig entry 'Driveway TC41' ... Unable to communicate with the device connect: ('Unable to query the device: 192.168.68.55: ', TimeoutError())every 600 s (246 lines in this log, last 06:11:31).homeassistant.reload_config_entrywithentry_id=01M1P28GK90EW3TX966A41WACZreturned HTTP 200 at 06:11 and did NOT clear it:camera.driveway_live_viewandswitch.drivewaystillunavailable,restored: true,last_changed2026-09-20T05:00:00 (the 14:59 AEST restart). Fix remains a physical power-cycle of the TC41; reloads are exhausted. Three 2689-byte dark placeholders (lounge_display, home_home, entryway_front_stairs) are unavailable evidence, not scene readings;hub_maxstill produces a real WebRTC frame (125 KB) despite its Nest placeholder failure. Scene content, all fresh: pool cover on, deck framing still unsurfaced behind bare joists, pool house complete, blue tarp and black toolbox on site, exposed dirt under the deck - no change in kind from 2026-09-21; roof/pergola an active jobsite (exposed joists, stacked blocks, mixer on its side, wheelbarrow); no people, parcels or animals in any still; front-door view shows the bin enclosure with yellow-lid and red-lid wheelie bins at the kerb, undisturbed. HA 2026.8.2 RUNNING, 677 states, 192 unavailable/unknown, 14 of them driveway entities;vacuum.roborock_qrevo_maxvstill off the LAN since 2026-09-18 08:41 AEST, unchanged. Source:~/.hermes/camera_sweeps/20260922-060348/manifest.jsonand published_*.jpg,~/.homeassistant/www/camera-stills/status.json, sha256 comparison across~/.hermes/camera_sweeps/2026-09-*/*driveway_real_still.jpg,~/.homeassistant/.storage/core.config_entriesandcore.entity_registry,~/.homeassistant/home-assistant.log, live HA/api/states,/api/config, ping/nc probes, 2026-09-22 06:03-06:12 AEST. - hermes. -
2026-09-20 (18:35 AEST, Shelly EM fetch error, transient, report claim corrected): The Ha Health item is a benign transient, and one of its three claims is wrong. Root cause: the Shelly EM coordinator (aioshelly Gen1) occasionally loses a single 60-second poll to a Wi-Fi/cloud-API hiccup; it retries once and recovers. Measured directly against
192.168.68.78: 40 and 60 consecutive/statusfetches produced zero failures, p50 38ms, worst poll 1.04s against aioshelly'sDEVICE_IO_TIMEOUT = 10.0(aioshelly/const.py:1356). A full simulated poll cycle (/settings+/status+/cit/dinside one 10s budget, 50 iterations) never exceeded the budget, p50 0.18s, max 5.53s. So the error is NOT a timeout; it is a lost connection mid-poll (CONNECT_ERRORS→DeviceConnectionError→ HAupdate_errorstring). Frequency: 17 occurrences across 2026-09-19..20, shortest gap 3.6 min, most 20+ min apart, and it self-clears every time. No data cost: CT1 energy delivered 60 readings/hour with a median 60s gap and no gap over 95s across the whole window. Contributing factor: Wi-Fi RSSI is -73 dBm (weak) on firmware20230913 v1.14.0, 16.4 days uptime, RAM 36/51 KB free; device reportshas_update: falsebut a betav1.14.1-rc1exists. CoAP config is correct, not stale:coiot.peer = 192.168.68.79:5683, and.79IS this host and HA's owninternal_url;python3.14(pid 76290) holds UDP 5683. Report corrections: (1) the "no action needed" conclusion is right but for the wrong reason; (2)sensor.shelly_em_channel_1_energy_costis NOT a fault, it is an ORPHANED legacy registry row (platformenergy, unique_idsensor.shelly_em_channel_1_energy_grid_cost, created 2026-06-22) superseded by the livesensor.shelly_em_channel_1_energy_cost_2(unique_id436d902d..._grid_cost, created 2026-08-05, reading 0.602 AUD). The dead row is already listed inhermes_home_integration_sentinel.py::OPTIONAL_UNAVAILABLEand filtered inhermes_cmd_house.py:140. **Fix appli... [truncated] -
2026-09-20 (weekly retune, 15:30-15:48 AEST): HA restarted at 14:59 AEST (log rotated;
home-assistant.log.1tail shows lifx coordinatorCancelledError: Home Assistant is stoppingat 14:58:55 and pyhap accessory shutdown, then a clean start at 14:59:30 with the usual custom-integration warnings). Consequence:sensor.apple_macbook_pro_powerwas unavailable 15:00-15:06 AEST, which failed the air-fryer cron 7 times. Live entity health at 15:40: 672 states, 177 unavailable/unknown in the raw set; the shared tag's204/248counts only operational domains and sensors with a real device class. Standing unavailable groups, unchanged in kind: roborock Qrevo MaxV (52 entities, off the LAN since 2026-09-18), driveway camera and switch (camera.driveway_live_view,switch.driveway*, tplink192.168.68.55timed out again at 15:41:30, device answered ping on 2026-09-15), Tuya/cloud LEDs (9 lights plus their buttons), arr/SABnzbd/Lidarr (unavailable by design outside the 00:00-05:00 window; SABnzbd and Lidarr retry at 600 s), Canon printer ink, iPhone companion sensors, six media players, BLE temperature sensor, three device trackers. 78 entities are unavailable that the 10:43 baseline did not list, almost all of them buttons (identify/restart/pan/tilthelpers) - the shared tag already ignoresidentifynames, so the increase is button noise rather than new faults. Source:/Users/dwayne/.homeassistant/home-assistant.log(+.1), live HA/api/statesand/api/history/period,~/.hermes/state/ha_unavailable_baseline.json, 2026-09-20. - hermes. -
2026-09-20 05:13 AEST: hourly
ha_health_check.pyalert carried the two standing transients (Shelly EM fetch error 04:51:34, Tuyalight.lounge_led_2write rejection 05:00:00.798, chain unchanged since 2026-09-15). Device probes green: Shelly EM HTTP 200, rssi -73 dBm, both emeters valid, metering live at 1251.41 W / 12.52 W; both lightsonafter the rejected write. Two things the script's window could not see: 15 *arr connection-refused lines logged 05:00:09-05:00:34 sit just inside the 05:13 run's 70-minute window and would have been counted had the run started seconds later, andvacuum.roborock_qrevo_maxvis still off the LAN (unavailable since 2026-09-18T20:38:49Z, ping 100% loss) because roborock logs at INFO.home-assistant.log.faultlast changed 2026-08-25 00:02, no growth. Source: home-assistant.log, launchd.log, live curl/ping/HA states, cron executions.db. -
2026-09-19 (20:46 AEST): Fixed the false
ha=downin the shared cross-status tag.~/.hermes/bin/ha-tag-statusdefaultedTS_IPto pro-2 (100.97.49.102), a retired host, so every probe hit a closed port and printeddown ❌even though HA answers on127.0.0.1:8123. The script now walksCANDIDATE_HOSTS(HA_HOST override, 127.0.0.1, 100.80.168.13, then the retired pro-2 as last resort) and picks the first with an open 8123; the SSH secret fallback only runs when no host answers. Live result:203/268 ⚠️ 🏠in 0.54s (wasdown ❌). Same class of fault in~/.hermes/bin/nexus-tag-status: it SSHed to Pro (100.87.212.111, Tailscale offline, last seen 5h), so it printed nothing and exited 255 becauseset -euo pipefailaborted the script. It now probes the local Nexus API at127.0.0.1:8888first, falls back to Pro, and treats X-instancelast_heartbeatwithin 300s as online (X instances report heartbeats, notstatus). Live result:4/5 ⚠️(Nexus-X100 last heartbeat 2026-08-30).cross-statusnow readsnexus=4/5 ⚠️ | ha=203/268 ⚠️ 🏠 | gw-pro=ok | gw-pro2=offline | doge=offline, refreshed into~/.hermes/state/cross-status.{txt,json}. Root cause of the recurring wrong headline: the status scripts kept hard-coded host addresses from the retired Pro/Pro-2 topology while the services moved to this host. Verified:python3 -m py_compileon ha-tag-status,bash -non nexus-tag-status, both run live,cross-status-refreshrun. Source:~/.hermes/bin/{ha-tag-status,nexus-tag-status,cross-status},tailscale status, live HA and Nexus API reads,~/.hermes/state/cross-status.txt., hermes. -
2026-09-17 (20:35 AEST): identified the source of the recurring "HA lists 15 unavailable/unknown entities as critical candidates" line in the daily State of the Union. The 15 are 6 orphaned
automation.*registry entries (dishwasher_done, heat_pump_dryer_done, garden/roof/entrance camera motion alerts, roof_sun_announce_when_sun_is_on_roof) plus 9 driveway camera entities. Root cause of the 6: 20 automations were removed fromautomations.yamlandpackages/roof_sun.yamland replaced by dashboard-callable scripts inpackages/manual_controls.yaml(generated 2026-09-15, header dated 2026-09-15). HA still holds theircore.entity_registryrows (platform: automation,disabled_by: null), so at the 2026-09-15 09:29 AEST restart it restored each asunavailablewithrestored: true. Current split:automations.yaml9 ids,configuration.yamlinlinesqlite_journal_mode_bootstrap,packages/roof_sun.yaml1,packages/camera_ai_analysis.yaml3,packages/manual_controls.yaml19 scripts = 33 registry automation rows of which 20 are orphans. Two live dashboards still reference orphan entities:lovelace.command_centrepoints atautomation.smart_ac_cool_downandautomation.auto_vacuum_when_away, andlovelace.climate_energypoints atautomation.roof_sun_announce_when_sun_is_on_roof. Live HA at 20:35: 536/684 available, 148 unavailable/unknown; report at~/clawd/scripts/ha_unavailable_report.py. Not yet done, needs Dwayne's go-ahead: delete the 20 orphan registry rows and repoint the two dashboard cards at the replacement scripts. Source:~/.homeassistant/.storage/core.entity_registry,automations.yaml,packages/manual_controls.yaml,.storage/lovelace.*, live HA state read,ha_unavailable_report.pyrun., hermes. -
2026-09-16 (11:42 AEST): closed the recurring
sensor.roborock_qrevo_maxv_current_roomfalse attention inhermes_home_integration_sentinel.py. The 11:42 tick reportedattention, before 1 / after 1 / resolved 0, with aroborockreload returning HTTP 200 that did not clear the entity; the same shape appeared in 29 ticks over the previous 7 days, plus anokat 2026-09-16 01:03 AEST where a reload did clear it. The sensor is not a fault. HA history for 2026-09-16 01:02-01:42 UTC shows the state flippingunknown->Bedroom->unknown->Living room->unknownon 14-second to 25-minute intervals whilevacuum.roborock_qrevo_maxvstayedcleaningand healthy, so a single-tick read is a map-update gap, and a config-entry reload cannot fix it. Fix:bad_attention_entities()takes acurrent_room_streakcounter, persisted ascurrent_room_streakin~/.hermes/ops/state/home_integration_sentinel.json. The sensor is dropped from the attention list unless the vacuum readscleaningon that tick and the silent streak has reachedCURRENT_ROOM_PAGE_STREAK(3). When the vacuum entity is itself unhealthy its own entry carries the page, so the derived room sensor stays out. Two earlier drafts of this patch were wrong and the verification script caught both: the first advanced the streak twice per tick (before- and after-read), so the threshold tripped on the second tick instead of the third; the second let the older "not cleaning" rule swallow the vacuum-unhealthy case. Verified:py_compileclean. The checks now live as a durable suite,~/.hermes/tests/test_home_integration_sentinel_room.py, run with/Users/dwayne/.hermes/hermes-agent/venv/bin/pytest, 7 tests, 7 passed, sha256be9e6d1463f018a3d7b39d8302fe18e0364f1e1919aa97636b93dacac0c0c9cf. Coverage: gate table at streaks 0-5, room-named states,docked/idle/absent vacuum, tick-by-tick streak persistence asserting exactly one reload at tick 3, reset on report, and a falsification test that lo... [truncated] -
2026-09-15 (08:15 AEST):
ha_health_check.pyalerted on 18 ERROR lines in its 70-minute window. Live breakdown from the same log window: 9RequestHandler.connection_made()OSError: [Errno 22] Invalid argumentin a 12-second burst at 07:54:56 (aiohttptcp_keepalivecallingsetsockopton a transport the client already closed; 2 lines on 2026-09-12, 14 on 2026-09-14, 9 on 2026-09-15, cosmetic), 3command_linetimeouts onsensor.power_known_loads_report(HA logged "Updating Command Line Sensor Power Known Loads Report took longer than the scheduled update interval 0:01:00" at 07:55:53; the script itself runs in 0.34-0.43s standalone, so the timeout is HA event-loop contention, and the sensor still held 475.5 W), 3ipp.coordinatorprinter reachability timeouts, 1 websocket auth-phaseClientConnectionResetError, 1 haffmpegProcessLookupError, 1 cast failure. HA itself was healthy: state RUNNING on 2026.8.2, 73 of 690 entities unavailable (threshold 150),/api/statesin 0.003s. The cast line matters:automation.xai_daily_super_briefing(HA idxai_daily_super_briefing_0730, enabled) fired at 07:30:02 andFailed to cast media http://192.168.68.79:8123/local/xai-tts/xai_[redacted-id].mp3, a path the 2026-09-13 speaker-removal task removed from Hermes cron jobs but not from this HA-side automation. The file serves fine from the host (HTTP 200, 1,325,568 bytes, 0.04s), so the failure is the cast device not reaching the LAN URL. That message has recurred since 2026-09-11 (17Failed to cast medialines acrosshome-assistant.logand.1; earlier ones used192.168.68.116, which is no longer a host address). Soundbar history shows audio did play at 07:00 today (media_player.bose_soundbar_700idle -> playing -> idle at 21:00:09/13/21 UTC), so the 07:00 Morning Greeting speaker path is also still live. Driveway camera (camera.driveway_live_view,switch.driveway) remainedunavailablesince 2026-09-14T14:29:46Z with the device answering ping at 5... [truncated] -
2026-09-15 (08:35 AEST): The
ha-exposed-camera-stills-refreshcron job (835ebfa7498e,*/30 * * * *,no_agent=false, scriptha_expose_camera_stills_quiet.sh) reportedScript timed out after 300son its 08:02:22 AEST tick. The script is NOT broken and the stills are NOT stale: the same Python payload run by hand in the ha314 env took 2m24.739s and exited 0. The timeout is a cap mismatch plus host load, the globalcron.script_timeout_secondsis 300s andload averagesread 72.09 79.23 152.88 on 12 cores during the tick, against 6.18 20.84 87.89 when the manual run finished. Exposure is owned redundantly: the LaunchAgentcom.dwayne.ha-expose-camera-stills(StartInterval300,runs = 1903,last exit code = 0) runs the SAME wrapper every 5 minutes and wrotestatus.jsonat 2026-09-15 08:31:32 AEST with 5/7currentand 2stale_real. The cron job is the redundant copy. Consequence: two concurrent runs race on the per-run.candidate.jpgpaths and the cron leg eats a 300s kill on any loaded tick. Stillstale_realat 08:31:32:driveway(serving latest real20260915-060103_camera_driveway_real_still.jpg;camera.driveway_live_viewstateunavailablesincelast_changed2026-09-14T14:29:46Z) andhub_max(Nest API says stream not available, file from 2026-09-14 15:01:30).garden,pool,roofrefreshed viaha_snapshot;front_doorandfront_stairsrefreshed viawebrtc live(456542 and 59109 bytes) at 08:32, so the front-door WebRTC path that readstale_realat 08:03 recovered on its own. Disk is not the cause:/System/Volumes/Data93% used, 15 GiB free. Historical context only:ha_expose_camera_stills.launchd.outholds threeOSError: [Errno 28] No space left on devicefailures from 2026-09-11, all before the 08:35 reclaim; none today. Source:cron/executions.dbrows for 835ebfa7498e,cron/jobs.json,launchctl print gui/501/com.dwayne.ha-expose-camera-stills, manualha_expose_camera_stills.pyrun (08:23:58 paylo... [truncated] -
2026-09-15 (02:20 AEST): Driveway camera (TP-Link TC41, 192.168.68.55) dropped off the API layer.
camera.driveway_live_view,sensor.driveway_signal_level,switch.drivewayand thedriveway_led/_motion_detection/_person_detection/_tamper_detectionswitches all readunavailablesince 2026-09-15 00:29:46 AEST. The device is still on the LAN (ping 0% loss, TCP 554/443/2020 open) but an HTTPS GET returns no response (curl code 000 after 10s). Consequence: HA's snapshot path into the tplink camera raises haffmpegTimeout reading image/Timeout while waiting of FFmpeg, thenRuntimeError: read() called while another coroutine is already waiting for incoming datainasyncio.subprocess.communicate, logged asError doing job: Task exception was never retrieved (task: None). Counts from the live log: 13 lines between 2026-09-15 00:33:21 and 01:35:55, 11 on 2026-09-14, 5 on 2026-09-12.ha_health_check.pyreported 6 ERROR lines inside its 70-minute window. The exposed still refresh at 02:15:31 servesstale_realfor driveway from20260914-180043_camera_driveway_real_still.jpg; the other five cameras refreshed current. HA held 550/690 entities available. The crash pattern stopped after 01:35:55; standalone haffmpeg timeouts at 01:53:02 and 02:04:24 raised no job error. Next useful action is a physical power-cycle of the driveway camera; the remaining house surface is healthy, so no HA restart is indicated. Source:/Users/dwayne/.homeassistant/home-assistant.log, live HA/api/statesread,ping/nc/curlprobes,/Users/dwayne/.homeassistant/www/camera-stills/status.json., hermes. -
2026-09-13: Fixed the weekly
security-audit-weeklyfalse positive.~/.hermes/scripts/local_security_audit_report.py:51checked the retired LaunchAgent labelio.homeassistant.coreand reported "LaunchAgent missing or not loaded" every week while HA was healthy. The live label iscom.dwayne.homeassistant(launchctl PID 92617, matching the audit's own PID evidence); the old label survives only as archived plists (io.homeassistant.core.plist.disabled-docker-migration,.codey-pre-path-fix,.bak-fix-all-[redacted-id]). HA itself serves HTTP 200 on 127.0.0.1:8123. Changed the checked label tuple to("ai.hermes.gateway", "com.dwayne.homeassistant")and added a guard so alaunchctl listfailure reports "LaunchAgent state unknown" instead of falsely reporting both services missing. Verified: script now prints[SILENT](zero findings);~/clawd/state/local-security-audit.jsonfindings[]with firewall enabled, stealth on, Tailscale 100.80.168.13, no SSH listener; falsification test confirmed the check still reports a genuinely absent label (syntheticonly ai.hermes.gatewayinput produced the HA finding) while the live host produces none andio.homeassistant.coreis still absent. Note the cron resolves scripts from~/.hermes/scripts/(the~/clawd/scripts/copy is unreferenced and gitignored, still carrying the stale label);~/.hermes/scripts/is itself gitignored via.gitignore:2:*, so this fix is not version-controlled and lives only on disk. Source: livelaunchctl list, plist inventory, script source, script run and falsification probe 02:30 to 02:40 AEST this session., hermes. -
2026-09-12: Fixed two structural false-positive sources in
hermes_home_integration_sentinel.py. (1)network_indicatednow runs on a schedule-filtered log tail: newschedule_noise_free_log()drops *arr endpoint errors (localhost:17878radarr,localhost:18989sonarr,localhost:18686lidarr) during 05:00-00:00 AEST, whencom.dwayne.arr-scheduleholds those containers down and HA retries every poll; 194 such lines were in the 500-line tail, mislabelling every ticknetwork_attentionand printing a bogus network-repair instruction. Real non-*arr host faults still flag (verified with a syntheticCannot connect to host 10.0.0.5line). (2)bad_attention_entities()now excludessensor.camera_vision_*derivatives, since the camera entity is authoritative viaHEALTH_ALIASES. Hub Max sweep at 17:08:17 AEST returned ok and cleared the sensor;--forceat 17:14:38 AEST wrotenoop, before/after 0. Pre-fix backup:hermes_home_integration_sentinel.py.bak-pre-netfilter-20260912-171238. Source: sentinel source, live HA state read,~/.hermes/ops/state/home_integration_sentinel.json,/Users/dwayne/ARR/arr-schedule.log., hermes. -
2026-09-11: Hub Max vision sensor recovered via a manual
--camera hub_maxsweep (20:08:59 AEST, ok, summary 7/7 at 20:08); sentinel--forcethennoopwith zero bad entities. Identified two structural faults: thenetwork_attentionlabel came from radarr/sonarr endpoint errors (ports 17878/18989 publish no host port while the containers run), andsensor.camera_vision_hub_maxis motion-triggered only (event.lounge_display_motionlast changed 2026-09-11T03:33:03Z), so it reads stale between motion events. Also observed 98% disk use and one aborted sweep withErrno 28onvision_analysis.tmp. Source:~/.hermes/ops/state/home_integration_sentinel.json,~/.homeassistant/camera-vision.log,df -h,docker ps., hermes. -
2026-09-06 (weekly synthesis): Recorded camera cluster status 2026-09-05: Shelly EM + ONVIF cluster resolved; single persisted bad entity
sensor.x_camera_stream(mobile_app) remains, network_indicated back to true. House 509/640 available, Power back at 498W. Source:~/.hermes/ops/state/home_integration_sentinel.json., hermes. -
2026-08-24: Recorded Shelly EM + ONVIF camera cluster outage (2026-08-22 to 2026-08-24): air-fryer report blocked on HA fetch, sentinel reload loop resolving 0 of 19 entities, Power unavailable. Distinct from the older TP-Link camera incident. Source:
~/.hermes/ops/state/home_integration_sentinel.json, kg., hermes. -
2026-08-17: Hermes Cron sidebar dashboard restored (
/hermes-cron/fleet, iframe of/local/hermes-cron-fleet.html). Regenerated bycron_rigour_audit.py. Source: Dwayne asked to add it back., grok. -
2026-08-03: Superseded HA tunnel description, HA now directly reachable at 100.80.168.13:8123 (macbook-pro-3). Old Pro-2 host retired. Source: live reachability check, script audit, 2026-08-02., hermes.
-
2026-07-09: created from scripts inventory + cross-status, claude-cowork.
-
2026-09-20 (18:20 AEST, driveway camera RE-DIAGNOSED): The 18:01 sweep verdict ("driveway camera needs a device-side check, power/Wi-Fi/Nest app") is wrong on both counts, and the fault is a RECURRENCE of 2026-09-15. Two corrections. (1) Not Wi-Fi, not power, not Nest. The camera is a TP-Link TC41 at 192.168.68.55, not a Nest device, so the Nest app has nothing to show for it. It answers ping 0% loss and holds TCP 443/554/2020 open, and its ARP MAC
98:ba:5f:47:d3:79matches theDriveway TC41config-entry unique_id exactly, so it is on the LAN and identifiable. It is wedged at the API layer: on 554 it accepts the connection and returns nothing on 3/3 attempts, while its healthy same-model twinEntrance TC41(192.168.68.84) answers with 64 bytes on 3/3.homeassistant.reload_config_entryfor the entry re-armed the retry loop but did not recover it (stillunavailable; tplink log resets to "Retrying in 5 seconds"). A device reboot is not available to me: no controllable plug feeds it. So the physical power-cycle is confirmed as the fix, not a guess. (2) The report's "driveway" slot is a DIFFERENT camera from the entity named "Driveway camera". The sweep maps slugdrivewaytocamera.driveway_live_view(TP-Link, the wedged one), butcamera.home_homeis a Nest device whose registry name is literallyDriveway cameraand the sweep files it under slugfront_door. So the sweep reports seven cameras under names that do not match their devices. HA has also been failing this entry since at least 2026-09-19 06:38 (Unable to query the device: 192.168.68.55: TimeoutError), retrying every 600s, while siblings Entrance/Pool/Pergolux stay healthy. Timeline: camera stopped producing frames ~2026-09-14 12:49 (last frame timestamp), still frozen at mtime 2026-09-15 01:30, HA logged it down from 2026-09-19 06:38. This is the third wedge of this camera unit (prior records 2026-09-15 02:20 and 2026-09-20 06:03), each with the same port-open-but-silent signature, which... [truncated] -
2026-09-20 (06:03 AEST, camera vision sweep): 14 cameras, 17 captures, 4 failures, 0 actionable. Fresh stills at 05:59:33-05:59:38 AEST for pool, garden, roof (1280x720, embedded camera timestamps); front door refreshed via webrtc at 06:00. Two stale:
camera.driveway_live_view(unavailable, HA snapshot 404) served a still byte-identical (sha256 06ce21c4) across 20 consecutive sweeps since 2026-09-14 18:03, and its embedded timestamp reads2026-09-14 12:49:17PM; the TC41 at 192.168.68.55 answers ping (0% loss, 9.6 ms avg) and TCP 554, but HTTPS GET returns code 000 after 8 s, so the fix is a physical power-cycle, not a reload.camera.entryway_front_stairsserved the 18:00 still because WebRTC failed withwebrtc_offer_failed/ Nest429 RESOURCE_EXHAUSTEDon ExecuteDeviceCommand, the first 429 in at least 10 sweeps (earlier sweeps were allcurrentviawebrtc_live). Three 2689-byte dark placeholders (lounge_display, home_home, entryway_front_stairs) as before: unavailable evidence, not a scene reading. Mapped real-still entities stayed fresh, including hub_max (d0560825 vs 76c1b951 at 18:00). No people, vehicles, deliveries or damage visible. Source:~/.hermes/camera_sweeps/20260920-060305/manifest.jsonand published_*.jpg,/Users/dwayne/.homeassistant/www/camera-stills/status.json,ping/nc/curlagainst 192.168.68.55., hermes. -
2026-09-20 (07:20 AEST): fixed the recurring
globird-ac-optimizercron error ("HTTP 404: Entity not found" onautomation.smart_ac_cool_down). Cause: the legacy cool-down automation was replaced byscript.ac_cool_down_and_announceinpackages/manual_controls.yamlon 2026-09-15, butwinter_ac_baseline.pystill did an unguarded GET on the dead entity and let the 404 fail the run. Fix: wrap that GET and map HTTP 404 tocool_down_automation = "unavailable", matching the COOLING_DISARMED_STATES already accepted bywinter_ac_globird_controller.py. Verified by re-running the 07:00 phase: exit 0,ok: true,hvac_mode heat,target_c 22,readback_converged true,cool_down_automation unavailable. Also live:sensor.ac_seasonreadssummerwhile the winter controller keeps forcing heat, so the two Smart AC automations disagree about season andsmart_ac_seasonal_midday_boostcan fight the baseline. Ad-hoc verification (no suite exists for these scripts): a throwaway tempfile-based harness stubbedha_requestwith a real 404 RuntimeError and with a non-404 RuntimeError; all 6 checks passed, i.e. the 404 path exits 0 with a valid receipt mappingcool_down_automationtounavailable, and a non-404 HA failure still raises. Source:~/.hermes/scripts/winter_ac_baseline.py,~/.hermes/scripts/winter_ac_globird_controller.py, live HA state + rerun., hermes. -
2026-09-20 (15:07 AEST): HA core crashed and relaunched itself, taking the air-fryer watch down with it.
com.dwayne.homeassistant(launchd, PID 76289) segfaulted at 14:59:33 and launchd restarted it (runs = 2,last exit code = 139); HA reinitialised at 15:00:00.658 after 28.39 s. Crash report~/Library/Logs/DiagnosticReports/python3.14-2026-09-20-145933.ips:python3.14,SIGSEGVEXC_BAD_ACCESSat 0x0, faulting thread 3 throughlibunwind _Unwind_RaiseException->libc++abi __cxa_throw->_objc PyObjCErr_ToObjCWithGILState->CoreBluetooth -[CBCentralManager handlePeripheralDiscovered:]. An Objective-C exception raised inside a CoreBluetooth delegate callback crossed into CPython via PyObjC and killed the process; no graceful shutdown appeared in the log, only lifx entries endingCancelledError: Home Assistant is stoppingat 14:58:55. This is the first recorded HA segfault, and the first time thebluetoothintegration is identified as a crash source here. -
Consequence chain: the
tuyaconfig entry01KKZT8SVT8TGCYWYM63GS1M45(ap-000424.ccdec59da5af48ff92c321ab78a5c0e5.2145) failed setup at 15:00:00.646 withApiRequestException: network error:(-9999999) Remote api run unknown failed, leaving 65 tuya-family entitiesunavailable. That took out the Air Fryer plug family (sensor.apple_macbook_pro_power,_voltage,sensor.air_fryer,switch.apple_macbook_pro_socket_1,sensor.diffuser_total_energy), allunavailablewithlast_changed2026-09-20T05:00:00Z. Cronair-fryer-done-watch(0aeb740a318c,*/1 * * * *) then failed every tick from 15:00:37 to 15:05:44 withobservation_unavailable: sensor.apple_macbook_pro_power state is unavailable,failure_streak5. The 14:59:34 tick failed differently, withHTTP Error 404: Not Found(entity not yet registered mid-restart). -
Fix applied and verified:
homeassistant.reload_config_entrytakesentry_id, notconfig_entry_id- the latter returns HTTP 400. Called withentry_id=01KKZT8SVT8TGCYWYM63GS1M45at 15:06:30; entry state returned toloadedand the five affected entities all read live values: power 0.0 W, socketon,sensor.air_fryer1306.08 (junk helper, listed in the script'sBAD_HELPERS),sensor.diffuser_total_energy0. The 15:06:43 tick completed. Unavailable load fell from 221 of 669 entities at 15:03 to 177 at 15:07. -
Not a tuya-cloud flake this time: the 15:00:00 failure is a cold-start API exception inside the crash-restart window, distinct from the long-standing sporadic
light.turn_onwrite rejections. Standing roborock LAN absence and the driveway TC41 camera remain unchanged. Source:~/.homeassistant/home-assistant.log,home-assistant.log.1,launchd.log,launchctl print gui/501/com.dwayne.homeassistant, crash.ips, live HA/api/statesandconfig_entries/entry,cron/executions.db. - hermes. -
2026-09-21 (06:03 AEST, camera vision sweep): 14 cameras, 17 captures, 4 failures, 0 actionable. Fresh stills 06:00 AEST for pool, roof, garden, front door, front stairs, hub max (all byte-distinct from 18:01 yesterday; pool/roof/garden differ every sweep). Pool scene unchanged over 12 h: circular metal pool still covered/empty, deck framing unsurfaced, stone retaining wall and white modular building as before, no people. Roof/pergola still an active jobsite (exposed joists, plywood sheet, blue tarp, stacked blocks, wheelbarrow, mixer), not storm damage. Driveway camera still frozen: published still is byte-identical (md5 3749c787) across 14 consecutive sweeps since 2026-09-14 18:03 and its embedded watermark reads 2026-09-14 12:49:17 PM. Live re-probe of 192.168.68.55: ping 0% loss at 15.5 ms, TCP 443 and 554 open, HTTPS GET returns 000 after 8 s; sibling Entrance TC41 at 192.168.68.84 answers on 554 - same port-open-but-silent wedge as 2026-09-15 and 2026-09-20, fix remains a physical power-cycle. Three 2689-byte dark placeholders (lounge_display, home_home, entryway_front_stairs) are unavailable evidence, not scene readings. No people, vehicles, deliveries or damage visible in any fresh still; wheelie bins (yellow lid and dark lid) at the kerb in the 06:00 front-door still. Source:
~/.hermes/camera_sweeps/20260921-060330/manifest.jsonand published_*.jpg,~/.homeassistant/www/camera-stills/status.json, md5 comparison across~/.hermes/camera_sweeps/2026*/*driveway_real_still.jpg, live ping/nc/curl against 192.168.68.55 and .84., hermes. -
2026-09-23 (12:03 AEST, camera vision sweep): 14 cameras, 17 captures, 4 failures, 0 actionable. Fresh stills 12:02-12:03 AEST for pool (12:02:50), roof (12:02:51), garden (12:02:48), front door, front stairs, hub max; every one byte-distinct from both the 06:03 and the 2026-09-22 18:03 set, so the whole sweep path is current except the driveway. Two material scene changes at midday, both firsts in the recent record: (1) the pool is UNCOVERED with open blue water visible, against the dark taut cover in the 06:00:47 still eight hours earlier - so the cover came off during the morning; (2) two workers are on site at 12:02, one walking the bare deck framing and one leaning over the pool edge, where every sweep from 2026-09-20 to this morning's showed no people. Deck framing around the pool is still bare joists, planks not yet laid, cabin exterior finished, blue tarp and black case still on site. Roof/pergola at 12:02:51 remains an active jobsite: one worker bent over the framing, plywood work sheets, red extension cord, stacked concrete blocks along the fence, wheelbarrow and mixer, no solar panels. Garden still (12:02:48) shows a boxy dark off-road vehicle and a white SUV with an unresolved door logo at the kerb, plus a white car further down the street; front door, front stairs and hub max show no people, no parcels, no animals. Driveway camera now frozen for 9 days:
published_driveway.jpgsha25606ce21c4be7a86edf44adf8460097bdb7eaa5a059e914785d0814d42a16393c1is byte-identical across 19 sweep directories (the newest distinct driveway still on disk is20260914-060040_camera_driveway_real_still.jpg), and its embedded watermark still reads2026-09-14 12:49:17PM. Live re-probe of192.168.68.55: ping 2/2, 8.2-55.9 ms, TCP 443 and 554 both open, HTTPS GET returns code 000 after 8 s. HA tplink log holds 436Driveway TC41lines, latest retry2026-09-23 11:56:02withUnable to query the device: 192.168.68.55: TimeoutError(). Three 2689-byte dark placeholders (lounge_display, home_home, entryway_front_stairs) remain unavailable evidence, not scene readings;hub_maxstill yields a real 125 KB WebRTC frame despite its Nest placeholder failure. Source:~/.hermes/camera_sweeps/20260923-120354/manifest.jsonand published_*.jpg,~/.homeassistant/www/camera-stills/status.json, sha256 comparison across~/.hermes/camera_sweeps/2026-09-*/*driveway_real_still.jpg,~/.homeassistant/home-assistant.log, live ping/nc/curl against 192.168.68.55. - hermes. -
2026-09-26 (19:50 AEST, pool tiles went "Unavailable" - four bridge defects found and fixed): The user's screenshot showed all six tiles as Unavailable. Root causes, all in the bridge: (1) The Tuya debug log level is runtime-only and a Home Assistant restart reverts it. The keepalive cron runs every 20 minutes, so after HA restarted at 19:24 the tiles were dark until the next tick. The bridge now re-arms
tuya_sharingdebug itself at startup and in its publish loop, so the cron is a backstop rather than the only path. (2) A quiet device must not blank the tiles. Any stale window published availability=offline, which renders every tile Unavailable even though the last reading was known. Availability now goes offline only when NO reading has ever been parsed; a reporting gap holds the last values. (3) The energy total was memory-only and reset to 0 on every restart. That is wrong for atotal_increasingmeter and made "Energy since install" drop to zero. Now persisted to~/.hermes/state/pool-heatpump-bridge.jsonand reloaded on start. Replay deliberately does NOT integrate energy (the restored total already covers that window) and resets the integration baseline afterwards, so it cannot double-count. (4) Replay read only the live log. HA rotates on restart, so--replay-mb 10found 1 payload line while 4671 sat inhome-assistant.log.1. Replay now reads rotated logs too, matched strictly as.log/.log.Nso the strayhome-assistant.log.faultis excluded. (5)KeepAlive {SuccessfulExit: false}+ exit 0 on SIGTERM = never respawned. launchd read a clean stop as "finished for good", which is how the tiles stayed dead after a restart. The bridge now exits non-zero when stopped by a signal (still 0 for--once), so the LOADED job respawns it without needing a plist reload.launchctl bootstrapis blocked in-process by the Hermes gateway guard, so a plist change alone cannot be applied from an agent run. Verified live: SIGTERM -> log line "stopped by signal; exiting non-zero so launchd restarts the job" -> launchd respawned with a new pid. Harness/private/var/folders/.../T/hermes-verify-keepalive.py, 69/69. Also learned: an APIlovelace/config/savecan report success while.storage/lovelace.*is never written (six dashboards once reported success with an unwritten file, and an HA restart then dropped the block). Verify the FILE, not the API reply. The dashboards harness now asserts persistence to.storageas well as the API config. Two bridges must never run at once: each integrates its own energy total and overwritespool-heatpump-bridge.json, so the kWh figure jumps and can double-count. The bridge now takes anfcntl.flockon~/.hermes/state/pool-heatpump-bridge.lockand refuses to start a second copy (--onceskips it so a probe still works). Open the lock withos.open(..., O_CREAT|O_RDWR), NOTopen(path, "w"):"w"truncates before the flock is attempted, so a refused second instance erases the live pid. , hermes. -
2026-09-28 03:00 AEST: d3-matrix no longer invents solar generation. Correction from Dwayne: "there is no solar roof ct1 is the whole house circuit, ct2 is the aircon circuit". The page behind
http://100.80.168.13:8123/d3-matrixis/local/d3_dashboard.html(iframe in the storage-moded3-matrixdashboard). It declaredconst solarW = 1800as a "Pergolux Solar Roof" source, computedtotalInW = solarW + mainsW, drew it as a supply in the energy sankey, and printed "1.8 kW Active Solar Flow" on the iso roof polygon and "10:00-14:00 Free Solar" on the tariff tile. No solar entity exists in HA at all (checked all 683 entities; no solar/pv/generation/export sensor), andbinary_sensor.sun_on_roofis a sun-GEOMETRY sensor (azimuth/elevation against east/west windows), not generation. Correct model: CT1 (sensor.shelly_em_channel_1_power_2) = whole house, the only measured source; CT2 (sensor.shelly_em_channel_2_power) = a sub-meter on the aircon circuit, a branch INSIDE the CT1 total, so it is a load below the bus and must never be added to the total. Also removed: hardcoded placeholders (currentMainsW = 1955,currentAirconW = 1210, the matching stateCache entries and the 1955 in the badge/iso text) that painted an invented diagram before live data arrived. Added aliveDataSeengate: the sankey now renders "Waiting for live readings from the whole-house meter" until a real CT1 reading lands,stateCacheuses an em dash, andMath.min(currentAirconW, mainsW)stops CT2 ever exceeding the whole house. The tariff line now reads "Free window 10:00-14:00" (GloBird FOUR4FREE, perpackages/electricity_pricing.yaml) instead of implying solar. Node--checkon the inline script exits 0; served copy matches disk; iframe bumpedv=12.0.1-vision-20260909->v=12.1.0-nosolar-20260928. Backupwww/d3_dashboard.html.bak-nosolar-20260928-025018(verified to still contain the solar fiction, so it is a true revert point). Ad-hoc harnesshermes-verify-393g4cok.py33/33 exit 0. , hermes. -
2026-09-28 02:40 AEST: arr-stack launchpad URLs now use the Tailscale IP, not loopback. Dwayne asked that
http://100.80.168.13:8123/arr-stackuse tailnet IPs instead of local. Its Media Ops "Launchpad" held 7custom:button-cardtap_action.url_pathvalues onhttp://127.0.0.1:<port>(Plex 32400/web/index.html, Overseerr 15055, Sonarr 18989, Radarr 17878, Lidarr 18686, SABnzbd 18090, Prowlarr 19696). Loopback resolves to whichever device renders the dashboard, so the buttons only worked on the HA host itself; from a phone they pointed at the phone. Rewritten tohttp://100.80.168.13:<port>, preserving scheme, port and path. All 7 then answer HTTP 200 on the tailnet host, including Plex/web/index.html. arr_stack is mode: storage, so the lovelace WebSocket API edit path is valid here (contrast energy-comfort, which is mode: yaml and refuseslovelace/config/savewith "Not supported"). Backup of the prior config saved as JSON to.storage/dashboard-arrstack-backup-20260928-023540/arr-stack.json- a.storage/lovelace.*file copy is not a reliable revert for a running HA, so the config itself is saved and restored through the API. Scanned all 17 dashboards: no other dashboard carries a loopback service URL, so no follow-up needed. Ad-hoc harnesshermes-verify-cjic9zvt.py27/27 exit 0, with falsification on the loopback predicate. Note: curl shows 307/302 for Overseerr/Prowlarr without-L; those are live services. , hermes. -
2026-09-27 22:15 AEST: energy-comfort is a YAML-mode dashboard - do NOT try to save it over the lovelace WebSocket API.
configuration.yamldeclareslovelace: dashboards: energy-comfort: mode: yaml, filename: exec_dashboard/dashboard.yaml.lovelace/config/savereturns{"success": false, "error": {"code": "error", "message": "Not supported"}}for YAML dashboards, and the read still returns a config, so a read-then-save round trip looks plausible and silently fails. The cache-buster therefore lives in~/.homeassistant/exec_dashboard/dashboard.yaml(- type: iframe, url: /local/exec_dashboard/index.html?v=N), not in.storage.add_pool_to_dashboards.pyalready documents that it skips energy-comfort for this reason; that script's WS save path is only for storage-backed dashboards. To update the page:refresh.py(runsha_exec_extract.pythenbuild.py, writeswww/exec_dashboard/index.html+exec_dashboard_data.json), then bump?v=indashboard.yaml, then reload. The page fetchesexec_dashboard_data.json?t=' + Date.now()withcache: 'no-store', so the JSON self-busts; only the HTML needs the manual version bump. Reload viahomeassistant/reload_core_config(notlovelace/reload_resources, which does not re-read the YAML file). Verified 2026-09-27 22:08: bumping v5 -> v6 and reloading made the live WS read report?v=6; the static page (200, 63629 bytes) and its JSON (200, 39993 bytes) are reachable without the login wall because they sit underwww/. Ad-hoc harnesshermes-verify-razl3368.py24/24 exit 0. , hermes. -
2026-09-27 21:20 AEST: Dead power-sensor mappings removed; the unattributed residual is now labelled. Dwayne asked what was using ~490W. Answer: a standing 350-380W always-on base, plus a report defect. Evidence: over 4 days CT1's hourly MINIMUM never falls below 273W (typical 350-450W), and after the aircon was switched off at 20:44:13 CT1 dropped exactly 713W (1235 -> 522) with CT2 confirming it, so the aircon is excluded. The 10:00-14:00 pool window takes CT1 to ~4700W, so it is excluded too. Only the fridge (
sensor.samsung_fridge_power, 77W) and towel heater (sensor.meir_towel_heater_current_consumption, 25W) are metered directly; the cameras (33W) are modelled. Everything else is unattributed. The report defect:power_baseline_monitor.pyprinted "Unavailable sources: 6", implying temporarily missing sensors. They never existed.sensor.data_centre_current_consumption,sensor.heat_pump_dryer_current_consumption,sensor.entertainment_hub_current_consumption,sensor.washing_macine_current_consumption,sensor.smart_plug_current_consumption,sensor.hair_dryer_powerAND a seventh I had missed,sensor.nano_room_energy_power, all have zero rows in the recorder. They were a retired integration's names hard-coded into the sums, each contributing 0 W and making the accounting look complete. Note my first report to Dwayne namedsmart_plugamong the six; the live template actually carried seven dead terms. Removed from:~/.homeassistant/configuration.yaml(power_tracked_devicestemplate, 14 terms -> 8; plus an orphanedintegrationplatform onsource: sensor.hair_dryer_power),packages/dashboard_compatibility.yaml,~/.hermes/scripts/power_baseline_monitor.py(measured_devices),ha_power_known_loads.py(DIRECT_POWER_ENTITIES),watchdog.py. Backups*.bak-deadpower-20260927-205545. Never substitute a kWh sensor into a W sum: the only surviving dryer/hair-dryer entities (sensor.tuya_dryer,sensor.hair_dryer_total_energy_2) aredevice_class: energy, so there is no real W meter for those loads and removal is the only correct option. Template reloaded; livesensor.power_tracked_devicesequals its 8 surviving terms (122 = 122);check_configexit 0. Report label changed to "Dead sensor mappings" and a new note explains a large Unknown as real draw. Ad-hoc harnesshermes-verify-kx6l90sl.py50/50 exit 0, including a tokenize-based check that dead ids survive only inside explanatory comments, not as live mappings. , hermes. -
2026-09-27 21:05 AEST: Two traps found while disabling the aircon (companion to the entry above).
automation.triggerBYPASSES the off state. Callingautomation/triggerwithentity_id: automation.smart_ac_winter_heat_baselineran the disabled automation and forcedclimate.panasonicback to heat (verified live; the 33/33 harness had asserted only that the entity readoff). The bypass is deliberate, and nothing in the house callsautomation.triggeron these automations, so it is not a live risk. The real trigger path IS gated:_async_disable()incomponents/automation/__init__.pycallsself._async_detach_triggers(), so an off automation does not fire from its own time or state triggers. Proven empirically: with the AC put intodry(the baitheat_only_guardwatches for), the off automation did NOT force heat back. When asserting a disabled automation is inert, check the entity is off AND that no caller usesautomation.trigger; the entity state alone is not the whole claim.climate.set_temperatureon the Panasonic flips the mode. Setting a target of 22 while the unit wasoffbrought it back asdry(logbook: our own call at 10:50:48, no automation context). To leave this unit idle, issueset_hvac_mode: offALONE and do not follow it with a temperature write. A cleanset_hvac_mode: offheld for 40s with target 22 andhvac_action: off. , hermes.
-
2026-09-27 20:50 AEST: Aircon profile disabled and climate.panasonic turned off at Dwayne's request. The aircon is
climate.panasonic. Its "profile" had THREE independent drivers, and stopping only one leaves the others free to turn it back on:- cron
db57480d29ddheating-profile,*/15 10-14 * * *->heating_profile_report.py(slots 10:00/11:00/13:15/14:00) - cron
b9d72391d9cfglobird-ac-optimizer,0 7,11,13,14,23 * * *->winter_ac_globird_controller.py(slots 07:00/11:00/13:00/14:00/23:00). This was the real culprit: it ran daily at 07:00 and forced heat. - HA automation
smart_ac_winter_heat_baselineinautomations.yaml(same slots + a heat_only_guard that re-forced heat whenever the AC left heat mode).automation.smart_ac_seasonal_midday_boostwas already off. All three stopped: both cron jobs paused (enabled: false,state: paused), the automation'sinitial_stateflipped true -> false, and both scripts' slot tables emptied (heating_profile_report.pyprofile_target()returns None; winter_ac_globird_controller.pyPHASES = {}) so a manual run or a resume cannot drive the aircon either.automations.yamlbacked up toautomations.yaml.bak-airconprofile-*;check_configexit 0;automation/reloadapplied. Durability: HA's automation component givesinitial_stateprecedence over the restored state ("initial_state %s overridden from config initial_state"), so the flippedfalsesurvives a restart even though.storage/core.restore_statestill records the automation ason. Verified against the installed component atminiforge3/envs/ha314/lib/python3.14/site-packages/homeassistant/components/automation/__init__.py. Cron nuance: a paused job keeps a stalenext_run_at(it still showed 23:00 tonight).next_run_atis NOT the dispatch gate:cron/jobs.py::_get_due_jobs_lockedskipsif not job.get("enabled", True). Assertenabled/state, nevernext_run_at, when checking that a paused job will not fire. Readers that do NOT command the AC and were left alone:hermes_home_integration_sentinel.py, bothwatchdog.py,event-chains.py(zeroset_hvac_mode).script.ac_warm_up_and_announce/ac_cool_down_and_announceinpackages/manual_controls.yamlare manual-only, not scheduled. To re-enable: resume both cron jobs and setinitial_state: true, then restore the slot tables. , hermes.
- cron
-
2026-09-27 18:30 AEST: Pool water temperature is now sampled only 10:00-14:00.
sensor.pool_water_temperature(trigger-based template sensor,unique_id: pool_water_temperature_sampled) samplessensor.pool_heat_pump_water_temperatureonly inside that window and holds the last in-window reading the rest of the day. Reason, from the recorder: readings run 17C at 10:01, reach 23C by 12:03, then decay to 17C by 16:04. Outside the window the unit reports stagnant pipe temperature, not pool water. Trigger-based is required, not optional: a plain template sensor cannot hold a value, because whenever the template rendersunknownitsthis.statebecomesunknowntoo, so there is nothing to carry forward. The sensor was seeded once at 23.0C from the last genuine in-window reading (27 Sep 12:03). Consumers repointed to the sampled entity:exec_dashboard/ha_exec_extract.py(POOL_PINNED["water_temp"]) and the pool block on 8 dashboards viaadd_pool_to_dashboards.py(WATER_ENTITY,MARKER,LEGACY_MARKER). Two bugs found while doing it: the dashboard strip routine walked onlycardsand neversections, so every dashboard ended up with two pool blocks; and the extractor resolved sensors throughstatistics_metaand the short-termstatestable, so a sensor created minutes earlier had no hourly bucket and no recorded row and silently read as missing.states_metaholds onlyentity_id(no unit column) - the unit comes fromstate_attributes.shared_attrs. Both fixed; the extractor falls back to a read-only API call for live values. HA accepts the config (/Users/dwayne/.homeassistant-venv/bin/hass --script check_config, exit 0); applied viatemplate/reload. , hermes. -
2026-09-27 18:05 AEST: FIXED (approved): the
power_tracked_devicestemplate now counts the real pool loads.configuration.yamlunique_id: power_tracked_devices: thepoolterm moved from the deadsensor.smart_plug_current_consumptiontosensor.home_entertainment_power, and a newpool_heat_pumpterm forsensor.pool_heat_pump_powerwas added to the sum. Applied viatemplate/reload(no HA restart). Backupconfiguration.yaml.bak-powertracked-20260927-175708(sha256 of the pre-change filef93bde65e4c32291e9abe797bdcfedf36c2d0c1ef2bcdc3111c1e5483aa3ff09). Validated with/Users/dwayne/.homeassistant-venv/bin/hass --script check_config -c /Users/dwayne/.homeassistant(exit 0) - this is the interpreter HA actually runs from (2026.8.2); the ha314/ha313 envs are too old and fail with a device_registry version error that is not a real config fault. Controlled proof: at the heat pump's observed peak (1892 W) plus the pump (1392 W) the old template reported tracked 756 W, the new one 4040 W, so 3284 W of pool load is now attributed instead of sitting in Unknown. Dashboard iframe bumped to?v=5. , hermes. -
2026-09-27 17:40 AEST: The authoritative "Tracked" figure is a Home Assistant template, not the Python scripts.
configuration.yamlunique_id: power_tracked_devicessums the CT2 aircon submeter plus a hard-coded list (fridge, entertainment, dishwasher, dryer, pool, data centre, washing machine, nano room, hair dryer, air fryer, towel heater, modelled) andsensor.power_untrackedsubtracts that from CT1. The template'spoolterm points atsensor.smart_plug_current_consumption, which no longer exists (404), and it has no term forsensor.home_entertainment_power(the real pump plug, ~1.4 kW) orsensor.pool_heat_pump_power(bridged, peak 1.9 kW). Editing the Python mapping alone does NOT change the reported Tracked/Unknown figures - that requires editing this template, which is a live HA config change and needs explicit approval plus an HA restart to load. Contract gotcha:Measuredin the baseline message is a residual (tracked minus aircon minus modelled), NOT the sum of the real meters. Do not assume the displayed figures decompose as measured + modelled = tracked. , hermes. -
2026-09-27 17:20 AEST: Pool loads are now real, not modelled, in the power accounting. Two genuinely metered pool loads existed but were unattributed, so they inflated the untracked residual.
sensor.pool_heat_pump_power(MQTT bridge, inverter heat pump, peak 1892 W, energy counter 7.19 kWh) andsensor.home_entertainment_power(friendly name "Pool pump", the circulation pump plug, ~1.04-1.39 kW, runs ~10:00-14:00) are now wired intoDIRECT_POWER_ENTITIESinha_power_known_loads.pyand the measured set inpower_baseline_monitor.py. Both sit inside CT1, so they must be subtracted from the residual, never added on top. Verified by correlation: on 26 Sep the heat pump drew ~1800 W while CT1 showed ~2500 W with aircon at ~20 W, so the whole-house meter contains it. Dead mappings no longer hide loads:sensor.smart_plug_current_consumption,sensor.data_centre_current_consumption,sensor.entertainment_hub_current_consumption,sensor.washing_macine_current_consumption,sensor.heat_pump_dryer_current_consumption,sensor.hair_dryer_powerandcamera.front_doorall read 0 W because the entities do not exist. A missing entity is indistinguishable from a genuine 0 W, so both scripts now report missing targets (dead_targets()/missing_direct_entities). Also corrected two wrong camera labels:garden_real_stillis the garden camera andpool_real_stillis the pool camera. , hermes. -
2026-09-26 (19:15 AEST, pool heat pump live tiles added to 8 dashboards; HA dashboard-save mechanism): The confirmed bridge entities now appear as live tiles on the environment and equipment dashboards: climate-energy, energy-ops, command-centre, home-daily, home-dashboard, dashboard-areas, floor-heating, maintenance-ops. Each gets a section whose cards are a heading ("Pool heat pump") followed by six flat tiles: Pool water, Set to, Heat pump power, Coil, Energy since install, Running. Left off by design: arr-stack, media-desk, party, cameras, hermes-cron, d3-matrix, digital-twin, colima-memory, dashboard-map (media/party/camera/cron/iframe/VM-memory surfaces, where a pool reading is noise). Script:
~/.hermes/scripts/add_pool_to_dashboards.py(--list, --apply, --only, --revert). 97/97 ad-hoc harness/private/var/folders/.../T/hermes-verify-i67kksv2.py. KEY MECHANISM, how to edit a dashboard on a RUNNING Home Assistant: go through the WebSocket API, not the storage file.{"type":"lovelace/config","url_path":"<url_path>"}to read and{"type":"lovelace/config/save","url_path":...,"config":...}to write, overws://127.0.0.1:8123/api/websocketwith the long-lived token. Theurl_pathis the dashboard's url_path fromlovelace_dashboards(e.g.climate-energy), NOT the storage filename (lovelace.climate_energy) and NOT thekeyfield inside the storage file (that is the filename). A file-level revert does NOT work while HA is running: HA keeps the dashboard in memory and rewrites.storage/lovelace.*, socp-ing a backup over the file is silently overwritten. The only revert that holds is saving the config back through the same API. Backups are therefore stored as aconfigs.jsonmanifest keyed by url_path, plus a plain file copy for inspection only. Section shape that renders: a section'scardsshould be aheadingfollowed bytiles as DIRECT siblings. Nestedgridinside a section renders inconsistently across sections-based views; the house pattern onhome_dailyis flat.trend-graphis an established tile feature in this install (present in pre-existing 2026-08 backups), so it is safe to use. , hermes. -
2026-09-26 (17:20 AEST, pool heat pump datapoints: dp112 = power CONFIRMED, dp105 = temperature, E6 CLEARED): Screenshot
img_06698aede562.jpg(app, 17:18) matched against the live datapoint stream. CONFIRMED dp112 = power in WATTS. The app read0.624 kW;dp112read exactly624at 17:18:11, and 601 at 17:18:09. The unit then ramped624 -> 1598over 90 s while working. That ramp is the signature of an inverter compressor, and it explains the 0..1957 span and the 0 reading while idle. This REVERSES the earlier "not the power figure" note, which rested on my assumption about the timestamp of the E6 screenshot rather than a verified simultaneous read, that assumption was wrong. dp105 = a TEMPERATURE, not the flow rate. It climbed0 -> 65 -> 97continuously as the unit heated. It passed through 65 at 17:18:09, which is the same number as the app's 65% flow, but it kept rising past it, so the overlap was coincidence. Coil/discharge temperature stands (0 idle, 100+ under load). The app's 65% flow is NOT carried by any datapoint. dp101 is NOT the E6. The app now shows NO alert at all whiledp101still readsTrue(unchanged since 17:12:04). So dp101 tracks the unit being active, not the fault code. Confirmed by absence. E6 has CLEARED. The app now shows Heating, flow 65%, water 24 C, setting 26 C, no alert. The unit is heating normally. The E6 water-flow hypothesis from 16:35 is therefore NOT the current state; the earlier fault has resolved.dp103=24 anddp107=26 both match the app exactly, reconfirming both. Final map: dp103 water temp, dp105 coil/discharge temp, dp107 set temp, dp112 power W, dp101 active status (not fault), dp110 heartbeat/status (unresolved), flow % unreported. , hermes. -
2026-09-26 (17:15 AEST, pool heat pump datapoints: CONFIRMED by controlled change): The correlation method above produced the datapoint map, and two controlled app changes confirmed it. CONFIRMED dp107 = set temperature. Dwayne nudged the app setpoint 26 -> 29 at 17:09:07;
dp107appeared for the first time in the whole log reading29(twice, same second) and nothing else moved. He then changed it back at 17:11:43;dp107read29 -> 28 -> 26in the same second. Two independent changes, two matching reads: the mapping is proven, not inferred. The setpoint is only pushed when it changes, which is why it never showed up in 3443 prior events. CONFIRMED dp103 = current water temperature. Only ever reads 24 or 25 across all events; the app showed 25. A 1-degree band is right for a pool. dp101 = a fault/status flag, mapping now ambiguous (superseded reading below). First flippedFalse -> Trueat 14:18:31, about 18 minutes after the circulation pump's 14:00 switch-off; E6 is a no-flow safety fault, so a flag asserting shortly after flow stops fitted. The 17:11 power toggle broke the clean "E6 fault" reading: dp101 wentFalseat 17:11:54 (the power-off), thenTrueat 17:11:58, thenTrueagain at 17:12:04. A pure fault flag would not clear on a power press and then re-assert 4 seconds later on its own. Honest state: dp101 is a status/fault bit tied to the unit being active, not proven to be the E6 alone. The app's current text is the only way to settle it. guess dp105 = coil / refrigerant temperature. 758+ events, spans 0..104, sat at 0 while idle, then climbed 0 -> 28 -> 80 within two minutes of the unit starting. A real analogue sensor, not the setpoint. unresolved dp112, dp110. dp112 spans 0..1957. Its pump-ON band is narrow (1003..1054, 42 events) and its pump-OFF range is broad (0..1957, 1308 events), so it does NOT cleanly separate on pump state and is NOT the app's power figure (the app read 0.000 kW while dp112 read 1621). dp110 toggles 0/32 several times a second, far too fast to be a reading, heartbeat or run-status bit. Note: after the setpoint change the unit started: dp105 0 -> 80 and dp112 0 -> 1140 over ~2 min, with setpoint 26 above water 24-25, i.e. real heating demand. The 17:11 power on/off/on did not move the setpoint, dp103 or dp105's trend, dp105 stayed hot (73..84) and dp112 stayed up (851..1254), so the toggle did not actually stop the unit; it only exercised dp101. No datapoint carries the app's power figure: the app read 0.000 kW while dp112 read 1621, so dp112 is not kW. No datapoint has read 26 as a mode or a boost value, so silent/auto/heating/timer are either not reported or share an encoded field. Datapoints are now: dp101 status/fault (ambiguous), dp103 water temp, dp105 coil temp (guess), dp107 set temp (CONFIRMED), dp110 heartbeat/status (unresolved), dp112 unknown analogue. 3443 heat pump events in the current log;/tmp/tscan+/tmp/tsdkprobe venvs removed oncepool_dp_window.pysuperseded them. Scripts:~/.hermes/scripts/pool_dp_window.py(window vs contrast, reverts debug itself),~/.hermes/artifacts/pool_dp_watch.py(snapshot/watch). 34/34 ad-hoc harness. , hermes. -
2026-09-26 (17:05 AEST, pool heat pump datapoints: read-only identification by correlation): Since Tuya serves no schema for product
1ovawewadpk2jiaw, its five datapoints cannot be named from the API. They can be named by correlating them against a ground-truth signal, and the pool circulation pump is exactly that:sensor.home_entertainment_powerdraws ~1393 W daily 10:00-14:00 and 0 W otherwise. Method (read-only, no new Tuya session): do NOT open a secondtuya_sharingMQTT client,mq.pybuildslink_id = tuya-device-sharing-sdk-python.<uuid>and requests a server-issuedclientId, andmq.py:65notes a client "retry with its now-stale client_id", so a competing session risks invalidating Home Assistant's own link. Instead turn onlogger.set_level({"tuya_sharing": "debug"})at RUNTIME (log-level only; thelogger:block inconfiguration.yamlhas no debug lines, so a restart reverts it with nothing to undo on disk) and read the raw payloads the manager already logs:payload-> b'{"protocol":4,"data":{"devId":"...","status":[{"dpId":112,"t":...,"value":...}]}}'. The devId and its datapoints are in the same JSON object, so attribution is exact, unlike grepping a dpId, which mixes devices. Debug costs ~1 MB/hour of log growth. Scripts:~/.hermes/artifacts/pool_dp_watch.py(snapshot / watch) and~/.hermes/scripts/pool_dp_window.py(window vs contrast correlation, reverts the log level when done). The five datapoints:101(False/True),103(24/25),105,110(0/32),112. Partial identification, and its limit: today's log only starts 12:55:52 because debug was switched on then, which catches the last minutes of the pump-on window but not its start. In 12:55-14:00 (pump ON) onlydp112reported, with a tight band of 9 distinct values (1003-1054). After the pump stopped at 14:00,dp105anddp112both moved continuously,dp105over 0-104 anddp112over 0-1957. So the pump-on band does NOT separate cleanly and the earlier hypothesis that these are flow readings is NOT confirmed. What it does show is thatdp105/dp112are continuous analogue readings, not the on/off flag: the only clean boolean isdp101.dp110's 0/32 alternation at ~0.5 Hz is far too fast to be a temperature and is unconfirmed. Do not write to these without identification: localtuya writes values, and guessing the setpoint (a plausible candidate isdp105, which spanned 0-104, against the app's 26C setpoint) could command the pump wrongly. A read-only DPS-to-candidate map should be established first by changing ONE setting in the Tuya app and watching which number moves. Capture armed: cron job1179f63110fdpool-heatpump-dp-window, one-shot2026-09-27T17:00,no_agent, scriptpool_dp_window.py, reads 10:00-14:00 vs 14:10-16:30 from tomorrow's log.tuya_sharingdebug is intentionally left on so the window is recorded; revert after the capture. Sources: live logger level changes, HA log payload lines, recorderstatesread-only,.storageregistries, 2026-09-26 16:35-17:05 AEST., hermes. -
2026-09-26 (16:35 AEST, pool heat pump: why HA shows nothing): The Tuya pool heat pump
climate.inverter_pool_heat_pumphas been stuck at stateunknownwith nohvac_modesand nocurrent_temperature. It is NOT an integration fault and NOT a connectivity fault. The Tuya sharing API serves an empty device specification for this product, so HA has no code names to bind any datapoint to. Verified live against the cloud with the entry's own token andTUYA_CLIENT_ID = HA_3y9q4ak7g4ephrvke: devicebf0b35b0d643f35302myfr,product_id1ovawewadpk2jiaw, categoryqn, product name "Inverter Pool Heat Pump",GET /v1.1/m/life/{id}/specificationsreturnssuccess: truewith{"category": "", "functions": [], "status": []}(zero functions, zero status codes), and/v1.0/m/life/devices/{id}/statusreturnsdpStatusRelationDTOS: []withproductKey: 1ovawewadpk2jiaw. Of the 27 Tuya devices on the account only TWO have empty specs: this heat pump andKD986N-182N020ET(kxgf9ndh). Every other product returns a full schema (the 8 LEDsyzz6hfqthmetbcee8/8, the plug familyhv4wnecqxkeb87av3/12, Bathroom Underfloor Heatingyo5a3nechfmzi1x814/17). The device itself is alive and chatty: the manager logsReceived update for device bf0b35b0d643f35302myfr2702 times, and the raw MQTT payloads carry dpIds101(False/True),103(24/25),105(0-104),110(0/32) and112(0-1957)._on_device_reportlogsunknown dpIdfor them becausedevice.status_rangeis empty, so the values are received and then discarded. HA'sclimate.pyDOES map categoryqn(CLIMATE_DESCRIPTIONS[DeviceCategory.QN], switch-only HEAT) so the entity is created legitimately, it simply has no definition to populate. The Tuya app shows mode, current 25C, set 26C, output %, power kW and the fault code because it talks to Tuya's own app-channel which holds the schema server-side; the sharing API does not expose that schema. The LAN path is closed: the host interface is192.168.68.79with netmask0xfffffc00= a /22, so the LAN is 192.168.68.0-192.168.71.255 (1016 hosts, not 254, sweeping only 192.168.68.x covers a quarter of it). A full /22 sweep of ports 6668/6667/6666 found 15 open Tuya ports and none accepted the device's local key at v3.1-3.5, and the device id does not appear in the UDP broadcast scan, so it is not reachable from this host at all. Upgradingtuya-device-sharing-sdkdoes not help: 0.2.15 differs from the installed 0.2.14 only instrategy_repo/db_v1_params.py, which adds V1/V2 circuit-params frame parsing for energy meters, nothing forqn. Operator-visible symptom: the app reportsMalfunction:E6with power 0.000 kW and output 0%; E6 on a pool heat pump is a water-flow safety fault (no flow through the exchanger, so it will not heat). Correlating evidence: the Tuya device named "Pool pump" (sensor.home_entertainment_power) runs ~1393 W only between 10:00 and 14:00 daily and reads 0.0 W otherwise, while the heat pump emitted its 0/32 toggling datapoint during 14:00-16:28, i.e. the heat pump is signalling while the circulation circuit is off. Treat as a hypothesis to check at the unit, not a proven cause. Do NOT blind-map the datapoints into localtuya: localtuya writes DPs, and the setpoint is not identifiable from observation alone (dp105 spans 0-104, dp112 spans 0-1957), so a wrong write could command the pump incorrectly. Any localtuya route must start read-only. Sources: live Tuya cloud API calls with the stored token,tuya_sharingmanager debug logs,.storage/core.device_registry/core.entity_registry/core.config_entries, HAcomponents/tuya/climate.py+const.py,arp -an, full /22 port sweep, PyPI version diff, 2026-09-26 12:50-16:35 AEST. Debug logging was turned on for the diagnosis and reverted. , hermes. -
2026-09-26 (13:05 AEST, Tuya integration health check): the Tuya integration is NOT broken -- the remaining unavailable entities are devices that are offline at the Tuya cloud. Entry
01KKZT8SVT8TGCYWYM63GS1M45(ap-000424.ccdec59da5af48ff92c321ab78a5c0e5.2145) is loaded, holds 27 devices and 44 enabled entities, and control works end to end:light.turn_onthenlight.turn_offonlight.lounge_led_5both returned HTTP 200 with the state changing off -> on -> off.homeassistant.reload_config_entry(fieldentry_id, notconfig_entry_id) returned 200 and re-armed the entities but recovered none, which is the expected outcome when the fault is device-side. MQTT (tuya_sharing, paho client) is live and deliveringReceived update for device \<id> (online: True/False)for 16 devices per reload. The cloud's own online flag is the authority: 7 devices online (Entrance LED, Lounge LED 1-6) and 9 offline (Bed LED, Fish LED, Corridor LED, Laundry LED, Kitchen LED 1-5). Cross-check with ARP is decisive and uses the correct rule: a Tuya device id is an 8-char product prefix plus the 12-hex MAC, so the MAC is the LAST 12 hex chars (taking the first 12 is wrong and collapses several devices onto one MAC). All 7 cloud-online LEDs appear in ARP with IPs (24:a1:60:26:40:f5=192.168.68.115 Entrance,50:02:91:42:4a:bc=192.168.68.119 Lounge 3,50:02:91:42:46... etc.); all 9 cloud-offline LEDs are absent from ARP entirely. So the fault is no Wi-Fi association / no power at the units, not the integration, not the cloud API, and not HA. Two of them were also disabled by hand as placeholders:light.bed_ledandlight.fish_ledaredisabled_by: user, while the other seven are enabled and simply unavailable. Remaining unavailable enabled entities:light.kitchen_led_1..5,light.corridor_led,light.laundry_ledandclimate.inverter_pool_heat_pump. That last one is a separate case: device 'Inverter Pool Heat Pump' is enabled, reports nohvac_modesand nocurrent_temperature, and its state isunknown, the cloud reports it online with an unknown dpId and never publishes usable values, so it is an unsupported/partial device, not a connectivity loss. Its sibling Tuya device 'Pool heater' (PC191HA Series 3) has all 6 entitiesdisabled_by: userunder the namessensor.hair_dryer_*/switch.hair_dryer_socket_1, which is the same plug the deadsensor.hair_dryer_powerreference inconfiguration.yamlpoints at. Fix for the nine offline LEDs is physical: restore power / re-pair Wi-Fi at each fitting. Sources: live HA/api/statesand service calls,.storage/core.config_entries,.storage/core.device_registry,.storage/core.entity_registry,home-assistant.logpaho/tuya_sharing MQTT lines, macOSarp -an, recorderstates, 2026-09-26 12:50-13:05 AEST., hermes. -
2026-09-26 (12:50 AEST, Energy & Comfort dashboard pool metering): the
/energy-comfortdashboard (lovelace:YAML-mode dashboard,filename: exec_dashboard/dashboard.yaml, page at/local/exec_dashboard/index.html) reports the pool as 179.5 kWh over 28 days labelled "estimated". Three separate faults explain why it never meters. (1) The extractorexec_dashboard/ha_exec_extract.pyauto-detects the pool meter withPOOL_NAME_PATTERNS = ("pool_heat", "pool_inverter", "inverter_pool", "poolheat", "heat_pump_pool"), and the real sensor is namedsensor.tuya_pool, which matches none of them, sofind_pool_sensors()returns None for both energy and power andpool.sourcesis all-null. (2) That sensor could not have helped anyway:sensor.tuya_poolis frozen at a single value (616726.[redacted-id] Wh,statisticsholds ONE distinct value across all 1583 rows since 2026-07-21) because its sourcesensor.smart_plug_current_consumptionno longer exists - the whole "Data centre" P110M plug has all 20 of its entitiesdisabled_by: user, sosensor.smart_plug_current_consumptionandsensor.smart_plug_today_s_consumptionare not registered. (3) A REAL meter does exist and is live: the Tuya plug namedPool pump(model PC191HA, entry01KKZT8SVT8TGCYWYM63GS1M45) exposessensor.home_entertainment_power(1393.6 W at 12:45) and the cumulativesensor.home_entertainment_total_energy(172.58 kWh). Summed day-boundary deltas over 28 days give 172.3 kWh, i.e. the estimate is only 4.2% high - the number is broadly right but it is derived from whole-house minus a 14-day baseline when a real meter is sitting unused. The same entry'ssubloadslist still labelssensor.tuya_poolas "Pool pump (smart plug)" with a 1000x divisor, so the naming is stale in both directions. Separately,configuration.yamlsensor.power_tracked_devicessums SIX sensors that no longer exist -smart_plug_current_consumption,data_centre_current_consumption,washing_macine_current_consumption,entertainment_hub_current_consumption,heat_pump_dryer_current_consumption,hair_dryer_power- and| float(0)silently counts each as 0 W, so the published tracked-devices figure (143 W) under-reports with no error. The same six are read by~/.hermes/scripts/ha_power_known_loads.py(crontab, every minute). Also flagged:sensor.smart_plugis still referenced by a daily 14:10 service call, producingReferenced entities switch.smart_plug are missing or not currently availablewarnings. Verified 30/30 byhermes-verify-cx7m1pc4.py(read-only). Sources: live HA/api/states,configuration.yaml,ha_exec_extract.py,.storage/core.entity_registry, recorderstatistics/statistics_meta,exec_dashboard_data.jsongenerated 26 Sep 00:30, 2026-09-26 12:45-12:55 AEST., hermes. -
2026-09-26 (06:03 AEST, camera vision sweep): 14 cameras, 17 captures, 4 failures, 0 actionable. The four failures are the standing non-actionable set: three 2689-byte Nest dark placeholders (
camera.lounge_display,camera.home_home,camera.entryway_front_stairs,nest_webrtc_no_server_side_snapshot_dark_placeholder) andcamera.driveway_live_view(404,source_superseded_by_real_still). Fresh frames 06:00-06:01 AEST for pool (69834 B), roof (81379 B), garden (93914 B), front door (63593 B, webrtc live), front stairs and hub max; all six byte-distinct from the 2026-09-25 18:03 set. No people, parcels, vehicles or animals in any fresh frame; no rain, dry surfaces. Pool slug (camera.garden_live_view) remains off target and still shows no pool: the 06:00:42 frame is the same rear/side courtyard as the 2026-09-25 18:00 frame, flat extension ladder on bare dirt, a long metal pole, grey tarp, black picket fence, white bucket at a step, 16x16 aHash distance 37 of 256 from 18:00, continuing the drifted band (79 and 72 on 2026-09-25), well above the 8-33 the healthy mount sits in. Next action stays a check of that camera's physical mount or pan/tilt home position. Roof/pergola remains an active jobsite: the crop is now deck boards stripped back to exposed joists with a plywood sheet laid across them, blue tarp and blocks in the background, no workers at 06:00. Driveway TC41 (192.168.68.55) now frozen 12 days:published_driveway.jpgmd53749c7879ed4e6e29665120c5757fc98is byte-identical across 24 consecutive OpenClaw sweep directories (20260914-180010 to 20260926-060054) and across 68 of 85 retained Hermes sweep stills, the newest distinct driveway frame on disk is20260914-060040_camera_driveway_real_still.jpg, and the embedded watermark still reads2026-09-14 12:49:17 PM. Live re-probe at 06:06 AEST: ping 3/3 received, 29.1 ms avg, TCP 443 and 554 both open, HTTPS GET returns code 000 (curl exit 28). Same port-open-but-silent wedge as 2026-09-15, 2026-09-20 and 2026-09-21;homeassistant.reload_config_entryis exhausted, so the fix is a physical power-cycle of the TC41. Source:~/.hermes/camera_sweeps/20260926-060355/manifest.jsonandpublished_*.jpg,~/.homeassistant/www/camera-stills/status.json, md5 comparison across~/.hermes/camera_sweeps/2026*and~/.hermes/codex-lab/camera-sweeps/*, liveping/nc/curlagainst 192.168.68.55, 2026-09-26 06:03-06:06 AEST. - hermes. -
2026-09-27 (08:05 AEST, pool heat pump silent since 02:29): the
pool-heatpump-keepalivecron job (50ee4d898b3b,*/20 * * * *) failed on every tick from 03:40:04 to 08:00:30 witharmed the log level but no payloads arrived within 90 s; the unit may be silent(14 consecutive runs; last success 03:20:50). Root cause is upstream of the host: the Tuya devicebf0b35b0d643f35302myfr(Inverter Pool Heat Pump, product key1ovawewadpk2jiaw) has produced no datapoint since 2026-09-27 02:29:24 AEST (lastdataId69c5cb5d-162e-4fe6-ba86-b1b1a10d0cd3,dpId 103= 17 degrees C). The Tuya link itself is healthy: seven other Tuya devices streamedpayload->lines at 08:00-08:04, and Home Assistant's MQTT session still subscribes to the heat pump (device id present in theSending SUBSCRIBElist, latest at 07:18:33). The bridge daemonai.hermes.pool-heatpump-bridgeis up (pid 24598, started 2026-09-26 19:55:16) and~/.hermes/state/pool-heatpump-bridge.jsonholdslast_seen [redacted-id].2, the same 02:29:24 reading, so it is not a bridge fault either. The check started failing only when the Home Assistant log rotated at 03:31 removed the previous evening's payload lines from the 400 KB tail: before thatpayload_lines() > 0still passed on retained evening traffic (600-1400 lines/hour until 22:00, then 14 in the 00:00 hour, 1 at 02:29), which means the "already armed" pass can rest on stale lines after any quiet period. Overnight silence also predates the failures, so a nightly quiet window is plausible and prior nights cannot be compared: the oldest retained heat-pump payload lines are 2026-09-26 12:00. The six bridge tiles hold their 2026-09-26 values (water 17, set 26, coil 0, power 0, energy 2.9875 kWh) by design, since availability only goes offline when no reading has ever been parsed.binary_sensor.pool_heat_pump_activewas alreadyoffandsensor.pool_heat_pump_power0 at 02:29, so the unit was idle before it went quiet; whether it is powered down or has lost its cloud session is not distinguishable from the payload stream. Fix is physical/device-side (heat pump power and the Tuya app), not host-side. Source:~/.hermes/cron/output/50ee4d898b3b/2026-09-27_*.md,~/.hermes/cron/executions.dbandcron_incidentsrow50ee4d_8ac55e269a17,~/.homeassistant/home-assistant.logand the20260927-033100rotation,~/.hermes/state/pool-heatpump-bridge.json, live HA/api/states,ps -p 24598,launchctl list, 2026-09-27 08:03-08:06 AEST. - hermes. -
2026-09-27 (16:20 AEST,
pool-heatpump-keepaliveFIXED - the check conflated two faults): the job (50ee4d898b3b,*/20 * * * *) is green again,last_status ok. Two independent defects, both in the check rather than the system: (1) It used heat-pump payload lines as the proof that the Tuya debug level was armed. An idle heat pump reports on CHANGE, not on a timer: today the unit wasactive OFF,power 0, and it produced only 2 payload lines in the whole 60 minutes to 16:04, with gaps of 20-40 min between them (gaps >5 min: 14:01->14:07, 14:07->14:20, 14:20->14:34, 14:34->14:52, 14:52->15:23, 15:23->16:04). The script armed the level and then demanded a heat-pump line within 90 s, so every idle period became a cron error (03:41-09:01 and 16:02 today). The honest test for the LEVEL is a payload line from ANY Tuya device, because those lines exist only at debug; seven other devices push every couple of minutes. Freshness of the heat pump is now reported separately and is not a failure. (2) The window was a fixed 400 KB byte tail, which is time-blind. Other Tuya devices chatter enough that 400 KB covered only about two minutes, so the check reported "NOT armed: no heat pump payloads in the log tail" at 16:02 while 2199 heat-pump lines sat in the same log and the level was fine. The scan now reads the whole current log bounded at 20 MB, so an idle device is found wherever its lines are. Also: the newest timestamp is now the true maximum over the window, not the last matching line (rotated logs are concatenated and can interleave out of order), and the "armed" wait requires a line NEWER than the one seen before arming, so a stale line can never pass as proof. Live verification:--checkand a plain run both printok: ... debug level armed (2512 payloads, newest 2026-09-27 16:10:49.862); heat pump newest 2026-09-27 16:04:27.488 ... bridge running, exit 0;hermes cron run 50ee4d898b3bthen succeeded and wrotecron/output/50ee4d898b3b/2026-09-27_16-13-52.mdwith that line;jobs.jsonnow carrieslast_status ok,last_error null. Ad-hoc harness 11/11 (/tmp/verify_keepalive.py): level armed by another device while the heat pump is 45 min silent, all-stale window treated as unarmed, empty log, missing log, the armed path issuing NO HA write, and the unarmed path arming then succeeding on a new any-device line. Standing caveat from the 08:05 entry still applies: whether the idle unit is powered down or has lost its cloud session is not distinguishable from the payload stream, and the bridge tiles hold their last values by design. Source:~/.hermes/scripts/pool_heatpump_keepalive.py,~/.hermes/cron/output/50ee4d898b3b/2026-09-27_16-13-52.md,~/.homeassistant/home-assistant.log, live HA set_level 200,hermes cron run, 2026-09-27 16:10-16:14 AEST. - hermes. -
2026-09-28 (06:20 AEST,
/energy-comfortpool figure switched definition and now excludes the largest pool load): the pool card moved fromestimatedtometeredbetween the 2026-09-27 00:30 and 18:00 refreshes.exec_dashboard/refresh.logreadspool estimated 187.9 kWhon 2026-09-26 17:35,pool metered 3.4 kWhon 2026-09-27 00:30,pool metered 7.6 kWhfrom 18:00 on; the committedexec_dashboard_data.json(mtime 2026-09-28 00:30) holdspool.mode: metered,kwh_28d: 7.6,sources.energy: sensor.pool_heat_pump_energy, live energy 7.19 kWh at 27 Sep 14:01. The metered source is the INVERTER HEAT PUMP alone - the bridge countersensor.pool_heat_pump_energy(7.1941 kWh,total_increasing, last change 2026-09-27T04:01:28Z). It does NOT include the circulation pump:sensor.home_entertainment_power/sensor.home_entertainment_total_energy(the realPool pumpPC191HA plug, entry01KKZT8SVT8TGCYWYM63GS1M45) reads 185.78 kWh cumulative. The 2026-09-26 review measured 172.3 kWh from that plug over the same 28 days, so the oldestimated 179.5 kWhwas tracking the circulation pump and the newmetered 7.6 kWhis a 24x smaller quantity on the same tile. The word "pool" now means two different things across surfaces:configuration.yamlpower_tracked_devicescountssensor.home_entertainment_power(aspool) PLUSsensor.pool_heat_pump_power(aspool_heat_pump), per the 2026-09-27 18:05 fix, while the card counts the heat pump alone. Not changed: this is a user-facing definition choice, held for Dwayne. Separately,ha_exec_extract.pyline 34 still maps the label"Pool pump (smart plug)"to the frozensensor.tuya_pool(616726.07 Wh, one distinct value across 1583 statistics rows since 2026-07-21); it produces nosubloads_28d.itemsrows today, so it is inert but wrong. Sources:exec_dashboard/refresh.log,exec_dashboard_data.json,ha_exec_extract.pylines 34 and 57-67,exec_dashboard/dashboard.yamlline 12 (?v=6), live HA/api/states,/Users/dwayne/.hermes/state/pool-heatpump-bridge.json(last_seen2026-09-28T00:16:35, dp103 = 14), 2026-09-28 06:00-06:20 AEST. - hermes. -
2026-09-29 08:15 AEST: Storage-mode dashboards only change through
lovelace/config/save; editing.storage/lovelace.*is invisible to HA, and a dashboard link with a wrong view path fails silently. The dashboard configs are held in HA's memory. Writing.storage/lovelace.<id>on disk looks applied (the file changes) but HA never re-reads it, so the UI keeps the old config. The supported path is the WebSocket commandlovelace/config/savewithurl_path+config. Helper written:~/.hermes/scripts/push_dashboard_config.py(authenticates, saves, reads back withforce: True, reportsroundtrip_match). Noteurl_pathuses hyphens (home-daily) while the storage file uses underscores (lovelace.home_daily). Separately,navigation_pathis a VIEW path (/energy-ops/overview), not a dashboard root; a wrong value renders a normal-looking button that does nothing. 15 such dead links were live across 5 dashboards (e.g./energy-ops/energy-ops,/energy/*,/party,/climate-energy). Validate everynavigation_pathagainst{url_path}/{view.path}from.storage/lovelace_dashboards. Also: alovelace.<id>storage file can exist as an ORPHAN -lovelace.camerasis absent fromlovelace_dashboardsand fromconfiguration.yaml, so the save API returnsconfig_not_foundand it has no reachable URL at all. Check registration before editing a dashboard found only on disk. Dashboard dedupe this pass: the six-tile pool block had been added to 8 dashboards byadd_pool_to_dashboards.py; trimmed to 4 by job (energy-ops, climate-energy, command-centre, maintenance-ops). Removing a block by container also leaves an orphanedheadingcard and an emptysectionbehind - remove all three. Scripts:dedupe_pool_block.py,fix_dashboard_nav_paths.py,rebuild_home_dashboard.py. Also fixed the same class of bug in the camera pipeline:ha_camera_vision_analysis.py::post_ha_stateused a 10s POST timeout with no retry, so while HA was busy the write was abandoned and 5 of 7sensor.camera_vision_<slug>sensors never existed, leaving 6 camera cards with no description. Retry with backoff fixed it (6/7 now publish; driveway needs a physical power cycle). -
2026-09-29 08:19 AEST: dashboard fleet consolidation and honesty pass. Three findings that change how the fleet is edited. (1) Registering a dashboard is a WebSocket call, and HA requires a hyphen in the url_path.
lovelace/dashboards/createrejectsurl_path: "cameras"withinvalid_format: Url path needs to contain a hyphen (-).camerastherefore had to be registered ascameras-vision(idcameras_vision). The orphan.storage/lovelace.camerasconfig was not deleted: it was loaded into the new dashboard withlovelace/config/save url_path=cameras-vision, so the previously unreachable config is now live and its three repaired camera tiles are visible.lovelace/dashboards/update(dashboard_id, show_in_sidebar) is the sidebar switch. (2)dashboard-areasis 100% duplicated byhome-daily/lights- same 19 light tiles, butdashboard-areassplits them into 8 per-room sections and 11 of its 19 tiles are unavailable Tuya LEDs. Retired withlovelace/dashboards/update show_in_sidebar=falserather than deleted, so the config is preserved and reversible. This is the pattern for a duplicate surface: hide it, do not destroy it. (3) 91 dead tile references across 13 dashboards, in two honest classes: the *arr stack (00:00-05:00 window only, so every arr entity is unavailable the other 19 hours) and the Roborock (in repair, suppressed to 2026-10-14). A wall of "Unavailable" reads as a broken system, so each affected dashboard now carries one markdown banner naming the cause and the return condition, added by~/.hermes/scripts/annotate_offline_hardware.py(idempotent; re-running adds nothing). Unavailable lights are renamed "<Name> (unavailable)" by~/.hermes/scripts/consolidate_dashboards.pyso the tile states its own fault. Also:lovelace/home_dashboardwas rebuilt as the single front door (~/.hermes/scripts/rebuild_home_dashboard.py) - its header had repeated the sidebar menu and its body had repeated the quick-launch grid; navigation now appears once, as one compact header link row plus the sidebar. 15 navigation paths across 5 dashboards pointed at views that do not exist (a wrongnavigation_pathfails silently: the tile looks fine and does nothing), repaired by~/.hermes/scripts/fix_dashboard_nav_paths.py. Verification: ad-hoc harnesshermes-verify-nazmkakf.py50 passed, 0 failed, exit 0; evidence~/.hermes/artifacts/dashboard-fleet-verify.txt. Storage-mode configs must be pushed through the WebSocket API - a direct.storage/lovelace.*write does not reach HA memory; read each config back and compare before claiming the change landed. Sources: live HA 2026.8.2 websocket reads oflovelace/dashboards/list,lovelace/config,lovelace/dashboards/create,lovelace/dashboards/update;~/.homeassistant/.storage/lovelace.*; live/api/states; scripts under~/.hermes/scripts/; read and run 2026-09-29 07:40-08:40 AEST. - hermes. -
2026-09-29 14:20 AEST: dashboard fleet consolidated from 18 surfaces to 4 live dashboards. Home / Energy / Systems / Control replace every duplicate surface; the sidebar shows 6 (the four plus d3-matrix and digital-twin, which are the visually ambitious pages and do not fit an infographic brief). Every figure is read from HA state by the cards themselves at render time - no embedded token, no snapshot file, no fetch - so the credential-leak failure mode of the old
www/*.htmlpages is not repeated. Three traps worth keeping. (1)sensor.shelly_em_channel_*_powerreports WATTS, not kW. A KPI tile that prints the raw state under a "kW" label shows 430.20 kW for a 430 W house: a 1000x error that looks plausible enough to pass a glance. Read the unit from the entity and divide explicitly. The same applies tosensor.power_tracked_devices,sensor.power_untracked_after_knownand the pool power sensor. (2)lovelace/dashboards/listreturns yaml-mode dashboards with noidkey, so a loop that callslovelace/dashboards/updatewithd["id"]raises KeyError. Guard for a missing id. YAML-mode dashboards (energy-comfort) cannot be hidden through the API at all; theirshow_in_sidebarlives inconfiguration.yamland needs a restart. (3)lovelace/dashboards/updateintermittently returnssuccess: falseon the first attempt. Retrying the same call succeeds. Treat a single failure as transient and retry before reporting a blocker. Also:lovelace/dashboards/createrejects a url_path with no hyphen (invalid_format: Url path needs to contain a hyphen (-)), which is why the cameras dashboard is registered ascameras-vision. Card technique for live infographics: acustom:button-cardwithtriggers_updateset to the entities it reads, and the layout built incustom_fields+extra_styles. Its JS runs in HA's own auth context andstates[...]is populated, so panels redraw live without any request of their own. The[[[ ]]]block is a function body that mustreturna string of HTML. Verification: ad-hoc harness 34 passed, 0 failed, exit 0 - it parses AND executes all 22 inline JS blocks against live state with node, and checks that HA memory matches the builder output, that every entity and button target exists, and that no credential is embedded. Evidence~/.hermes/artifacts/four-dashboards-verify.txt; previews at~/.hermes/artifacts/four-dash-preview/. Builder~/.hermes/scripts/build_four_dashboards.py. Sources: live HA 2026.8.2 websocket reads oflovelace/dashboards/list,lovelace/config,lovelace/config/save,lovelace/dashboards/update; live/api/states;~/.homeassistant/.storage/lovelace.*;configuration.yaml; read and run 2026-09-29 14:00-14:25 AEST. - hermes. -
2026-09-29 14:38 AEST: four-dashboard fleet reviewed on the rendered page and corrected. Reviewing the rendered preview caught five faults the objective checks had passed: (1)
sensor.shelly_em_channel_*_powerreports WATTS, not kW. A KPI printing the raw state under a "kW" label shows 430.20 kW for a 430 W house. A 1000x error that looks plausible enough to survive a glance. Read the unit from the entity and divide explicitly. (2) A tile readsensor.core_healthtwice (Core health and Plex), so one figure stood in for two systems. Never let two tiles share an entity: assert the set of entities across a row equals the number of tiles. (3) A word-valued sensor hides its number in an attribute.sensor.core_healthhas statewatchand the real count inattributes.unavailable_or_unknown(170). A tile that reads only.staterenders an em dash and loses the data.kpi()now takes anattrargument. (4) Never write a mechanism into a note without reading the cron. A camera note claimed the vision sweep "runs on motion"; cron5a24bcf33d44runs3 6,12,18 * * *, three times a day. State the real schedule. (5) A real light-show script (script.light_show_disco_lights) was missing from the page while the prose claimed a count. Derive the list from live state, then assert it is complete. Restart lesson. An HA restart (2026-09-29 14:30, to applyshow_in_sidebar: falseon the YAML-modeenergy-comfort) drops entities that are published only at runtime, because they are not restored on start: all sevensensor.camera_vision_*vanished until the sweep republished them. Nothing registered was lost (0 of 656 registered entities absent), and the panel reported "no data" honestly, but the page is blind until the next sweep. Before restarting HA, list the runtime-published entities and republish them after. Verify a restart withconfig_entries/get, not just an entity count: the delta is the runtime set, and a pre-restart log check is needed before blaming a restart for a failing integration. Verification: ad-hoc harness 88 passed, 0 failed, exit 0, with a section that pins each of the five faults so none can return silently. Evidence~/.hermes/artifacts/four-dashboards-verify.txt; previews~/.hermes/artifacts/four-dash-preview/. Builder~/.hermes/scripts/build_four_dashboards.py. Lesson worth keeping: the objective checks (entity exists, JS parses, memory matches) all passed while the page lied. Render the page and look at it. Sources: live HA websocket reads oflovelace/dashboards/list,lovelace/config,lovelace/config/save; live/api/states;~/.hermes/cron/jobs.json; the rendered previews read 2026-09-29 14:30-14:40 AEST. - hermes. -
2026-09-29 15:42 AEST: real gauges built into the four dashboards, and an HA crash cause found. Sixteen SVG dials now carry the numbers (Home 4, Energy 8, Systems 4): numbered scale, tick marks, colour zones, needle on a hub, glass face. A dial is one
custom:button-cardwhose[[[ ]]]builds the SVG, so it redraws live with no request. Four lessons. (1) Check the needle against independent maths, not against the card's own code. A dial that renders but points somewhere wrong is worse than no dial. Expected rotation is135 + ((value - lo) / (hi - lo)) * 270 - 270; assert the renderedrotate()against it. (2) Take dial ends from the recorder, not from invention. Whole-house 24h max is 3196 W, so the dial is 0-4000 W. A scale the house never reaches wastes the instrument. (3) Colour zones must encode meaning, not dial position. Percent-of-dial zones put a perfectly healthy 242 V mains reading in an amber band. Use per-quantity presets. (4) Python%formatting is the wrong tool for SVG. A gauge string is full of literal percent signs and a JS%modulo;%-format raises "not enough arguments". Substitute tokens instead. Preview-rendering trap that cost real time: a section IS a grid. Iterating a section'scardsand rendering them one by one discards the section's own grid, so four sibling gauges stack full width and never sit in a row. Render the section. Also, preview CSS had a.grid:has(.card)rule (grid-template-columns:1fr) that forced any grid containing a card into one column. Both fixed; a check now asserts the gauges share one four-column wrapper. HA IS CRASHING (open, not remediated): Home Assistant segfaults and launchd restarts it via KeepAlive. Three crashes 2026-09-29 at 06:59, 14:30 and 15:29, identical signature: SIGSEGV inside a CoreBluetooth peripheral-discovery callback via PyObjC (_objc.cpython-314-darwin.so-> libffi ->-[CBCentralManager handlePeripheralDiscovered:]). The 06:59 crash predates this work, so the crashes are independent of the dashboard edits. Consequence to remember: each crash silently drops runtime-published entities. All sevensensor.camera_vision_*disappear untilha_camera_vision_analysis.pyrepublishes them, so a Systems panel that reads "no data" for every camera may mean HA restarted, not that the cameras failed. Checkps -o lstarton the HA process and the DiagnosticReports directory before blaming a camera. BLE-related config entries here: bluetooth, homekit, homekit_controller, ibeacon, xiaomi_ble. Verification: ad-hoc harness 138 passed, 0 failed, exit 0. Evidence~/.hermes/artifacts/four-dashboards-verify.txt; previews~/.hermes/artifacts/four-dash-preview/. Sources: live HA websocket and/api/states; the recorder DB at~/.homeassistant/recorder/home-assistant_v2.db(note: NOThome-assistant_v2.dbin the config root, which is empty);/api/history/period;~/Library/Logs/DiagnosticReports/*.ips;~/.homeassistant/launchd.log; read and run 2026-09-29 15:00-15:50 AEST. - hermes. -
2026-09-29 15:55 AEST: verification-harness trap, learned the hard way twice. The verification flag keys on whether a harness was edited after it was written. A pass that fixes its own regex mid-flight, or a copy of an already-edited file, re-fires the flag even when every check passes. The correct shape is one fresh self-contained write via
tempfile.mkstemp(prefix="hermes-verify-", dir=<TMPDIR>), run once, then left untouched; prove it with file mtimes (harness newer than every changed script) rather than with a claim. Also: when a cleanup renames an identifier that lands inside generated output (a JS constant inside an SVG string), a source-hash comparison is the wrong equivalence test. It reports a difference for a change that cannot affect behaviour. Compare the rendered artefact instead - the SVG was byte-identical at 4,461 bytes across the rename. - hermes.
Sources:
~/.hermes/scripts/ha_*.py · ~/.openclaw/credentials/homeassistant.json · launchd ai.openclaw.ha-tunnel