Allow the user to specify image URL for image live provider.

This commit is contained in:
Dermot Duffy
2023-02-26 19:47:33 -08:00
parent 5e927a23b0
commit 6d2d31f709
9 changed files with 66 additions and 46 deletions
+2 -1
View File
@@ -51,8 +51,9 @@ export class FrigateCardLiveImage extends LitElement implements FrigateCardMedia
return html` <frigate-card-image
.imageConfig=${{
mode: 'camera' as const,
mode: this.cameraConfig.image.url ? 'url' as const : 'camera' as const,
refresh_seconds: this._playing ? this.cameraConfig.image.refresh_seconds : 0,
url: this.cameraConfig.image.url,
// Don't need to pass layout options as FrigateCardLiveProvider has
// already taken care of this for us.
}}
+8 -6
View File
@@ -723,12 +723,14 @@ export class FrigateCardLiveProvider
// If the play call fails, and the media is not already muted, mute it first
// and then try again. This works around some browsers that prevent
// auto-play unless the video is muted.
this._providerRef.value?.play().catch((ev) => {
if (ev.name === 'NotAllowedError' && !this.isMuted()) {
this.mute();
this._providerRef.value?.play().catch();
}
});
if (this._providerRef.value?.play) {
this._providerRef.value?.play().catch((ev) => {
if (ev.name === 'NotAllowedError' && !this.isMuted()) {
this.mute();
this._providerRef.value?.play().catch();
}
});
}
}
public pause(): void {
+1
View File
@@ -19,6 +19,7 @@ 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_IMAGE_URL = `${CONF_CAMERAS}.#.image.url` 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 =
+12 -6
View File
@@ -132,6 +132,7 @@ import {
CONF_CAMERAS_ARRAY_GO2RTC_MODES,
CONF_CAMERAS_ARRAY_GO2RTC_STREAM,
CONF_CAMERAS_ARRAY_HIDE,
CONF_CAMERAS_ARRAY_IMAGE_URL,
} from './const.js';
import { localize } from './localize/localize.js';
import frigate_card_editor_style from './scss/editor.scss';
@@ -1411,12 +1412,17 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
true,
'config.cameras.image.editor_label',
{ name: 'mdi:image' },
html` ${this._renderNumberInput(
getArrayConfigPath(
CONF_CAMERAS_ARRAY_IMAGE_REFRESH_SECONDS,
cameraIndex,
),
)}`,
html`
${this._renderNumberInput(
getArrayConfigPath(
CONF_CAMERAS_ARRAY_IMAGE_REFRESH_SECONDS,
cameraIndex,
),
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_IMAGE_URL, cameraIndex),
)}
`,
)}
${this._putInSubmenu(
MENU_CAMERAS_WEBRTC_CARD,
+2 -1
View File
@@ -41,7 +41,8 @@
"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)"
"refresh_seconds": "Number of seconds after which to refresh live image (0=never)",
"url": "Image URL to use instead of camera entity snapshot"
},
"id": "Unique id for this camera in this card",
"live_provider": "Live view provider for this camera",
+2 -1
View File
@@ -41,7 +41,8 @@
"icon": "Icona per questa telecamera (Autoidentificato dall'entità)",
"image": {
"editor_label": "",
"refresh_seconds": ""
"refresh_seconds": "",
"url": ""
},
"id": "ID univoco per questa telecamera in questa carta",
"live_provider": "Provider di visualizzazione dal vivo per questa telecamera",
+2 -1
View File
@@ -41,7 +41,8 @@
"icon": "Ícone para esta câmera (detectado automaticamente pela entidade)",
"image": {
"editor_label": "",
"refresh_seconds": ""
"refresh_seconds": "",
"url": ""
},
"id": "ID exclusivo para esta câmera nesse cartão",
"live_provider": "Provedor de visualização ao vivo para esta câmera",
+35 -28
View File
@@ -392,6 +392,18 @@ const customSchema = z
})
.passthrough();
/**
* Image config base options. Image config schema base is used both for the
* `image` live provider and the `image` card view.
*/
const imageBaseConfigDefault = {
refresh_seconds: 1,
};
const imageBaseConfigSchema = z.object({
url: z.string().optional(),
refresh_seconds: z.number().min(0).default(imageBaseConfigDefault.refresh_seconds),
});
/**
* Live provider options
*/
@@ -402,9 +414,7 @@ const go2rtcConfigSchema = z.object({
});
export type Go2rtcConfig = z.infer<typeof go2rtcConfigSchema>;
const liveImageConfigSchema = z.object({
refresh_seconds: z.number().min(0).default(1),
});
const liveImageConfigSchema = imageBaseConfigSchema;
export type LiveImageConfig = z.infer<typeof liveImageConfigSchema>;
const webrtcCardConfigSchema = z
@@ -415,26 +425,25 @@ const webrtcCardConfigSchema = z
.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(),
});
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>;
/**
@@ -718,13 +727,11 @@ const viewConfigSchema = z
const IMAGE_MODES = ['screensaver', 'camera', 'url'] as const;
const imageConfigDefault = {
mode: 'url' as const,
refresh_seconds: 0,
...imageBaseConfigDefault,
};
const imageConfigSchema = z
.object({
const imageConfigSchema = imageBaseConfigSchema
.extend({
mode: z.enum(IMAGE_MODES).default(imageConfigDefault.mode),
url: z.string().optional(),
refresh_seconds: z.number().min(0).default(imageConfigDefault.refresh_seconds),
layout: mediaLayoutConfigSchema.optional(),
})
.merge(actionsSchema)