Allow the user to specify image URL for image live provider.
This commit is contained in:
@@ -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.
|
||||
}}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user