Move all live provider options into cameras.
This commit is contained in:
@@ -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.
|
||||
}}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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>
|
||||
`
|
||||
: ''}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user