Repository: dermotduffy/frigate-hass-card
Pull request #1575 (2024-09-23 03:43:24)

feat: Add new `casting` profile

felipecrs: The profile works great by the way, except that I could not make it work under my TV.

It has been some time since I don't actually perform casting.

It just stays like this forever:

https://github.com/user-attachments/assets/1dd3b82f-c90b-46fb-be53-3bc55e0b0186

Here's the configuration I'm using:

```yaml
type: custom:frigate-card
cameras:
  - camera_entity: camera.video_porteiro
    live_provider: go2rtc
    image:
      refresh_seconds: 1
profiles:
  - low-performance
  - casting
live:
  controls:
    builtin: false
  show_image_during_load: true
image:
  refresh_seconds: 1
```

By the way, I'm not sure which `image.refresh_seconds` is actually effective for the `show_image_during_load`. That's why I have both.

I wonder if you can reproduce it.
dermotduffy: > By the way, I'm not sure which image.refresh_seconds is actually effective for the show_image_during_load. That's why I have both.

The one under `camera` should be it, since that is the configuration for the "live provider".

Let me try to cast (also been a while since I did it!).

felipecrs: I can reproduce that behavior in my older Sony TV with Android TV 9. It doesn't get past the Connected HA screen.

However, in my two Chromecast with Google TV with Android 12 devices, they get past that screen and behaves as the video above.

Regardless, something seems wrong beyond the card itself.
felipecrs: I don't think it's worth keeping this PR unmerged anyway. The only feedback I have would be if these settings be included in the profile or not:

1. `live.controls.builtin: false`
2. `live.show_image_during_load: true`
3. Perhaps `cameras_global.image.refresh_seconds: 1`?

Patch:
diff --git a/docs/configuration/profiles.md b/docs/configuration/profiles.md
index ac4441b3..900d652d 100644
--- a/docs/configuration/profiles.md
+++ b/docs/configuration/profiles.md
@@ -14,10 +14,21 @@ the user.
 
 ?> Profiles are applied top to bottom. If multiple profiles change a configuration default, then the last one "wins"
 
-| Profile name      | Purpose                    |
-| ----------------- | -------------------------- |
-| `low-performance` | Increase card performance. |
-| `scrubbing`       | Allow media "scrubbing".   |
+| Profile name      | Purpose                                        |
+| ----------------- | ---------------------------------------------- |
+| `casting`         | Configure the card to be casted.               |
+| `low-performance` | Configure the card for lower end devices.      |
+| `scrubbing`       | Configure the card to allow "video scrubbing". |
+
+## `casting`
+
+To aid casting the card to Chromecast devices, the `casting` profile will adjust card defaults to better suit casting. You may wish to combine this the `low-performance` profile below, since Chromecast devices tend to lower performance. To combine, list `low-performance` first, to allow `casting` to take precedence:
+
+```yaml
+profiles:
+  - low-performance
+  - casting
+```
 
 ## `low-performance`
 
