Homeassistant
Home Assistant integration for swamp — query entity states, call services, list and update automations, and fetch history and long-term sensor statistics over the REST and WebSocket APIs.
Model:
@magistr/homeassistant—list-entities,get-state,call-service,list-services,list-automations,get-automation-config,update-automation,get-history(JSON + CSV),get-statistics(long-term WebSocket aggregates), andbackfill-to-vm(bulk-import HA statistics into VictoriaMetrics).
Authenticates with a long-lived access token, supports http/https, and writes typed resources for every method so results are queryable as swamp data.
Global Arguments
| Argument | Type | Description |
|---|---|---|
| host | string | Home Assistant host (e.g., homeassistant.local) |
| token | string | Long-lived access token - use vault: ${{ vault.get(my-vault, HA_TOKEN) }} |
| protocol? | string | Protocol (http or https, default: https) |
| wsTimeoutMs | number | WebSocket statistics request timeout in ms (default: 60000) |
2026.08.02.1
Real-fixes the seven remaining MED/LOW findings (LB3–LB9) that the 2026.08.01.1
test backfill characterized but did not fix, in the local
homeassistant-latent-bugs issue-lifecycle model (never a Lab issue). Every
former "pinned-to-buggy" test is flipped to prove the corresponding fix; every
benign/frozen contract, methods, and property pin stays BYTE-IDENTICAL.
- LB3 — non-JSON WS frame (MED):
fetchStatistics'smessagehandler no longer silently swallows a malformed frame (catch { return; }, waiting out the full 60s timeout). It now fast-rejects with a static"WebSocket received a non-JSON frame"error (never echoes the server-controlledev.data, preserving the LB4 no-leak invariant). - LB4 — server-echoed token redaction (MED): added
redactToken/redactedCausehelpers (value-based, telegram-send/headphones precedent). Applied at every server-echo throw site: WSauth_invalid, WSerrorevent, WS!successresult,haFetch's non-ok response body AND a caught network-layerfetch()rejection (redacted.message+ a fresh single-level redactedcause— never the raw error, sinceDeno.inspect/ console formatting walks.causeincluding its.stack), and the VM import non-ok error. Redaction is a strict no-op on any message that doesn't contain the token, so every benign error-path pin (auth_invalid with no token echo, native WS errors, theerror.jsoncontract body, all REST 404/500 pins) stays byte-identical. - LB5 — raw REST path interpolation (MED): wrapped every interpolated path
segment in
encodeURIComponent—get-state,call-service,get-automation-config,update-automation, and the per-entity/stateslookup insidebackfill-to-vm. Identity for benign ids (letters, digits, dots, underscores are unreserved), so every frozen URL contract pin stays byte-identical; only a literal/(or other reserved char) is now escaped into one opaque%2Fsegment instead of injecting an extra path segment. - LB6 — token not marked sensitive (LOW):
InputSchema.tokennow carries.meta({ sensitive: true })(matches telegram-send'sbotToken). - LB7 — asymmetric backfill fan-out (LOW):
backfill-to-vm's per-entityfetchStatisticscall is now guarded exactly like the existing/statesguard (headphones onboard-artists / seadexsummary.errorsprecedent): a failure records a redactederroron that entity'sbackfill-reportsummary, logs a redacted warning, skips its samples, and the fan-out CONTINUES to the next entity instead of tearing down the whole method. Added an additive, optionalentities[].errorfield to thebackfill-reportresource schema. - LB8 — statistics missing-key vs. empty-range (LOW): replaced
(msg.result && msg.result[statisticId]) || []with an explicit four-way branch: (1) the requestedstatisticIdkey is present → resolve it verbatim (including an explicit[]); (2)resultis an object with ZERO keys → resolve[]— Home Assistant omits the key entirely for a legitimately empty range, so this is NOT an error; (3)resulthas OTHER keys but not ours → reject"Statistics response omitted requested statistic '<id>'"(anomalous — we only ever request one id); (4)resultis falsy/not an object → reject"Statistics response missing result payload"(malformed). A new test pins the empty-object-is-legit-empty case explicitly so a future change can't accidentally make it throw. - LB9 — hardcoded WS timeout + swallowed close errors (LOW): added a
defaulted
wsTimeoutMsglobal argument (z.number().int().positive() .default(60000)) threaded intofetchStatistics; bothget-statisticsandbackfill-to-vmreadcontext.globalArgs.wsTimeoutMs ?? 60000. The default renders the byte-identical"WebSocket timeout after 60s"message. Everytry { ws.close() } catch {/*ignore*/}call site is replaced by acloseQuietlyhelper that still NEVER re-throws or changes the rejection reason, but now surfaces the (redacted) close failure viacontext.logger.warning. - Pin flips: adversarial
:415(malformed frame, drop FakeTime),:475/:584(WS/REST token echo → asserts redacted),:499/:518/:533(raw path injection → asserts%2F-escaped); coverage:251/:269(statistics wrong-key/falsy-result → asserts the new rejection messages) and:767(token meta → assertssensitive === true); methods:724(backfill fan-out → asserts the method now RESOLVES with a per-entityerror). New
Notes trimmed at a line boundary to fit the registry's 4900-byte per-version cap. Full section: https://github.com/umag/swamp-workspace/blob/d850d8e8372d4c0008c9245959a090b37095de7a/homeassistant/CHANGELOG.md
Release 2026.07.16.2 — align model versions with manifests
Maintenance release across the @magistr extensions. For most packages this
carries no functional change: the only edit is the model's version: field,
brought back in line with its manifest version so the published model type
version and the package version no longer drift.
Functional changes in this release are limited to:
anime-cron: normalizeTitle now strips a ": subtitle" suffix and a trailing parenthesized year before comparison, fixing dedup false-misses where the torrent title carries a subtitle or year that the AniList romaji does not.
arckit: first publish. Standalone ArcKit port — a 12-phase architecture governance state machine with 65 bundled templates, driven by a bundled skill.
Also tracks three extensions (kaiten, observability-agent, music-library) that previously existed only as untracked working-tree directories, recovered from stashes.
Maintenance version bump. No functional changes.
Added 1 models
Merge pull request #5 from umag/extensions/jscad-stl-pair
extensions: add 15 more @magistr extensions + auto-discover CI
- Has README or module doc2/2earned
- README has a code example1/1earned
- README is substantive1/1earned
- Most symbols documented1/1earned
- No slow types (deprecated)1/1earned
- Dependencies pass trust audit2/2earned
- Has description1/1earned
- Platform support declared (or universal)2/2earned
- License declared1/1earned
- Verified public repository2/2earned