From 9098eb3483e08a2a8cfa4648404c359951437ebe Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Wed, 2 Oct 2024 23:45:16 -0300 Subject: [PATCH] feat: Rework casting profile and documentation (#1602) * feat: Rework casting profile and documentation * Apply suggestions from code review Co-authored-by: Dermot Duffy * Some other minor details * Update docs/usage/casting.md Co-authored-by: Dermot Duffy * Improve const.ts * Some other small changes * Update docs/usage/casting.md Co-authored-by: Dermot Duffy --------- Co-authored-by: Dermot Duffy --- docs/usage/casting.md | 156 ++++++++++++++++++-------- src/config/profiles/casting.ts | 23 +++- src/const.ts | 11 +- tests/config/profiles/casting.test.ts | 6 + 4 files changed, 144 insertions(+), 52 deletions(-) diff --git a/docs/usage/casting.md b/docs/usage/casting.md index 960e3b34..8a92b5d3 100644 --- a/docs/usage/casting.md +++ b/docs/usage/casting.md @@ -1,18 +1,115 @@ -# Casting the Card +# Casting -This card can be (Chrome) casted to a device (such as a [Nest -Hub](https://store.google.com/us/product/nest_hub_2nd_gen)) through the use of -[Home Assistant Cast](https://cast.home-assistant.io/). +This card allows casting a camera stream to a Google Cast device. You can enable the cast button with: -## Instructions +```yaml +type: custom:frigate-card +cameras: + - camera_entity: camera.office + live_provider: go2rtc +menu: + media_player: + enabled: true +``` -- Visit [Home Assistant Cast](https://cast.home-assistant.io/) and click `Start Casting` -- Enter your Home Assistant URL, and authorize your account. -- Click `Start Casting` and choose the device to cast to from the browser menu. -- Choose which view/dashboard to display. -- If successful, the view will be cast to the device. +Clicking this button will allow you to choose a Google Cast device to cast the camera stream to, or the clip that is currently playing. -## Limitations +There are two different casting methods for cameras: `standard` and `dashboard` (see below). + +## Standard Casting + +This is the default casting method. + +```yaml +type: custom:frigate-card +cameras: + - camera_entity: camera.office + live_provider: go2rtc + cast: + method: standard +menu: + media_player: + enabled: true +``` + +This will send the camera stream to the Google Cast device through the use of the [`media_player.play_media` Home Assistant action](https://www.home-assistant.io/integrations/media_source/#playing-media-from-a-media-source). + +The main disadvantage of this method is that the stream will be played through HLS which has a **delay of around 10 seconds**, making it sub-optimal for live monitoring. + +## Dashboard Casting + +This method is powered by [Home Assistant Cast](https://cast.home-assistant.io) and requires additional configuration, but allows **low latency streaming** with go2rtc. + +```yaml +type: custom:frigate-card +cameras: + - camera_entity: camera.office + live_provider: go2rtc + cast: + method: dashboard + dashboard: + dashboard_path: dashboard-cast + view_path: office +``` + +When the cast button is clicked, the card will perform the following Home Assistant action: + +```yaml +action: cast.show_lovelace_view +data: + entity_id: media_player.kitchen + dashboard_path: dashboard-cast + view_path: office +``` + +Which in turn will cast the specified Home Assistant dashboard (and view) to the Google Cast device. + +For the example above to work, a dashboard with `dashboard-cast` URL must exist: + +![image](https://github.com/user-attachments/assets/67f0e145-df05-412a-8c6d-897feb5439d2) + +Within that dashboard, create an `office` view and finally within that view you can place the Frigate card configured for that camera for a proper full-screen casting experience: + +```yaml +views: + - title: Office + type: panel + path: office + cards: + - type: custom:frigate-card + cameras: + - camera_entity: camera.office + live_provider: go2rtc + profiles: + - low-performance + - casting +``` + +The `casting` profile pre-configures the card to be casted to a 16:9 screen (such as a TV), including hiding all interactive elements and the menu. The `low-performance` profile is also recommended, as normally casting devices have limited hardware capabilities. + +![](https://github.com/user-attachments/assets/bd96c4ad-36f5-4501-9018-23b496e7edc5) + +When casting to a Google Nest Hub, the following configuration can be used: + +```yaml +type: custom:frigate-card +cameras: + - camera_entity: camera.office + live_provider: go2rtc +profiles: + - low-performance + - casting +menu: + style: hidden +dimensions: + aspect_ratio: 1024:600 +``` + +This restores the menu and sets the aspect ratio to 1024:600, which is the resolution of the Nest Hub. + +![](../images/card-on-nest-hub.jpg 'Casting on a Nest Hub :size=400') + +### Limitations Casting Home Assistant dashboards comes with a number of caveats: @@ -20,38 +117,5 @@ Casting Home Assistant dashboards comes with a number of caveats: ([source](https://cast.home-assistant.io/faq.html)). This means clips playing and the `ha` live provider can not work. Other live providers such as `jsmpeg` and `webrtc-card` function correctly. -- The Javascript fullscreen API does not work (so the fullscreen button does not - work, but see below for an equivalent). - -## The `casting` profile - -The optional [casting profile](../configuration/profiles.md?id=casting) provides -some defaults to improve your casting experience. Use it like: - -```yaml -profiles: - - casting -``` - -## Recommended configuration for Nest Hub - -Using a `panel` dashboard with the following base configuration will result in -the card consuming the entire device screen: - -### Configuration - -```yaml -type: custom:frigate-card -cameras: - - camera_entity: camera.office - live_provider: go2rtc -dimensions: - aspect_ratio: 1024:600 - aspect_ratio_mode: static -profile: - - casting -``` - -### Result - -![](../images/card-on-nest-hub.jpg 'Casting on a Nest Hub :size=400') +- The Javascript fullscreen API does not work, so the fullscreen button does not + work (use a `panel` view instead). diff --git a/src/config/profiles/casting.ts b/src/config/profiles/casting.ts index 6537700a..65f11faa 100644 --- a/src/config/profiles/casting.ts +++ b/src/config/profiles/casting.ts @@ -1,18 +1,37 @@ import { CONF_CAMERAS_GLOBAL_IMAGE_REFRESH_SECONDS, CONF_DIMENSIONS_ASPECT_RATIO, + CONF_DIMENSIONS_ASPECT_RATIO_MODE, CONF_LIVE_AUTO_UNMUTE, CONF_LIVE_CONTROLS_BUILTIN, CONF_LIVE_SHOW_IMAGE_DURING_LOAD, CONF_MEDIA_VIEWER_CONTROLS_BUILTIN, + CONF_MENU_BUTTONS_FULLSCREEN, + CONF_MENU_BUTTONS_MEDIA_PLAYER, + CONF_MENU_BUTTONS_MUTE, + CONF_MENU_BUTTONS_PLAY, + CONF_MENU_STYLE, } from '../../const.js'; export const CASTING_PROFILE = { - [CONF_LIVE_AUTO_UNMUTE]: ['selected', 'visible'], - [CONF_DIMENSIONS_ASPECT_RATIO]: '16:9', [CONF_LIVE_CONTROLS_BUILTIN]: false, [CONF_MEDIA_VIEWER_CONTROLS_BUILTIN]: false, + // TVs are generally not touch-enabled, so we don't want to show the menu + [CONF_MENU_STYLE]: 'none', + + // But in case the user enables the menu, let's make sure to enable the + // buttons that make sense and disable the ones that don't + [`${CONF_MENU_BUTTONS_PLAY}.enabled`]: true, + [`${CONF_MENU_BUTTONS_MUTE}.enabled`]: true, + [`${CONF_MENU_BUTTONS_FULLSCREEN}.enabled`]: false, + [`${CONF_MENU_BUTTONS_MEDIA_PLAYER}.enabled`]: false, + + [CONF_LIVE_AUTO_UNMUTE]: ['selected', 'visible'], + + [CONF_DIMENSIONS_ASPECT_RATIO_MODE]: 'static', + [CONF_DIMENSIONS_ASPECT_RATIO]: '16:9', + // 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. diff --git a/src/const.ts b/src/const.ts index 2b851ffa..e37be8a9 100644 --- a/src/const.ts +++ b/src/const.ts @@ -328,11 +328,14 @@ export const CONF_MENU_STYLE = `${CONF_MENU}.style` as const; export const CONF_MENU_BUTTON_SIZE = `${CONF_MENU}.button_size` as const; export const CONF_MENU_BUTTONS = `${CONF_MENU}.buttons` as const; -export const CONF_MENU_BUTTONS_FRIGATE = `${CONF_MENU}.buttons.frigate` as const; -export const CONF_MENU_BUTTONS_CAMERA_UI = `${CONF_MENU}.buttons.camera_ui` as const; +export const CONF_MENU_BUTTONS_FRIGATE = `${CONF_MENU_BUTTONS}.frigate` as const; +export const CONF_MENU_BUTTONS_CAMERA_UI = `${CONF_MENU_BUTTONS}.camera_ui` as const; +export const CONF_MENU_BUTTONS_FULLSCREEN = `${CONF_MENU_BUTTONS}.fullscreen` as const; +export const CONF_MENU_BUTTONS_PLAY = `${CONF_MENU_BUTTONS}.play` as const; +export const CONF_MENU_BUTTONS_MUTE = `${CONF_MENU_BUTTONS}.mute` as const; export const CONF_MENU_BUTTONS_MEDIA_PLAYER = - `${CONF_MENU}.buttons.media_player` as const; -export const CONF_MENU_BUTTONS_TIMELINE = `${CONF_MENU}.buttons.timeline` as const; + `${CONF_MENU_BUTTONS}.media_player` as const; +export const CONF_MENU_BUTTONS_TIMELINE = `${CONF_MENU_BUTTONS}.timeline` as const; export const CONF_STATUS_BAR = 'status_bar' as const; export const CONF_STATUS_BAR_POSITION = `${CONF_STATUS_BAR}.position` as const; diff --git a/tests/config/profiles/casting.test.ts b/tests/config/profiles/casting.test.ts index cad2188d..d4a77515 100644 --- a/tests/config/profiles/casting.test.ts +++ b/tests/config/profiles/casting.test.ts @@ -7,11 +7,17 @@ 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_mode': 'static', '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.buttons.fullscreen.enabled': false, + 'menu.buttons.media_player.enabled': false, + 'menu.buttons.mute.enabled': true, + 'menu.buttons.play.enabled': true, + 'menu.style': 'none', }); });