@@ -42,6 +53,7 @@ See the [source code](https://github.com/dermotduffy/frigate-hass-card/blob/dev/
 
 ```yaml
 profiles:
+  - casting
   - low-performance
   - scrubbing
 ```
diff --git a/src/config/profiles/casting.ts b/src/config/profiles/casting.ts
new file mode 100644
index 00000000..c53bf972
--- /dev/null
+++ b/src/config/profiles/casting.ts
@@ -0,0 +1,23 @@
+import {
+  CONF_CAMERAS_GLOBAL_IMAGE_REFRESH_SECONDS,
+  CONF_DIMENSIONS_ASPECT_RATIO,
+  CONF_LIVE_AUTO_UNMUTE,
+  CONF_LIVE_CONTROLS_BUILTIN,
+  CONF_LIVE_SHOW_IMAGE_DURING_LOAD,
+  CONF_MEDIA_VIEWER_CONTROLS_BUILTIN,
+  CONF_MENU_STYLE,
+} from '../../const.js';
+
+export const CASTING_PROFILE = {
+  [CONF_MENU_STYLE]: 'none' as const,
+  [CONF_LIVE_AUTO_UNMUTE]: ['selected', 'visible'],
+  [CONF_DIMENSIONS_ASPECT_RATIO]: '16:9',
+  [CONF_LIVE_CONTROLS_BUILTIN]: false,
+  [CONF_MEDIA_VIEWER_CONTROLS_BUILTIN]: false,
+
+  // These values are defaults anyway unless another profile (e.g.
+  // low-performance) is also selected, but at pretty important to a good
+  // experience so are reset here.
+  [CONF_CAMERAS_GLOBAL_IMAGE_REFRESH_SECONDS]: 1,
+  [CONF_LIVE_SHOW_IMAGE_DURING_LOAD]: true,
+};
diff --git a/src/config/profiles/index.ts b/src/config/profiles/index.ts
index 3499dab5..9cea83fb 100644
--- a/src/config/profiles/index.ts
+++ b/src/config/profiles/index.ts
@@ -1,10 +1,12 @@
+import { deepRemoveDefaults } from '../../utils/zod.js';
 import { getConfigValue, setConfigValue } from '../management.js';
 import { ProfileType, RawFrigateCardConfig, frigateCardConfigSchema } from '../types.js';
-import { deepRemoveDefaults } from '../../utils/zod.js';
+import { CASTING_PROFILE } from './casting.js';
 import { LOW_PERFORMANCE_PROFILE } from './low-performance.js';
 import { SCRUBBING_PROFILE } from './scrubbing.js';
 
 const PROFILES = {
+  casting: CASTING_PROFILE,
   'low-performance': LOW_PERFORMANCE_PROFILE,
   scrubbing: SCRUBBING_PROFILE,
 };
diff --git a/src/config/types.ts b/src/config/types.ts
index 07bedb6c..6e95a03c 100644
--- a/src/config/types.ts
+++ b/src/config/types.ts
@@ -1973,7 +1973,7 @@ export interface CardWideConfig {
 // *************************************************************************
 //                      *** Profile Configuration ***
 // *************************************************************************
-const PROFILES = ['low-performance', 'scrubbing'] as const;
+const PROFILES = ['casting', 'low-performance', 'scrubbing'] as const;
 export type ProfileType = (typeof PROFILES)[number];
 export const profilesSchema = z.enum(PROFILES).array().optional();
 
diff --git a/src/editor.ts b/src/editor.ts
index 194fce21..4f43c307 100644
--- a/src/editor.ts
+++ b/src/editor.ts
@@ -611,6 +611,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
 
   protected _profiles: EditorSelectOption[] = [
     { value: '', label: '' },
+    { value: 'casting', label: localize('config.profiles.casting') },
     { value: 'low-performance', label: localize('config.profiles.low-performance') },
     { value: 'scrubbing', label: localize('config.profiles.scrubbing') },
   ];
diff --git a/src/localize/languages/ca.json b/src/localize/languages/ca.json
index 0eba4fcc..63ba95c2 100644
--- a/src/localize/languages/ca.json
+++ b/src/localize/languages/ca.json
@@ -413,6 +413,7 @@
       "warning": "Aquesta targeta està en mode de perfil baix, de manera que els valors predeterminats han canviat per optimitzar el rendiment"
     },
     "profiles": {
+      "casting": "",
       "editor_label": "",
       "low-performance": "",
       "scrubbing": ""
diff --git a/src/localize/languages/en.json b/src/localize/languages/en.json
index 39ae61c1..d3ba2a2c 100644
--- a/src/localize/languages/en.json
+++ b/src/localize/languages/en.json
@@ -413,6 +413,7 @@
       "warning": "This card is in low profile mode so defaults have changed to optimize performance"
     },
     "profiles": {
+      "casting": "Casting",
       "editor_label": "Configuration profiles",
       "low-performance": "Low performance",
       "scrubbing": "Video scrubbing"
diff --git a/src/localize/languages/fr.json b/src/localize/languages/fr.json
index ee89a4f2..6c02fc0b 100644
--- a/src/localize/languages/fr.json
+++ b/src/localize/languages/fr.json
@@ -413,6 +413,7 @@
       "warning": "Cette carte est en mode profil bas, les paramètres par défaut ont donc été modifiés pour optimiser les performances."
     },
     "profiles": {
+      "casting": "",
       "editor_label": "Configuration des profils",
       "low-performance": "Basse performance",
       "scrubbing": "Balayage vidéo"
diff --git a/src/localize/languages/it.json b/src/localize/languages/it.json
index 9c9333c2..76338a10 100644
--- a/src/localize/languages/it.json
+++ b/src/localize/languages/it.json
@@ -413,6 +413,7 @@
       "warning": "Questa scheda è in modalità basso profilo, quindi le impostazioni predefinite sono state modificate per ottimizzare le prestazioni"
     },
     "profiles": {
+      "casting": "",
       "editor_label": "",
       "low-performance": "",
       "scrubbing": ""
diff --git a/src/localize/languages/pt-BR.json b/src/localize/languages/pt-BR.json
index 617eb10c..e0d017c5 100644
--- a/src/localize/languages/pt-BR.json
+++ b/src/localize/languages/pt-BR.json
@@ -413,6 +413,7 @@
       "warning": "Este cartão está no modo de baixo desempenho, então os padrões foram alterados para otimizar o desempenho"
     },
     "profiles": {
+      "casting": "",
       "editor_label": "",
       "low-performance": "",
       "scrubbing": ""
diff --git a/src/localize/languages/pt-PT.json b/src/localize/languages/pt-PT.json
index 3e1a1b9b..53cb03fa 100644
--- a/src/localize/languages/pt-PT.json
+++ b/src/localize/languages/pt-PT.json
@@ -413,6 +413,7 @@
       "warning": "Avisos"
     },
     "profiles": {
+      "casting": "",
       "editor_label": "",
       "low-performance": "",
       "scrubbing": ""


Test patch:
diff --git a/tests/config/profiles/casting.test.ts b/tests/config/profiles/casting.test.ts
new file mode 100644
index 00000000..886f86d0
--- /dev/null
+++ b/tests/config/profiles/casting.test.ts
@@ -0,0 +1,28 @@
+import { expect, it } from 'vitest';
+import { setProfiles } from '../../../src/config/profiles';
+import { frigateCardConfigSchema } from '../../../src/config/types';
+import { createRawConfig } from '../../test-utils';
+import { CASTING_PROFILE } from '../../../src/config/profiles/casting';
+
+it('should contain expected defaults', () => {
+  expect(CASTING_PROFILE).toEqual({
+    'cameras_global.image.refresh_seconds': 1,
+    'dimensions.aspect_ratio': '16:9',
+    'live.auto_unmute': ['selected', 'visible'],
+    'live.controls.builtin': false,
+    'live.show_image_during_load': true,
+    'media_viewer.controls.builtin': false,
+    'menu.style': 'none',
+  });
+});
+
+it('should be parseable after application', () => {
+  const rawInputConfig = createRawConfig();
+  const parsedConfig = frigateCardConfigSchema.parse(rawInputConfig);
+
+  setProfiles(rawInputConfig, parsedConfig, ['casting']);
+
+  // Reparse the config to ensure the profile did not introduce errors.
+  const parseResult = frigateCardConfigSchema.safeParse(parsedConfig);
+  expect(parseResult.success, parseResult.error?.toString()).toBeTruthy();
+});