Skip to content

Releases: GabrielGoldsteinAnidea/HA-Websocket-Stripper

0.2.3 — auto-entities regex/template/group resolution, XFF chain, self-healing kiosks

Choose a tag to compare

@GabrielGoldsteinAnidea GabrielGoldsteinAnidea released this 25 Aug 00:05
Immutable release. Only release title and notes can be modified.

Four reported issues, all fixed. Thanks to @lieblinger (#10), @Ltek (#4), @dimatx / @genik70 (#9) and @davidcoulson (#7) — every fix here came from a specific report, and several were only findable because people attached real YAML and real logs.

Local add-on reminder: code is baked into the image at build time, so this needs a Rebuild, not a Restart.

auto-entities: globs and regexes now work on every filter key (#10)

Only * globs were ever understood. A /regex/ was escaped as literal text, so

filter:
  include:
    - entity_id: "/^sensor\.pv_.*_power$/"

compiled to ^/\^sensor\\.pv_.*_power\$/$ — a pattern matching no entity id that has ever existed. Those entities were stripped and the cards showed "unavailable".

The matcher is now a port of auto-entities' own (src/match.ts), with its exact semantics:

Pattern Behaviour
/^sensor\.pv_.*_power$/ regex, not auto-anchored — you supply ^ / $
sensor.pv_* glob, anchored
sensor.foo exact equality

Critically it now applies to every filter key — domain, area, label, device, integration, name — not just entity_id, which is what upstream does. If you mirrored patterns into always_forward as a workaround, you can drop them.

Cards whose entities the proxy could not see (#4)

Three separate causes, all of which presented identically as "this card works on :8123 but not through the proxy":

  • HA's selector object form. The visual editor stores values as { custom: "input_boolean.bypass_*", active_choice: "custom" }. That was handled for area/label/device/integration but not for entity_id/domain, where it stringified to "[object Object]" and matched nothing.
  • filter: template: cards. Their entity list only exists after HA renders the Jinja, so a structural walk could never see it. The proxy now renders those templates over its existing control connection and takes the real entity ids from the output. A template that errors or times out contributes nothing and no longer affects the rest of the dashboard.
  • Group members. A card naming only a group — or expanding one client-side, like enhanced-shutter-card's show_group_members: true — left every member stripped, because the members appear nowhere in the dashboard config. Any allowlisted entity now contributes its entity_id attribute members, transitively. The auto-entities group: and name: filter keys are supported for the same reason.

400 Bad Request behind another reverse proxy (#9)

If you run Caddy / nginx / Traefik in front of this add-on, every request through it returned 400 while :8123 worked fine.

The handler that normalizes IPv4-mapped IPv6 used setHeader, replacing the entire forwarded chain with our immediate peer. Since http-proxy appends our hop to all three forwarded headers, HA received:

X-Forwarded-For:   <caddy>          (1 entry — flattened by us)
X-Forwarded-Proto: https,http       (2 entries)

and HA's forwarded middleware raises HTTPBadRequest on len(forwarded_proto) not in (1, len(forwarded_for)). Entries are now normalized in place, so the counts stay in step and the real client IP survives the upstream hop — which also means trusted_networks sees the browser rather than your proxy. The same normalization now applies to websocket upgrades, which never fired the HTTP-only hook.

Open dashboards pick up new entities by themselves (#7)

subscribe_entities is sent once per connection and HA cannot amend a live subscription, so a rebuild only ever affected new connections — an already-open kiosk kept its original entity list until someone physically reloaded the tab.

When a rebuild adds entities, affected connections are now dropped; the frontend treats that as an ordinary disconnect and reconnects against the current allowlist. Edit a dashboard and your wall panels catch up on their own. Removals deliberately don't churn open connections — carrying a few entities you no longer need is harmless.

Notes

  • Your allowlist may grow slightly after updating. That is the point: regex, name, and group-member entities that previously resolved to nothing are now included.
  • Still unsupported, and treated as matching nothing: not, and, or, floor, device_manufacturer, device_model, last_changed. List those entities in always_forward and open an issue if you rely on one.
  • Tests: 44 → 75.

Full changelog: v0.2.2...v0.2.3

0.2.2 — survive an HA restart; never forward an empty allowlist

Choose a tag to compare

@GabrielGoldsteinAnidea GabrielGoldsteinAnidea released this 24 Aug 00:46
Immutable release. Only release title and notes can be modified.

Update strongly recommended. This release fixes a bug that inverted the add-on's purpose on every fresh install.

Huge thanks to @maxi1134, who diagnosed both headline bugs on a live ~3,600-entity instance and sent the fix in #11.

An empty allowlist was being relayed as no filter

HA parses subscribe_entities as set(msg.get("entity_ids", [])) or None. An empty entity_ids is therefore not "subscribe to nothing" — the or None makes it no filter at all.

config.yaml shipped with dashboards defaulting to fridge-status / home-status / dashboard-deck. Those are my own dashboards. On anyone else's instance all three return config_not_found, the union came back empty, and the add-on then streamed every entity on the instance — the exact firehose it exists to prevent. On the reporting instance that hit Node's 2 GB heap limit every 30–130 seconds in a Supervisor restart loop. Because reinstalling an add-on resets options to their defaults, a fresh install landed straight in it.

  • dashboards now defaults to [].
  • /api/websocket is refused whenever the allowlist is empty, with the reason named in the log. A second guard in the relay drops the connection rather than ever sending an empty entity_ids.
  • A failed dashboard fetch now logs the dashboards that do exist, so config_not_found answers its own question.
  • No dashboards configured no longer exits — that just moved the restart loop into the Supervisor.

⚠️ If you never set the dashboards option

You were silently running with no trimming at all. After updating, the add-on will refuse /api/websocket and say so in the log until you set dashboards to your own url_path values (Settings → Dashboards). That is the fix working, not a regression.

Surviving a Home Assistant restart

Restarting HA killed the add-on and left it crash-looping until started by hand. Three causes:

  • Unhandled socket error on an in-flight ws upgrade. A raw upgrade socket arrives with no 'error' listener, and http-proxy only attaches one after HA answers 101. An HA restart resets every in-flight stream at once (camera, Assist), and a reset in that window reached Node as a fatal unhandled 'error'.
  • process.exit(2) when the first allowlist couldn't be built. The add-on and core restart together, so each restarted container died in ~300 ms until the Supervisor gave up. It now retries on the same backoff used for later drops.
  • The proxy now listens before HA is reachable, so it serves the moment core answers. Until the first allowlist exists, /api/websocket gets a 503 and the frontend retries on its own.

A restarting HA also returns in stages, which used to leave the allowlist wrong:

  • A rebuild after reconnect merges rather than replaces, so a half-loaded core can't shrink a good allowlist. Real dashboard/registry edits still replace, so removals still take effect.
  • buildAllow() bails when every dashboard fails (HA authenticating but not yet serving lovelace) so the caller retries. A single dashboard failing is still tolerated — that's a typo'd url_path.
  • The control connection has a handshake timeout, so a core that accepts TCP but never completes the handshake still triggers a reconnect.

Also

  • EADDRINUSE / EACCES on startup now exits naming the port option instead of printing a raw stack (#6).
  • auth_invalid no longer exits; it logs loudly and retries slowly.
  • Repeated failures are collapsed in the log instead of flooding hundreds of identical lines per second.
  • DOCS.md no longer claims the allowlist is computed only at startup — live recompute has been in since 0.2.0.
  • Test suite grown to 44, covering an HA restart, a cold boot against a down HA, a half-started core, and the empty-allowlist guard.

Local add-on reminder: code is baked into the image at build time, so this needs a Rebuild, not a Restart.

Full changelog: v0.2.1...v0.2.2