Commit Graph
100 Commits
Author SHA1 Message Date
Dermot Duffy 1135dd8ec0 fix: go2rtc live provider should work with non-proxied absolute URLs (#2724)
- Closes: #2719
2026-08-29 14:08:48 -07:00
Dermot Duffy 4b101545cc fix: Prevent premature reinitialization when stale HA reconnects (#2717)
- Closes: #2714
2026-08-29 13:47:38 -07:00
Dermot Duffy fb53681978 fix: Cache 2-way audio detection to avoid re-probing (#2716)
- Closes: #2299
2026-08-28 21:23:49 -07:00
Dermot Duffy 8ee69be58b fix: Download recordings with a video file extension (#2712)
- Closes: #2711
2026-08-27 19:17:15 -07:00
Dermot Duffy 543e5d0fcf fix: Report a go2rtc backchannel offer error as a failure rather than a missing capability (#2710) 2026-08-23 21:54:20 -07:00
Dermot Duffy e6b93d53e9 fix: Reduce visual clutter on startup (#2709)
- Closes: #2584
2026-08-23 12:25:44 -07:00
Dermot Duffy 03d9df8282 chore: Standardize code, comments and docs on US-English spelling (#2707) 2026-08-23 08:57:41 -07:00
Dermot Duffy 965f2b4c9d fix: Keep the notification popup open while its text is selected (#2706) 2026-08-22 21:29:04 -07:00
Dermot Duffy b2f3243f3e fix: Size the gallery fallback icon frame to fill the thumbnail box (#2705)
- Closes: #2701
2026-08-22 15:02:28 -07:00
Dermot Duffy 60e01cf537 chore: Migrate from @lit-labs/task to @lit/task (#2704)
- Closes: #2703
2026-08-22 14:15:07 -07:00
Dermot Duffy 0aebf3c484 fix: Keep the notification popup visible / centered (#2702)
- Closes: #2693
2026-08-22 08:36:42 -07:00
Dermot Duffy 2f74889bcb fix: Cancel the pending debounced resize when the media dimensions container disconnects (#2700)
- Closes: #2699
2026-08-21 20:45:31 -07:00
Dermot Duffy 11cc543406 fix: Hold the 2-way audio backchannel open for the shortest possible time (#2697)
The card claimed a camera's ONVIF audio backchannel in two places that
had
nothing to do with a call: the capability probe at camera init
(`&microphone`),
and a pre-armed `sendonly` audio transceiver on every live WebRTC offer.
Merely
looking at a dashboard occupied the camera's speaker line. Outbound
audio now
travels on its own audio-only WebRTC connection, opened when a call is
answered
and closed when it ends.

- The backchannel is claimed only for the duration of a call. Idle
viewing
  claims nothing.
- Two-way audio now works in `mse`, `mp4` and `mjpeg` modes (note: the
outbound
  audio still traverses WebRTC).
- No renegotiation and no video blink at call start or end.
- A call that cannot carry audio now reports it and ends, instead of
showing a
  live microphone that goes nowhere.
- `live.microphone.always_connected` is now purely about the browser
microphone
  permission prompt.
- Call setup measured at 66ms (LAN) and ~260ms (cellular) for ICE and
DTLS, plus
  ~300ms for `go2rtc` to open an RTSP backchannel.

Verified against a live Frigate + `go2rtc` instance, and by unit tests
at 100%
coverage.

 - Closes #2691
 - Closes #2039
 - Closes #2178

Ref #2299 -- the probe no longer opens a backchannel, but it still runs
per
camera on every load and reconnect, and still dials the camera on the
direct-`go2rtc` path. Caching remains to be done.

Ref AlexxIT/go2rtc#1860 -- once a call has opened a backchannel,
`go2rtc` keeps
that media set up on the camera's RTSP session for the life of the
producer.

Diagnoses #2678
2026-08-21 20:13:58 -07:00
Dermot Duffy 2d7c86c93c refactor: Convert the build scripts to TypeScript and group them (#2696)
- Closes: #2695
2026-08-21 14:18:37 -07:00
Dermot Duffy 1e03e6a3d9 fix: Stop the automatic upgrade from discarding valid configuration (#2692)
- Closes #2689
2026-08-19 17:02:46 -07:00
Dermot Duffy 84b90bcc7e fix: Remove the unnecessary item limit from folder media (#2688)
- Fixed: #2573
2026-08-17 21:49:11 -07:00
Dermot Duffy 858c5d0610 fix: Move upgrade button to top of editor (#2687) 2026-08-14 21:13:10 -07:00
Dermot Duffy c2f25861ea fix: Remove the microphone selection conditions (#2686)
- Closes: #2679

BREAKING CHANGE: `selected` and `unselected` are removed from
`live.microphone.auto_unmute` / `auto_mute`. A camera change ends any
call and the microphone only carries audio during a call, so neither
were useful.
2026-08-14 18:16:39 -07:00
Dermot Duffy 85d6811761 fix: Release the microphone to the browser when a call ends (#2685)
The microphone is connected when a call needs it and released the moment
that
call ends, so the browser stops reporting it as in use at hangup rather
than
`disconnect_seconds` later.

Existing configurations are migrated automatically by the visual editor.

 - Closes: #2681 

BREAKING CHANGE: `live.microphone.disconnect_seconds` is removed. The
microphone is released when a call ends, so there is no idle countdown
to
configure. Use `live.microphone.always_connected` to hold it open
instead.

BREAKING CHANGE: The `microphone_connect` and `microphone_disconnect`
actions
are removed. The card owns the microphone lifecycle; `microphone_mute`
and
`microphone_unmute` remain.

BREAKING CHANGE: `call` is removed from `live.microphone.auto_mute`,
whose
default is now `[]`. The microphone is muted when a call ends regardless
of
this option.

BREAKING CHANGE: `microphone_unmute` has no effect outside a call.
Nothing
carries the audio at any other time, so the request is ignored rather
than
opening the microphone.
2026-08-14 16:17:31 -07:00
dermotduffy 65bc52d18e Merge remote-tracking branch 'origin/main' into dev 2026-08-10 21:32:41 -07:00
68b10622c5 chore: Merge branch 'main' into dev (#2676)
Co-authored-by: verjager <78507418+verjager@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-10 21:26:54 -07:00
Dermot Duffy 70ca0a9af4 fix: Stop a released PTZ key ending another held key's movement (#2675)
- Closes: #2668
2026-08-10 20:22:39 -07:00
Dermot Duffy 3808a1b030 fix: Stop PTZ movement and browser key handling from fighting (#2674)
- Closes: #2623
2026-08-10 14:49:50 -07:00
Dermot Duffy a8aa9ec7c2 fix: Should recover from an interrupted initialization (#2673) 2026-08-10 14:27:40 -07:00
Dermot Duffy 98a0084e7a fix: Stop the live media frame collapsing while a stream loads (#2671)
- Closes: #2574
2026-08-09 21:17:06 -07:00
Dermot Duffy 45f88a86a4 fix: Restore picture element actions on Home Assistant 2026.8+ (#2670)
- Closes: #2663
2026-08-09 20:58:15 -07:00
Dermot Duffy a8ce6acb44 chore: Enable noImplicitAny across the codebase (#2667) 2026-08-09 13:22:57 -07:00
Dermot Duffy 5e199b5612 fix: Report media failures once per failure (#2666) 2026-08-09 10:48:17 -07:00
Dermot Duffy 8d8d486afa test: Split browser test-utils (#2662)
- Closes: #2660
2026-08-07 16:27:28 -07:00
Dermot Duffy 673d8fe133 test: Add browser tests for the media viewing gallery and viewer (#2661) 2026-08-07 16:08:56 -07:00
Dermot Duffy 567690831c feat: Show the build date on the loading banner of unreleased builds (#2659) 2026-08-05 22:15:00 -07:00
Dermot Duffy c137f4c00c fix: Report and retry media failures for every camera in a grid (#2658)
- Closes: #2637
 - Related: #2099
2026-08-05 21:35:47 -07:00
Dermot Duffy 9f88aacefe fix: Ensure no camera outlives a failed initialization (#2657) 2026-08-04 21:33:46 -07:00
Dermot Duffy 80ec92e3b2 refactor: Replace Rollup with Vite (#2656)
This is a high risk change: every byte the card ships is emitted by a
different bundler, minified by a different minifier, and every
stylesheet is compiled by a different sass. The intent is that the
card's behaviour is unchanged.

**Significant development win**: Build time drops from 24s to 1.1s 🎉 

Also adds a test suite ('dist') that runs against the built bundle.
2026-08-04 15:00:13 -07:00
Dermot Duffy 06bcfd58ba refactor: Stamp build information in as "defines" (#2654) 2026-08-03 22:36:32 -07:00
Dermot Duffy 2d124fe3cd test: Add a test harness for the built card (#2653)
Mounts the card from `dist/` the way Home Assistant does, by URL with a
HACS tag, and asserts the entry stays a facade, that no chunk imports
it, that the browser is served the file unmodified, and that the card
starts up and fetches a language chunk lazily.

Runs in Chromium, Firefox and WebKit, so a change of bundler or minifier
is checked against every engine.
2026-08-03 21:50:21 -07:00
Dermot Duffy 18ea725564 test: Also run firefox & webkit automated tests (#2651) 2026-08-03 16:48:30 -07:00
Dermot Duffy 4a7c1b62b0 perf: Deduplicate concurrent Home Assistant registry list fetches (#2650) 2026-08-02 21:35:48 -07:00
Dermot Duffy 5d247d4074 test: Add browser based tests for keyboard focus handling (#2647) 2026-08-02 16:38:17 -07:00
Dermot Duffy 17146c8ca6 fix: Clear the initialized condition state when the card is not usable (#2646)
The `initialized` state (used in conditions/triggers) was written once
and never cleared, so it meant "has this card ever been initialized"
while everything reading it took it as "is this card usable now". Home
Assistant takes a card off the page and puts it back whenever its
dashboard tab is left and returned to, so the card initialized again
while the state claimed it was initialized throughout: `trigger:
initialized` fired once per card rather than once per startup, and
automations were dropped in between.

The card lifecycle is now an explicit state machine (`SessionManager`),
the only writer of `initialized`, which separates a card that is
starting up from one initializing part of itself again while it runs. A
new `ever` parameter (conditions/triggers) selects the old latched
behaviour.

`remote_control` uses that parameter to keep its two camera priorities
correct under repeated starts. With `camera_priority: entity` the card
now re-reads the entity every time it starts, so a camera selected while
the card was away is picked up on return. With `camera_priority: card`
the card writes the entity on its first start only, unchanged, since
repeating that write would overwrite a camera the user had selected.

Closes: #2642


BREAKING CHANGE: `condition: initialized` is now `false` whenever the
card is not usable, and `trigger: initialized` fires each time the card
starts up rather than only the first time. Set `ever: true` on either to
keep the previous behaviour.
2026-08-02 12:52:58 -07:00
Dermot Duffy e590f72783 test: Add a browser based test harness (#2641) 2026-07-31 10:16:58 -07:00
Dermot Duffy 51c0fab1ce fix: Do not activate pan & zoom on a detached element (#2640)
- Closes: #2633
2026-07-30 21:33:37 -07:00
Dermot Duffy f24064689a fix: Restore keyboard focus for key triggers (#2634) (#2639)
- Closes #2634
2026-07-30 20:52:54 -07:00
Dermot Duffy a736f3dfaf fix: Ensure a bare ptz_digital action resets the zoom (#2635)
- Closes: #2632
2026-07-29 23:18:06 -07:00
Dermot Duffy e9e6d9f833 feat: Use the card's own pan & zoom with the webrtc-card live provider (#2630)
- Closes: #2628
2026-07-29 15:46:03 -07:00
Dermot Duffy 70abb39762 fix: Stop the webrtc-card provider recreating its player on every render (#2629)
- Closes: #2625
2026-07-29 13:11:12 -07:00
Dermot Duffy 15b08db3e8 fix: Clear stale media unavailable errors when a camera recovers (#2627)
- Closes: #2576
2026-07-28 21:21:50 -07:00
Dermot Duffy 231e3087b7 fix: Error when a zoomable element is removed during zoom initialization (#2624)
- Closes: #2410
2026-07-26 20:54:07 -07:00
Dermot Duffy ad89aa9538 test: Split test utilities to improve test times (#2622) 2026-07-26 16:29:53 -07:00
Dermot Duffy 8d44fcecf1 fix: Ignore startup triggers from cameras without the trigger capability (#2621)
- Closes: #2103
2026-07-26 15:37:59 -07:00
Dermot Duffy 0f8593f9fa feat: Answer or reject inbound calls from menu (#2620)
- For: #2587
2026-07-26 14:24:14 -07:00
Dermot Duffy ababe133a8 chore: Switch coverage from istanbul to the v8 provider for speed (#2619) 2026-07-26 14:21:27 -07:00
Dermot Duffy 3b8835ee7c feat: Allow disabling the call controls (#2615)
- For: #2587
2026-07-26 11:35:18 -07:00
Dermot Duffy 0c3d46ad3d chore: Upgrade to Vitest 4 and regroup tests for performance (#2618) 2026-07-26 11:28:58 -07:00
Dermot Duffy a0fd464d23 test: Remove unnecessary jsdom environment declarations (#2616) 2026-07-25 22:47:59 -07:00
Dermot Duffy eccf7f3b97 ci: Upload the bundle treemap visualization browser-viewable (#2617) 2026-07-25 22:47:06 -07:00
Dermot Duffy 37b07f079d chore: Run checks, tests and build as parallel jobs (#2614) 2026-07-25 22:01:53 -07:00
Dermot Duffy c4c6058a70 test: Split the test suite into isolation groups for faster runs (#2613) 2026-07-25 21:47:13 -07:00
Dermot Duffy 8f953547b1 fix: Connect the microphone when an inbound call is *answered* (#2609) (#2612)
- Closes: #2609
2026-07-25 20:56:07 -07:00
Dermot Duffy 2a84f199de fix: Size the expanded dialog and grid columns to their content (#2611)
- Closes #2607
2026-07-25 13:46:31 -07:00
Dermot Duffy 5f2c7f5e4e fix: Prevent the media grid from collapsing when its cells are unchanged (#2608)
- For: #2306
2026-07-24 18:06:35 -07:00
Dermot Duffy 0a763b73a6 docs: Add config example for problematic doorbells (#2604) 2026-07-24 10:55:45 -07:00
Dermot Duffy f419798e8e fix: Exponentially back off initialization-error retries (#2579) (#2605)
- Closes #2579
2026-07-24 10:54:31 -07:00
Dermot Duffy 54f75beb9d fix: Improve media_query retry behavior (#2603) 2026-07-23 22:19:17 -07:00
Dermot Duffy 6817d27c91 fix: Guard the ha provider MJPEG stream URL against a missing access_token (#2602)
- Closes: #2601
2026-07-23 09:42:05 -07:00
Dermot Duffy cd53ebd3c8 fix: Correctly bind MJPEG stream URL in the ha live provider (#2305) (#2600)
- Closes: #2305
2026-07-22 22:00:47 -07:00
Dermot Duffy 448fa2d9f1 feat: Surface the provider's error cause in the media_unavailable notification (#2599)
- Closes: #2592
2026-07-22 20:45:49 -07:00
Dermot Duffy 5a549c0326 fix: Prevent microphone auto-disconnect during calls and restore the removed microphone connected condition (#2597)
- Closes: #2590
2026-07-22 16:12:31 -07:00
Dermot Duffy 8c36637092 fix: Only report a live error from the visible ha-camera-stream player (#2596)
- Closes: #2583
2026-07-22 13:45:46 -07:00
Dermot Duffy 7d66c5f005 chore: Replace ts-prune with knip (#2595)
- Closes: #2593
2026-07-22 13:26:34 -07:00
Dermot Duffy 2ca4aa95e5 chore: Replace deprecated 'node' module resolution with 'bundler' and tighten compiler options (#2594) 2026-07-22 12:12:00 -07:00
Dermot Duffy 87ecbc7e45 feat: Trigger automations on call answered, rejected and hung up (#2591) 2026-07-22 11:11:57 -07:00
Dermot Duffy f28b16a4b5 feat: Compress related editor fields into grid rows (#2589) 2026-07-21 22:31:13 -07:00
Dermot Duffy e29c0826cf feat: Add a simple mode to the visual card editor (#2588) 2026-07-21 19:13:29 -07:00
Dermot Duffy b6e1de5999 feat: Modernize the visual card editor. (#2586) 2026-07-20 20:44:16 -07:00
Dermot Duffy e204ec183f fix: Correct status bar alignment in Safari (#2581)
- Closes  #2578
2026-07-15 14:28:47 -07:00
Dermot Duffy c02c692f68 feat: Add experimental rewrite of go2rtc live provider (MSE/WebRTC/MP4/MJPEG) (#2580)
- Closes #2556
- Closes #2450

**Key intended features:**
 - go2rtc compatible
- 100% test coverage to significantly improve ability to test, maintain
and work around browser weirdnesses (e.g. Safari).
 - Written from the ground up in the style of the rest of the project.

**To use:**
 - Change `live_provider` from `go2rtc` to `go2rtc-experimental`.
2026-07-14 14:22:41 -07:00
Dermot Duffy 5662e48c22 test: Add regression test for media info stability (#2571)
- Relates to: #2563
2026-07-08 11:24:18 -07:00
Dermot Duffy fb1bbc739e feat: Automatically recover from frozen live streams (#2569)
- Closes: #2099
2026-07-07 21:37:24 -07:00
Dermot Duffy 9f956fe89f fix: Match state trigger/condition semantics to HA (#2565)
* Closes #2530
2026-07-04 10:23:51 -07:00
Dermot Duffy 3494706225 fix: Use auto_unmute as input to stream selection for HA (#2562)
- Closes #2479
2026-07-03 08:19:11 -07:00
Dermot Duffy 6a6d65d5c5 fix: Highlight folder icon for correct folder / media (#2560)
- Closes: #2128 
 - Closes: #2129
2026-07-02 10:26:30 -07:00
dermotduffy 3bb5ccd14f chore: Add missing close keyword: close #2529 (#2427) 2026-06-30 21:40:44 -07:00
dermotduffy 8ebb88cde9 chore: Add missing close keyword: close #2438 (#2468) 2026-06-30 21:40:44 -07:00
dermotduffy 3a78a8d3e9 chore: Add missing close keyword: close #2525 (#2553) 2026-06-30 21:40:43 -07:00
Dermot Duffy 937ba647a7 fix: Fix audio for cameras with mixed WebRTC/HLS capabilities (#2557)
- Closes #2479
2026-06-30 17:45:13 -07:00
Dermot Duffy 5c97e55a56 fix: Honor configured home preset / merge presets. (#2553)
- Closes:
https://github.com/dermotduffy/advanced-camera-card/issues/2525
2026-06-30 17:45:13 -07:00
Dermot Duffy d833edb65b perf: lazy-load js-yaml (~107KB) as needed (#2551)
- Closes: #2532
2026-06-30 17:45:13 -07:00
Dermot Duffy 3fe5e4004a fix: prevent infinite re-render loop from issue retry callbacks (#2550) 2026-06-30 17:45:13 -07:00
Dermot Duffy c3d3dd3934 chore: Update ha-nunjucks to 1.6.2 (#2549)
- Replaces:
https://github.com/dermotduffy/advanced-camera-card/pull/2515
2026-06-30 17:45:13 -07:00
Dermot Duffy ea251ca988 perf(bundle): lazy-load the nunjucks template engine (#2535)
Closes #2531.

## Summary

The full `nunjucks` templating engine (~226KB) plus `ha-nunjucks`
(~46KB) — roughly **272KB, ~13% of the eager entry chunk** — was
statically imported and downloaded by every card on initial load, even
though templates only apply when a config value contains a `{{ … }}` /
`{% … %}` delimiter. Most cards use no templates and never need the
engine.

This defers the engine behind a dynamic `import('ha-nunjucks/dist')` so
it ships in a separate, on-demand chunk instead of the eager
`card-*.js`.

## Approach

The render path (`TemplateRenderer.renderRecursively`) is **kept
synchronous** — it is called from many synchronous hot paths
(condition/trigger evaluators, picture-elements rendering, actions,
folder matchers), and making it async would be a large, high-risk
refactor of the evaluation core.

Instead:

- **New `src/card-controller/templates/engine.ts`** — a module-level
singleton lazy loader (`loadTemplateEngine()` / `getTemplateEngine()`)
shared across all `TemplateRenderer` instances, plus a
`containsTemplate()` delimiter helper.
- **Delimiter gating** — strings without a delimiter never touch the
engine (the overwhelming majority of renders).
- **Pre-warm at config time** — because every template string originates
in the config, a new mandatory `TEMPLATE_ENGINE` initialization aspect
loads the engine before first render whenever the config contains a
delimiter. This **guarantees no raw `{{ … }}` flash**: content/condition
rendering is blocked until the engine is present for template-using
cards. Cards without templates never load it.

## Result

- nunjucks + ha-nunjucks move out of the eager chunk into a separate
chunk fetched only when a card actually uses templates.
- No change to the synchronous public render API; condition/trigger
evaluation core untouched.

## Tests

- New `engine.ts` loader coverage (concurrent load, cached reuse,
not-loaded fallback).
- The 11 existing test files that render real (delimiter-bearing)
templates declare their dependency explicitly via
`beforeAll(loadTemplateEngine)` — no global/implicit setup hook.
- Full suite green (4764 tests), lint and ts-prune clean, per-file 100%
coverage maintained for the affected directories.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_016ykWemdkZrgywvC71pHc6c

---
_Generated by [Claude
Code](https://claude.ai/code/session_016ykWemdkZrgywvC71pHc6c)_
2026-06-30 17:45:13 -07:00
Dermot Duffy 7dc29865b8 chore: Enforce consistent type imports via @typescript-eslint/consistent-type-imports (#2545) 2026-06-30 17:45:13 -07:00
Dermot Duffy a87e5b8eeb chore: Enforce interfaces for object types (#2544) 2026-06-30 17:45:13 -07:00
Dermot Duffy c8e7fdd808 chore: Add typechecking and pruning to CI (#2543) 2026-06-30 17:45:13 -07:00
Dermot Duffy a96dc2e863 chore: Enforce import order via @ianvs/prettier-plugin-sort-imports (#2542) 2026-06-30 17:45:13 -07:00
Dermot Duffy 95f35bd326 refactor: Accept unknown in errorToConsole to drop as Error casts (#2541) 2026-06-30 17:45:13 -07:00
Dermot Duffy fcef48e75a test: Silence superfluous console output during test runs (#2540) 2026-06-30 17:45:13 -07:00
Dermot Duffy 921d45e577 chore: Enforce a few house rules via eslint (#2539) 2026-06-30 17:45:13 -07:00
Dermot Duffy 5572ec728e refactor: Centralize template render type assertion (#2538) 2026-06-30 17:45:13 -07:00
Dermot Duffy a31816c168 feat: Add event-based automation triggers (#2537) 2026-06-30 17:45:13 -07:00