Move all live provider options into cameras.

This commit is contained in:
Dermot Duffy
2023-02-26 18:45:37 -08:00
parent 29c0279e56
commit bf2952b228
13 changed files with 355 additions and 265 deletions
+62 -69
View File
@@ -162,7 +162,7 @@ cameras:
| `zone` | | :heavy_multiplication_x: | A Frigate zone used to filter events (clips & snapshots), e.g. `front_door`.|
| `client_id` | `frigate` | :heavy_multiplication_x: | The Frigate client id to use. If this Home Assistant server has multiple Frigate server backends configured, this selects which server should be used. It should be set to the MQTT client id configured for this server, see [Frigate Integration Multiple Instance Support](https://docs.frigate.video/integrations/home-assistant/#multiple-instance-support).|
#### Camera go2rtc configuration
#### Live Provider: Camera go2rtc configuration
The `go2rtc` block configures use of the `go2rtc` live provider. This configuration is included as part of a camera entry in the `cameras` array.
@@ -176,9 +176,9 @@ cameras:
| `modes` | `[webrtc, mse, mp4, mjpeg]` | :heavy_multiplication_x: | An ordered array of `go2rtc` modes to use. Valid values are `webrtc`, `mse`, `mp4` or `mjpeg` values. |
| `stream` | Determind by camera engine (e.g. `frigate` camera name). | :heavy_multiplication_x: | A valid `go2rtc` stream name. |
#### Camera WebRTC Card configuration
#### Live Provider: Camera WebRTC Card configuration
The `webrtc_card` block configures only the entity/URL for this camera to be used with the WebRTC Card live provider. This configuration is included as part of a camera entry in the `cameras` array.
Configures the `webrtc_card` live provider:
```yaml
cameras:
@@ -187,11 +187,42 @@ cameras:
| Option | Default | Overridable | Description |
| - | - | - | - |
| `entity` | | :heavy_multiplication_x: | The RTSP entity to pass to the WebRTC Card for this camera. Specify this OR `url` (below). |
| `url` | | :heavy_multiplication_x: | The RTSP url to pass to the WebRTC Card. Specify this OR `entity` (above). |
| `entity` | | :heavy_multiplication_x: | The RTSP entity to pass to the WebRTC Card for this camera. Specify this OR `url`. |
| `url` | | :heavy_multiplication_x: | The RTSP url to pass to the WebRTC Card. Specify this OR `entity`. |
| `*`| | :heavy_multiplication_x: | Any options specified in the `webrtc_card:` YAML dictionary are silently passed through to the AlexxIT's WebRTC Card. See [WebRTC Configuration](https://github.com/AlexxIT/WebRTC#configuration) for full details this external card provides. |
See [Using the WebRTC Card](#webrtc) below for more details on how to use the WebRTC Card live provider.
#### Live Provider: Image Configuration
All configuration is under:
```yaml
cameras:
- image:
```
| Option | Default | Overridable | Description |
| - | - | - | - |
| `refresh_seconds` | 1 | :heavy_multiplication_x: | The image will be refreshed at least every `refresh_seconds`. `0` implies no refreshing. |
#### Live Provider: JSMPEG Configuration
All configuration is under:
```yaml
cameras:
- jsmpeg:
```
| Option | Default | Overridable | Description |
| - | - | - | - |
| `options` | | :heavy_multiplication_x: | **Advanced users only**: Control the underlying [JSMPEG library options](https://github.com/phoboslab/jsmpeg#usage). Supports setting these JSMPEG options `{audio, video, pauseWhenHidden, disableGl, disableWebAssembly, preserveDrawingBuffer, progressive, throttled, chunkSize, maxAudioLag, videoBufferSize, audioBufferSize}`. This is not necessary for the vast majority of users: only set these flags if you know what you're doing, as you may entirely break video rendering in the card.|
<a name="webrtc-live-configuration"></a>
<a name="camera-dependencies-configuration"></a>
#### Camera Dependency Configuration
@@ -363,53 +394,8 @@ See the [fully expanded live configuration example](#config-expanded-live) for h
| `show_image_during_load` | `true` | :white_check_mark: | If `true`, during the initial stream load, the `image` live provider will be shown instead of the loading video stream. This still image will auto-refresh and is replaced with the live stream once loaded. |
| `actions` | | :white_check_mark: | Actions to use for the `live` view. See [actions](#actions) below.|
| `controls` | | :white_check_mark: | Configuration for the `live` view controls. See below. |
| `jsmpeg` | | :white_check_mark: | Configuration for the `jsmpeg` live provider. See below.|
| `webrtc_card` | | :white_check_mark: | Configuration for the `webrtc-card` live provider. See below.|
| `layout` | | :white_check_mark: | See [media layout](#media-layout) below.|
#### Live Provider: Image Configuration
All configuration is under:
```yaml
live:
image:
```
| Option | Default | Overridable | Description |
| - | - | - | - |
| `refresh_seconds` | 1 | :white_check_mark: | The image will be refreshed at least every `refresh_seconds`. `0` implies no refreshing. |
#### Live Provider: JSMPEG Configuration
All configuration is under:
```yaml
live:
jsmpeg:
```
| Option | Default | Overridable | Description |
| - | - | - | - |
| `options` | | :white_check_mark: | **Advanced users only**: Control the underlying [JSMPEG library options](https://github.com/phoboslab/jsmpeg#usage). Supports setting these JSMPEG options `{audio, video, pauseWhenHidden, disableGl, disableWebAssembly, preserveDrawingBuffer, progressive, throttled, chunkSize, maxAudioLag, videoBufferSize, audioBufferSize}`. This is not necessary for the vast majority of users: only set these flags if you know what you're doing, as you may entirely break video rendering in the card.|
<a name="webrtc-live-configuration"></a>
#### Live Provider: WebRTC Card Configuration
All configuration is under:
```yaml
live:
webrtc_card:
```
| Option | Default | Overridable | Description |
| - | - | - | - |
| `*`| | :white_check_mark: | Any options specified in the `webrtc_card:` YAML dictionary are silently passed through to the AlexxIT's WebRTC Card. See [WebRTC Configuration](https://github.com/AlexxIT/WebRTC#configuration) for full details this external card provides. This implies that if `entity` or `url` are specified here they will override the matching named parameters under the per camera configuration. |
See [Using WebRTC Card](#webrtc) below for more details on how to embed AlexxIT's WebRTC Card with the Frigate Card.
#### Live Controls: Thumbnails
All configuration is under:
@@ -1394,6 +1380,32 @@ cameras:
- mp4
- mjpeg
stream: sitting_room
- camera_entity: camera.sitting_room_webrtc_card
live_provider: webrtc_card
webrtc_card:
# Arbitrary WebRTC Card options, see https://github.com/AlexxIT/WebRTC#configuration .
entity: camera.sitting_room_rtsp
ui: true
- camera_entity: camera.kitchen
live_provider: jsmpeg
jsmpeg:
options:
audio: false
video: true
pauseWhenHidden: false
disableGl: false
disableWebAssembly: false
preserveDrawingBuffer: false
progressive: true
throttled: true
chunkSize: 1048576
maxAudioLag: 10
videoBufferSize: 524288
audioBufferSize: 131072
- camera_entity: camera.back_yard
live_provider: image
image:
refresh_seconds: 1
```
</details>
@@ -1533,25 +1545,6 @@ live:
lazy_unload: never
draggable: true
transition_effect: slide
webrtc_card:
# Arbitrary WebRTC Card options, see https://github.com/AlexxIT/WebRTC#configuration .
ui: true
jsmpeg:
options:
audio: false
video: true
pauseWhenHidden: false
disableGl: false
disableWebAssembly: false
preserveDrawingBuffer: false
progressive: true
throttled: true
chunkSize: 1048576
maxAudioLag: 10
videoBufferSize: 524288
audioBufferSize: 131072
image:
refresh_seconds: 1
controls:
next_previous:
style: chevrons
+3 -6
View File
@@ -2,7 +2,7 @@ import { HomeAssistant } from 'custom-card-helpers';
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import liveImageStyle from '../../scss/live-image.scss';
import { CameraConfig, FrigateCardMediaPlayer, LiveImageConfig } from '../../types.js';
import { CameraConfig, FrigateCardMediaPlayer } from '../../types.js';
import { getStateObjOrDispatchError } from './live.js';
import '../image.js';
@@ -14,9 +14,6 @@ export class FrigateCardLiveImage extends LitElement implements FrigateCardMedia
@property({ attribute: false })
public cameraConfig?: CameraConfig;
@property({ attribute: false })
public liveImageConfig?: LiveImageConfig;
@state()
protected _playing = true;
@@ -46,7 +43,7 @@ export class FrigateCardLiveImage extends LitElement implements FrigateCardMedia
}
protected render(): TemplateResult | void {
if (!this.hass || !this.liveImageConfig) {
if (!this.hass || !this.cameraConfig) {
return;
}
@@ -55,7 +52,7 @@ export class FrigateCardLiveImage extends LitElement implements FrigateCardMedia
return html` <frigate-card-image
.imageConfig=${{
mode: 'camera' as const,
refresh_seconds: this._playing ? this.liveImageConfig.refresh_seconds : 0,
refresh_seconds: this._playing ? this.cameraConfig.image.refresh_seconds : 0,
// Don't need to pass layout options as FrigateCardLiveProvider has
// already taken care of this for us.
}}
+1 -6
View File
@@ -10,11 +10,9 @@ import {
CardWideConfig,
ExtendedHomeAssistant,
FrigateCardMediaPlayer,
JSMPEGConfig,
} from '../../types.js';
import { dispatchMediaLoadedEvent } from '../../utils/media-info.js';
import { dispatchErrorMessageEvent } from '../message.js';
import { contentsChanged } from '../../utils/basic.js';
import { CameraEndpoints } from '../../camera-manager/types.js';
import { getEndpointAddressOrDispatchError } from '../../utils/endpoint.js';
@@ -32,9 +30,6 @@ export class FrigateCardLiveJSMPEG extends LitElement implements FrigateCardMedi
@property({ attribute: false })
public cameraEndpoints?: CameraEndpoints;
@property({ attribute: false, hasChanged: contentsChanged })
public jsmpegConfig?: JSMPEGConfig;
@property({ attribute: false })
public cardWideConfig?: CardWideConfig;
@@ -99,7 +94,7 @@ export class FrigateCardLiveJSMPEG extends LitElement implements FrigateCardMedi
videoBufferSize: 1024 * 1024 * 4,
// Override with user-specified options.
...this.jsmpegConfig?.options,
...this.cameraConfig?.jsmpeg?.options,
// Don't allow the player to internally reconnect, as it may re-use a
// URL with a (newly) invalid signature, e.g. during a Home Assistant
+2 -18
View File
@@ -9,9 +9,7 @@ import {
CardWideConfig,
FrigateCardError,
FrigateCardMediaPlayer,
WebRTCCardConfig,
} from '../../types.js';
import { contentsChanged } from '../../utils/basic.js';
import { dispatchMediaLoadedEvent } from '../../utils/media-info.js';
import { dispatchErrorMessageEvent, renderProgressIndicator } from '../message.js';
import { renderTask } from '../../utils/task.js';
@@ -27,9 +25,6 @@ export class FrigateCardLiveWebRTCCard
extends LitElement
implements FrigateCardMediaPlayer
{
@property({ attribute: false, hasChanged: contentsChanged })
public webRTCConfig?: WebRTCCardConfig;
@property({ attribute: false })
public cameraConfig?: CameraConfig;
@@ -95,23 +90,12 @@ export class FrigateCardLiveWebRTCCard
protected _createWebRTC(): HTMLElement | null {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const webrtcElement = this._webrtcTask.value;
if (webrtcElement && this.hass) {
if (webrtcElement && this.hass && this.cameraConfig?.webrtc_card) {
const webrtc = new webrtcElement() as HTMLElement & {
hass: HomeAssistant;
setConfig: (config: Record<string, unknown>) => void;
};
const config = { ...this.webRTCConfig };
// If the live WebRTC configuration does not specify a URL/entity to use,
// then take values from the camera configuration instead (if there are
// any).
if (!config.url) {
config.url = this.cameraConfig?.webrtc_card?.url;
}
if (!config.entity) {
config.entity = this.cameraConfig?.webrtc_card?.entity;
}
webrtc.setConfig(config);
webrtc.setConfig(this.cameraConfig.webrtc_card);
webrtc.hass = this.hass;
return webrtc;
}
-3
View File
@@ -861,7 +861,6 @@ export class FrigateCardLiveProvider
${ref(this._providerRef)}
.hass=${this.hass}
.cameraConfig=${this.cameraConfig}
.liveImageConfig=${this.liveConfig.image}
@frigate-card:media:loaded=${() => {
if (provider === 'image') {
// Only count the media has loaded if the required provider is
@@ -898,7 +897,6 @@ export class FrigateCardLiveProvider
class=${classMap(providerClasses)}
.hass=${this.hass}
.cameraConfig=${this.cameraConfig}
.webRTCConfig=${this.liveConfig.webrtc_card}
.cardWideConfig=${this.cardWideConfig}
@frigate-card:media:loaded=${this._videoMediaShowHandler.bind(this)}
>
@@ -910,7 +908,6 @@ export class FrigateCardLiveProvider
.hass=${this.hass}
.cameraConfig=${this.cameraConfig}
.cameraEndpoints=${this.cameraEndpoints}
.jsmpegConfig=${this.liveConfig.jsmpeg}
.cardWideConfig=${this.cardWideConfig}
@frigate-card:media:loaded=${this._videoMediaShowHandler.bind(this)}
>
+76 -2
View File
@@ -13,7 +13,6 @@ import {
CONF_LIVE_CONTROLS_THUMBNAILS_SIZE,
CONF_LIVE_LAZY_UNLOAD,
CONF_LIVE_PRELOAD,
CONF_LIVE_WEBRTC_CARD,
CONF_MEDIA_GALLERY,
CONF_MEDIA_VIEWER,
CONF_MENU,
@@ -617,6 +616,80 @@ const transformFrigateUIAction = (data: unknown): boolean => {
return false;
};
/**
* Move live provider options exclusively into camera configs.
* @returns An upgrade function.
*/
const upgradeCameraOptionsFromLiveToMultipleCameras = (): ((
obj: RawFrigateCardConfig,
) => boolean) => {
return function (obj: RawFrigateCardConfig): boolean {
const cameras = getConfigValue(obj, CONF_CAMERAS) as
| RawFrigateCardConfigArray
| undefined;
if (cameras === undefined) {
return false;
}
const webrtcCardConfig = getConfigValue(obj, 'live.webrtc_card') as
| RawFrigateCardConfigArray
| undefined;
const imageConfig = getConfigValue(obj, 'live.image') as
| RawFrigateCardConfigArray
| undefined;
const jsmpegConfig = getConfigValue(obj, 'live.jsmpeg') as
| RawFrigateCardConfigArray
| undefined;
if (!webrtcCardConfig && !imageConfig && !jsmpegConfig) {
return false;
}
if (webrtcCardConfig) {
cameras.forEach((camera) => {
if (
camera.live_provider === 'webrtc_card' &&
(camera.webrtc_card === undefined || typeof camera.webrtc_card === 'object')
) {
camera.webrtc_card = { ...webrtcCardConfig, ...camera.webrtc_card };
}
});
}
if (imageConfig) {
cameras.forEach((camera) => {
if (
camera.live_provider === 'image' &&
(camera.image === undefined || typeof camera.image === 'object')
) {
camera.image = { ...imageConfig, ...camera.image };
}
});
}
if (jsmpegConfig) {
cameras.forEach((camera) => {
if (
camera.live_provider === 'jsmpeg' &&
(camera.jsmpeg === undefined || typeof camera.jsmpeg === 'object')
) {
camera.jsmpeg = { ...jsmpegConfig, ...camera.jsmpeg };
}
});
}
setConfigValue(obj, CONF_CAMERAS, cameras);
deleteConfigValue(obj, 'live.webrtc_card');
deleteConfigValue(obj, 'live.image');
deleteConfigValue(obj, 'live.jsmpeg');
// Note: This upgrade is imperfect. There could be override conditions being
// set that this upgrade cannot understand, e.g. if in fullscreen mode then
// refresh a live image more frequently. Such functionality is not possible
// after this change, since camera configs cannot be overrided.
return true;
};
};
const UPGRADES = [
// v1.2.1 -> v2.0.0
upgradeMoveTo('frigate_url', 'frigate.url'),
@@ -656,7 +729,7 @@ const UPGRADES = [
),
),
upgradeArrayValue(CONF_CAMERAS, upgradeMoveTo('webrtc', 'webrtc_card')),
upgradeMoveToWithOverrides('live.webrtc', CONF_LIVE_WEBRTC_CARD),
upgradeMoveToWithOverrides('live.webrtc', 'live.webrtc_card'),
upgradeMoveToWithOverrides('image.src', CONF_IMAGE_URL),
// v3.0.0 -> v4.0.0-rc.1
@@ -741,4 +814,5 @@ const UPGRADES = [
val === 'frigate-jsmpeg' ? 'jsmpeg' : val,
),
),
upgradeCameraOptionsFromLiveToMultipleCameras(),
];
+4 -4
View File
@@ -13,9 +13,12 @@ export const CONF_CAMERAS_ARRAY_FRIGATE_LABEL =
export const CONF_CAMERAS_ARRAY_FRIGATE_URL = `${CONF_CAMERAS}.#.frigate.url` as const;
export const CONF_CAMERAS_ARRAY_FRIGATE_ZONE = `${CONF_CAMERAS}.#.frigate.zone` as const;
export const CONF_CAMERAS_ARRAY_GO2RTC_MODES = `${CONF_CAMERAS}.#.go2rtc.modes` as const;
export const CONF_CAMERAS_ARRAY_GO2RTC_STREAM = `${CONF_CAMERAS}.#.go2rtc.stream` as const;
export const CONF_CAMERAS_ARRAY_GO2RTC_STREAM =
`${CONF_CAMERAS}.#.go2rtc.stream` as const;
export const CONF_CAMERAS_ARRAY_HIDE = `${CONF_CAMERAS}.#.hide` as const;
export const CONF_CAMERAS_ARRAY_ID = `${CONF_CAMERAS}.#.id` as const;
export const CONF_CAMERAS_ARRAY_IMAGE_REFRESH_SECONDS =
`${CONF_CAMERAS}.#.image.refresh_seconds` as const;
export const CONF_CAMERAS_ARRAY_TITLE = `${CONF_CAMERAS}.#.title` as const;
export const CONF_CAMERAS_ARRAY_ICON = `${CONF_CAMERAS}.#.icon` as const;
export const CONF_CAMERAS_ARRAY_WEBRTC_CARD_ENTITY =
@@ -147,8 +150,6 @@ export const CONF_LIVE_CONTROLS_TIMELINE_WINDOW_SECONDS =
export const CONF_LIVE_CONTROLS_TITLE_MODE = `${CONF_LIVE}.controls.title.mode` as const;
export const CONF_LIVE_CONTROLS_TITLE_DURATION_SECONDS =
`${CONF_LIVE}.controls.title.duration_seconds` as const;
export const CONF_LIVE_IMAGE_REFRESH_SECONDS =
`${CONF_LIVE}.image.refresh_seconds` as const;
export const CONF_LIVE_LAYOUT_FIT = `${CONF_LIVE}.layout.fit` as const;
export const CONF_LIVE_LAYOUT_POSITION_X = `${CONF_LIVE}.layout.position.x` as const;
export const CONF_LIVE_LAYOUT_POSITION_Y = `${CONF_LIVE}.layout.position.y` as const;
@@ -159,7 +160,6 @@ export const CONF_LIVE_PRELOAD = `${CONF_LIVE}.preload` as const;
export const CONF_LIVE_TRANSITION_EFFECT = `${CONF_LIVE}.transition_effect` as const;
export const CONF_LIVE_SHOW_IMAGE_DURING_LOAD =
`${CONF_LIVE}.show_image_during_load` as const;
export const CONF_LIVE_WEBRTC_CARD = `${CONF_LIVE}.webrtc_card` as const;
const CONF_IMAGE = 'image' as const;
export const CONF_IMAGE_LAYOUT_FIT = `${CONF_IMAGE}.layout.fit` as const;
+103 -81
View File
@@ -23,6 +23,7 @@ import {
CONF_CAMERAS_ARRAY_FRIGATE_ZONE,
CONF_CAMERAS_ARRAY_ICON,
CONF_CAMERAS_ARRAY_ID,
CONF_CAMERAS_ARRAY_IMAGE_REFRESH_SECONDS,
CONF_CAMERAS_ARRAY_LIVE_PROVIDER,
CONF_CAMERAS_ARRAY_TITLE,
CONF_CAMERAS_ARRAY_TRIGGERS_ENTITIES,
@@ -63,7 +64,6 @@ import {
CONF_LIVE_CONTROLS_TITLE_DURATION_SECONDS,
CONF_LIVE_CONTROLS_TITLE_MODE,
CONF_LIVE_DRAGGABLE,
CONF_LIVE_IMAGE_REFRESH_SECONDS,
CONF_LIVE_LAYOUT_FIT,
CONF_LIVE_LAYOUT_POSITION_X,
CONF_LIVE_LAYOUT_POSITION_Y,
@@ -160,15 +160,17 @@ const MENU_CAMERAS = 'cameras';
const MENU_CAMERAS_DEPENDENCIES = 'cameras.dependencies';
const MENU_CAMERAS_FRIGATE = 'cameras.frigate';
const MENU_CAMERAS_GO2RTC = 'cameras.go2rtc';
const MENU_CAMERAS_IMAGE = 'cameras.image';
const MENU_CAMERAS_TRIGGERS = 'cameras.triggers';
const MENU_CAMERAS_WEBRTC = 'cameras.webrtc';
const MENU_CAMERAS_WEBRTC_CARD = 'cameras.webrtc_card';
const MENU_CAMERAS_LIVE_PROVIDER = 'cameras.live_provider';
const MENU_CAMERAS_ENGINE = 'cameras.engine';
const MENU_IMAGE_LAYOUT = 'image.layout';
const MENU_LIVE_CONTROLS = 'live.controls';
const MENU_LIVE_CONTROLS_NEXT_PREVIOUS = 'live.controls.next_previous';
const MENU_LIVE_CONTROLS_THUMBNAILS = 'live.controls.thumbnails';
const MENU_LIVE_CONTROLS_TIMELINE = 'live.controls.timeline';
const MENU_LIVE_CONTROLS_TITLE = 'live.controls.title';
const MENU_LIVE_IMAGE = 'live.image';
const MENU_LIVE_LAYOUT = 'live.layout';
const MENU_MEDIA_GALLERY_CONTROLS_THUMBNAILS = 'media_gallery.controls.thumbnails';
const MENU_MEDIA_GALLERY_CONTROLS_FILTER = 'media_gallery.controls.filter';
@@ -1157,12 +1159,12 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
* @returns A rendered template.
*/
protected _renderTitleControls(
domain: string,
menuDomain: string,
configPathMode: string,
configPathDurationSeconds: string,
): TemplateResult | void {
return this._putInSubmenu(
domain,
menuDomain,
true,
'config.common.controls.title.editor_label',
{ name: 'mdi:subtitles' },
@@ -1344,40 +1346,94 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
getArrayConfigPath(CONF_CAMERAS_ARRAY_ID, cameraIndex),
)}
${this._renderSwitch(
getArrayConfigPath(
CONF_CAMERAS_ARRAY_HIDE,
cameraIndex,
),
getArrayConfigPath(CONF_CAMERAS_ARRAY_HIDE, cameraIndex),
this._defaults.cameras.hide,
)}
${this._putInSubmenu(
MENU_CAMERAS_FRIGATE,
cameraIndex,
'config.cameras.frigate.editor_label',
{ path: FRIGATE_ICON_SVG_PATH },
html`
MENU_CAMERAS_ENGINE,
true,
'config.cameras.engines.editor_label',
{ name: 'mdi:engine' },
html`${this._putInSubmenu(
MENU_CAMERAS_FRIGATE,
cameraIndex,
'config.cameras.frigate.editor_label',
{ path: FRIGATE_ICON_SVG_PATH },
html`
${this._renderStringInput(
getArrayConfigPath(
CONF_CAMERAS_ARRAY_FRIGATE_CAMERA_NAME,
cameraIndex,
),
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_FRIGATE_URL, cameraIndex),
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_FRIGATE_LABEL, cameraIndex),
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_FRIGATE_ZONE, cameraIndex),
)}
${this._renderStringInput(
getArrayConfigPath(
CONF_CAMERAS_ARRAY_FRIGATE_CLIENT_ID,
cameraIndex,
),
)}
`,
)}`,
)}
${this._putInSubmenu(
MENU_CAMERAS_LIVE_PROVIDER,
true,
'config.cameras.live_provider_options.editor_label',
{ name: 'mdi:cctv' },
html` ${this._putInSubmenu(
MENU_CAMERAS_GO2RTC,
cameraIndex,
'config.cameras.go2rtc.editor_label',
{ name: 'mdi:alpha-g-circle' },
html`${this._renderOptionSelector(
getArrayConfigPath(CONF_CAMERAS_ARRAY_GO2RTC_MODES, cameraIndex),
this._go2rtcModes,
{
multiple: true,
label: localize('config.cameras.go2rtc.modes.editor_label'),
},
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_GO2RTC_STREAM, cameraIndex),
)}`,
)}
${this._putInSubmenu(
MENU_CAMERAS_IMAGE,
true,
'config.cameras.image.editor_label',
{ name: 'mdi:image' },
html` ${this._renderNumberInput(
getArrayConfigPath(
CONF_CAMERAS_ARRAY_FRIGATE_CAMERA_NAME,
CONF_CAMERAS_ARRAY_IMAGE_REFRESH_SECONDS,
cameraIndex,
),
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_FRIGATE_URL, cameraIndex),
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_FRIGATE_LABEL, cameraIndex),
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_FRIGATE_ZONE, cameraIndex),
)}
${this._renderStringInput(
)}`,
)}
${this._putInSubmenu(
MENU_CAMERAS_WEBRTC_CARD,
cameraIndex,
'config.cameras.webrtc_card.editor_label',
{ name: 'mdi:webrtc' },
html`${this._renderEntitySelector(
getArrayConfigPath(
CONF_CAMERAS_ARRAY_FRIGATE_CLIENT_ID,
CONF_CAMERAS_ARRAY_WEBRTC_CARD_ENTITY,
cameraIndex,
),
'camera',
)}
`,
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_WEBRTC_CARD_URL, cameraIndex),
)}`,
)}`,
)}
${this._putInSubmenu(
MENU_CAMERAS_DEPENDENCIES,
@@ -1423,36 +1479,6 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
},
)}`,
)}
${this._putInSubmenu(
MENU_CAMERAS_GO2RTC,
cameraIndex,
'config.cameras.go2rtc.editor_label',
{ name: 'mdi:alpha-g-circle' },
html`${this._renderOptionSelector(
getArrayConfigPath(CONF_CAMERAS_ARRAY_GO2RTC_MODES, cameraIndex),
this._go2rtcModes,
{
multiple: true,
label: localize('config.cameras.go2rtc.modes.editor_label')
}
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_GO2RTC_STREAM, cameraIndex),
)}`,
)}
${this._putInSubmenu(
MENU_CAMERAS_WEBRTC,
cameraIndex,
'config.cameras.webrtc_card.editor_label',
{ name: 'mdi:webrtc' },
html`${this._renderEntitySelector(
getArrayConfigPath(CONF_CAMERAS_ARRAY_WEBRTC_CARD_ENTITY, cameraIndex),
'camera',
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_WEBRTC_CARD_URL, cameraIndex),
)}`,
)}
</div>`
: ``}
</div>
@@ -1467,20 +1493,23 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
*/
protected _renderStringInput(
configPath: string,
type?:
| 'number'
| 'text'
| 'search'
| 'tel'
| 'url'
| 'email'
| 'password'
| 'date'
| 'month'
| 'week'
| 'time'
| 'datetime-local'
| 'color',
params?: {
label?: string;
type?:
| 'number'
| 'text'
| 'search'
| 'tel'
| 'url'
| 'email'
| 'password'
| 'date'
| 'month'
| 'week'
| 'time'
| 'datetime-local'
| 'color';
},
): TemplateResult | void {
if (!this._config) {
return;
@@ -1489,8 +1518,8 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
return html`
<ha-selector
.hass=${this.hass}
.selector=${{ text: { type: type || 'text' } }}
.label=${this._getLabel(configPath)}
.selector=${{ text: { type: params?.type || 'text' } }}
.label=${params?.label ?? this._getLabel(configPath)}
.value=${getConfigValue(this._config, configPath, '')}
.required=${false}
@value-changed=${(ev) => this._valueChangedHandler(configPath, ev)}
@@ -1711,13 +1740,6 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
CONF_LIVE_LAYOUT_POSITION_X,
CONF_LIVE_LAYOUT_POSITION_Y,
)}
${this._putInSubmenu(
MENU_LIVE_IMAGE,
true,
'config.live.image.editor_label',
{ name: 'mdi:image-sync-outline' },
html` ${this._renderNumberInput(CONF_LIVE_IMAGE_REFRESH_SECONDS)} `,
)}
</div>
`
: ''}
+10 -4
View File
@@ -15,6 +15,9 @@
"cameras": "Show events for specific cameras with this camera",
"editor_label": "Dependency Options"
},
"engines": {
"editor_label": "Camera engine options"
},
"frigate": {
"camera_name": "Frigate camera name (Autodetected from entity)",
"client_id": "Frigate client id (For >1 Frigate server)",
@@ -36,8 +39,15 @@
},
"hide": "Hide camera from UI",
"icon": "Icon for this camera (Autodetected from entity)",
"image": {
"editor_label": "Image Options",
"refresh_seconds": "Number of seconds after which to refresh live image (0=never)"
},
"id": "Unique id for this camera in this card",
"live_provider": "Live view provider for this camera",
"live_provider_options": {
"editor_label": "Live provider options"
},
"live_providers": {
"auto": "Automatic",
"jsmpeg": "JSMpeg",
@@ -184,10 +194,6 @@
"editor_label": "Live Controls"
},
"draggable": "Live cameras view can be dragged/swiped",
"image": {
"editor_label": "Image Live Provider Options",
"refresh_seconds": "Number of seconds after which to refresh live image (0=never)"
},
"layout": "Live Layout",
"lazy_load": "Live cameras are lazily loaded",
"lazy_unload": "Live cameras are lazily unloaded",
+10
View File
@@ -15,6 +15,9 @@
"cameras": "Mostra eventi per telecamere specifiche con questa telecamera",
"editor_label": "Opzioni di dipendenza"
},
"engines": {
"editor_label": ""
},
"frigate": {
"camera_name": "Nome della telecamera frigate (autodificato dall'entità)",
"client_id": "ID client Frigate (per > 1 Frigate server)",
@@ -36,8 +39,15 @@
},
"hide": "",
"icon": "Icona per questa telecamera (Autoidentificato dall'entità)",
"image": {
"editor_label": "",
"refresh_seconds": ""
},
"id": "ID univoco per questa telecamera in questa carta",
"live_provider": "Provider di visualizzazione dal vivo per questa telecamera",
"live_provider_options": {
"editor_label": ""
},
"live_providers": {
"auto": "Automatica",
"jsmpeg": "JSMpeg",
+10
View File
@@ -15,6 +15,9 @@
"cameras": "Mostrar eventos para câmeras específicas nesta câmera",
"editor_label": "Opções de dependência"
},
"engines": {
"editor_label": ""
},
"frigate": {
"camera_name": "Nome da câmera do Frigate (detectado automaticamente pela entidade)",
"client_id": "ID do cliente do Frigate (para >1 servidor Frigate)",
@@ -36,8 +39,15 @@
},
"hide": "",
"icon": "Ícone para esta câmera (detectado automaticamente pela entidade)",
"image": {
"editor_label": "",
"refresh_seconds": ""
},
"id": "ID exclusivo para esta câmera nesse cartão",
"live_provider": "Provedor de visualização ao vivo para esta câmera",
"live_provider_options": {
"editor_label": ""
},
"live_providers": {
"auto": "Automatico",
"jsmpeg": "JSMpeg",
+5 -5
View File
@@ -7,6 +7,7 @@ import {
import { getArrayConfigPath, getConfigValue, setConfigValue } from './config-mgmt.js';
import {
CONF_CAMERAS_ARRAY_TRIGGERS_OCCUPANCY,
CONF_CAMERAS_ARRAY_IMAGE_REFRESH_SECONDS,
CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_DETAILS,
CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
CONF_MEDIA_GALLERY_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
@@ -18,7 +19,6 @@ import {
CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS,
CONF_LIVE_CONTROLS_TITLE_MODE,
CONF_LIVE_DRAGGABLE,
CONF_LIVE_IMAGE_REFRESH_SECONDS,
CONF_LIVE_LAZY_UNLOAD,
CONF_LIVE_SHOW_IMAGE_DURING_LOAD,
CONF_LIVE_TRANSITION_EFFECT,
@@ -116,10 +116,6 @@ const LOW_PROFILE_DEFAULTS = {
[CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL]: false,
[CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_DETAILS]: false,
// Refresh the live camera image every 10 seconds (same as stock Home
// Assistant Picture Glance).
[CONF_LIVE_IMAGE_REFRESH_SECONDS]: 10,
// Disable all optional performance related features.
[CONF_PERFORMANCE_FEATURES_ANIMATED_PROGRESS_INDICATOR]: false,
@@ -136,6 +132,10 @@ const LOW_PROFILE_DEFAULTS = {
const LOW_PROFILE_CAMERA_DEFAULTS = {
[CONF_CAMERAS_ARRAY_TRIGGERS_OCCUPANCY]: false,
// Refresh the live camera image every 10 seconds (same as stock Home
// Assistant Picture Glance).
[CONF_CAMERAS_ARRAY_IMAGE_REFRESH_SECONDS]: 10,
};
/**
+69 -67
View File
@@ -392,6 +392,51 @@ const customSchema = z
})
.passthrough();
/**
* Live provider options
*/
const go2rtcConfigSchema = z.object({
modes: z.enum(['webrtc', 'mse', 'mp4', 'mjpeg']).array().optional(),
stream: z.string().optional(),
});
export type Go2rtcConfig = z.infer<typeof go2rtcConfigSchema>;
const liveImageConfigSchema = z.object({
refresh_seconds: z.number().min(0).default(1),
});
export type LiveImageConfig = z.infer<typeof liveImageConfigSchema>;
const webrtcCardConfigSchema = z
.object({
entity: z.string().optional(),
url: z.string().optional(),
})
.passthrough();
export type WebRTCCardConfig = z.infer<typeof webrtcCardConfigSchema>;
const jsmpegConfigSchema = z
.object({
options: z
.object({
// https://github.com/phoboslab/jsmpeg#usage
audio: z.boolean().optional(),
video: z.boolean().optional(),
pauseWhenHidden: z.boolean().optional(),
disableGl: z.boolean().optional(),
disableWebAssembly: z.boolean().optional(),
preserveDrawingBuffer: z.boolean().optional(),
progressive: z.boolean().optional(),
throttled: z.boolean().optional(),
chunkSize: z.number().optional(),
maxAudioLag: z.number().optional(),
videoBufferSize: z.number().optional(),
audioBufferSize: z.number().optional(),
})
.optional(),
});
export type JSMPEGConfig = z.infer<typeof jsmpegConfigSchema>;
/**
* Camera configuration section
*/
@@ -405,6 +450,9 @@ const cameraConfigDefault = {
all_cameras: false,
cameras: [],
},
image: {
refresh_seconds: 1,
},
hide: false,
triggers: {
motion: false,
@@ -412,14 +460,10 @@ const cameraConfigDefault = {
entities: [],
},
};
const webrtcCardCameraConfigSchema = z.object({
entity: z.string().optional(),
url: z.string().optional(),
});
const cameraConfigSchema = z
.object({
camera_entity: z.string().optional(),
live_provider: z.enum(LIVE_PROVIDERS).default(cameraConfigDefault.live_provider),
// Used for presentation in the UI (autodetected from the entity if
// specified).
@@ -433,29 +477,6 @@ const cameraConfigSchema = z
// this card.
id: z.string().optional(),
engine: z.enum(ENGINES).default('auto'),
frigate: z
.object({
// No URL validation to allow relative URLs within HA (e.g. Frigate addon).
url: z.string().optional(),
client_id: z.string().default(cameraConfigDefault.frigate.client_id),
camera_name: z.string().optional(),
label: z.string().optional(),
zone: z.string().optional(),
})
.default(cameraConfigDefault.frigate),
go2rtc: z
.object({
modes: z.enum(['webrtc', 'mse', 'mp4', 'mjpeg']).array().optional(),
stream: z.string().optional(),
})
.optional(),
// Camera identifiers for WebRTC.
webrtc_card: webrtcCardCameraConfigSchema.optional(),
dependencies: z
.object({
all_cameras: z.boolean().default(cameraConfigDefault.dependencies.all_cameras),
@@ -470,6 +491,26 @@ const cameraConfigSchema = z
entities: z.string().array().default(cameraConfigDefault.triggers.entities),
})
.default(cameraConfigDefault.triggers),
// Engine options.
engine: z.enum(ENGINES).default('auto'),
frigate: z
.object({
// No URL validation to allow relative URLs within HA (e.g. Frigate addon).
url: z.string().optional(),
client_id: z.string().default(cameraConfigDefault.frigate.client_id),
camera_name: z.string().optional(),
label: z.string().optional(),
zone: z.string().optional(),
})
.default(cameraConfigDefault.frigate),
// Live provider options.
live_provider: z.enum(LIVE_PROVIDERS).default(cameraConfigDefault.live_provider),
go2rtc: go2rtcConfigSchema.optional(),
image: liveImageConfigSchema.default(cameraConfigDefault.image),
jsmpeg: jsmpegConfigSchema.optional(),
webrtc_card: webrtcCardConfigSchema.optional(),
})
.default(cameraConfigDefault);
export type CameraConfig = z.infer<typeof cameraConfigSchema>;
@@ -795,10 +836,6 @@ export type TitleControlConfig = z.infer<typeof titleControlConfigSchema>;
* Live view configuration section.
*/
const liveImageConfigDefault = {
refresh_seconds: 1,
};
const liveThumbnailControlsDefaults = {
...thumbnailControlsDefaults,
media: 'all' as const,
@@ -815,7 +852,6 @@ const liveConfigDefault = {
draggable: true,
transition_effect: 'slide' as const,
show_image_during_load: true,
image: liveImageConfigDefault,
controls: {
next_previous: {
size: 48,
@@ -836,42 +872,8 @@ const livethumbnailsControlSchema = thumbnailsControlSchema.extend({
.default(liveConfigDefault.controls.thumbnails.media),
});
const liveImageConfigSchema = z.object({
refresh_seconds: z.number().min(0).default(liveConfigDefault.image.refresh_seconds),
});
export type LiveImageConfig = z.infer<typeof liveImageConfigSchema>;
const webrtcCardConfigSchema = webrtcCardCameraConfigSchema.passthrough().optional();
export type WebRTCCardConfig = z.infer<typeof webrtcCardConfigSchema>;
const jsmpegConfigSchema = z
.object({
options: z
.object({
// https://github.com/phoboslab/jsmpeg#usage
audio: z.boolean().optional(),
video: z.boolean().optional(),
pauseWhenHidden: z.boolean().optional(),
disableGl: z.boolean().optional(),
disableWebAssembly: z.boolean().optional(),
preserveDrawingBuffer: z.boolean().optional(),
progressive: z.boolean().optional(),
throttled: z.boolean().optional(),
chunkSize: z.number().optional(),
maxAudioLag: z.number().optional(),
videoBufferSize: z.number().optional(),
audioBufferSize: z.number().optional(),
})
.optional(),
})
.optional();
export type JSMPEGConfig = z.infer<typeof jsmpegConfigSchema>;
const liveOverridableConfigSchema = z
.object({
image: liveImageConfigSchema.default(liveConfigDefault.image),
jsmpeg: jsmpegConfigSchema,
webrtc_card: webrtcCardConfigSchema,
controls: z
.object({
next_previous: nextPreviousControlConfigSchema