Add camera specific webrtc configuration.
This commit is contained in:
+32
-19
@@ -1,3 +1,4 @@
|
|||||||
|
// TODO double media load event for webrtc
|
||||||
// TODO webrtc entities in camera section?
|
// TODO webrtc entities in camera section?
|
||||||
// TODO conditional elements based on camera name (requires event changed to propagate upwards)
|
// TODO conditional elements based on camera name (requires event changed to propagate upwards)
|
||||||
// TODO Remove media load event warning
|
// TODO Remove media load event warning
|
||||||
@@ -5,7 +6,7 @@
|
|||||||
// TODO search for TODOs
|
// TODO search for TODOs
|
||||||
|
|
||||||
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
||||||
import type {
|
import {
|
||||||
BrowseMediaSource,
|
BrowseMediaSource,
|
||||||
ExtendedHomeAssistant,
|
ExtendedHomeAssistant,
|
||||||
CameraConfig,
|
CameraConfig,
|
||||||
@@ -14,6 +15,7 @@ import type {
|
|||||||
MediaShowInfo,
|
MediaShowInfo,
|
||||||
WebRTCConfig,
|
WebRTCConfig,
|
||||||
StateParameters,
|
StateParameters,
|
||||||
|
FrigateCardError,
|
||||||
} from '../types.js';
|
} from '../types.js';
|
||||||
import { EmblaOptionsType } from 'embla-carousel';
|
import { EmblaOptionsType } from 'embla-carousel';
|
||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from 'custom-card-helpers';
|
||||||
@@ -420,6 +422,7 @@ export class FrigateCardLiveProvider extends LitElement {
|
|||||||
: this.liveConfig.provider == 'webrtc'
|
: this.liveConfig.provider == 'webrtc'
|
||||||
? html`<frigate-card-live-webrtc
|
? html`<frigate-card-live-webrtc
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.cameraConfig=${this.cameraConfig}
|
||||||
.webRTCConfig=${this.liveConfig.webrtc || {}}
|
.webRTCConfig=${this.liveConfig.webrtc || {}}
|
||||||
>
|
>
|
||||||
</frigate-card-live-webrtc>`
|
</frigate-card-live-webrtc>`
|
||||||
@@ -482,23 +485,35 @@ export class FrigateCardLiveWebRTC extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected webRTCConfig?: WebRTCConfig;
|
protected webRTCConfig?: WebRTCConfig;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
protected cameraConfig?: CameraConfig;
|
||||||
|
|
||||||
protected hass?: HomeAssistant & ExtendedHomeAssistant;
|
protected hass?: HomeAssistant & ExtendedHomeAssistant;
|
||||||
protected _webRTCElement: HTMLElement | null = null;
|
|
||||||
protected _callbacksAdded = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the WebRTC element. May throw.
|
* Create the WebRTC element. May throw.
|
||||||
*/
|
*/
|
||||||
protected _createWebRTC(): TemplateResult | void {
|
protected _createWebRTC(): HTMLElement | undefined {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const webrtcElement = customElements.get('webrtc-camera') as any;
|
const webrtcElement = customElements.get('webrtc-camera') as any;
|
||||||
if (webrtcElement) {
|
if (webrtcElement) {
|
||||||
const webrtc = new webrtcElement();
|
const webrtc = new webrtcElement();
|
||||||
webrtc.setConfig(this.webRTCConfig);
|
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?.url;
|
||||||
|
}
|
||||||
|
if (!config.entity) {
|
||||||
|
config.entity = this.cameraConfig?.webrtc?.entity;
|
||||||
|
}
|
||||||
|
webrtc.setConfig(config);
|
||||||
webrtc.hass = this.hass;
|
webrtc.hass = this.hass;
|
||||||
this._webRTCElement = webrtc;
|
return webrtc;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(localize('error.missing_webrtc'));
|
throw new FrigateCardError(localize('error.webrtc_missing'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,24 +525,23 @@ export class FrigateCardLiveWebRTC extends LitElement {
|
|||||||
if (!this.hass) {
|
if (!this.hass) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this._webRTCElement) {
|
let webrtcElement: HTMLElement | undefined;
|
||||||
try {
|
try {
|
||||||
this._createWebRTC();
|
webrtcElement = this._createWebRTC();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return dispatchErrorMessageEvent(this, (e as Error).message);
|
return dispatchErrorMessageEvent(
|
||||||
}
|
this,
|
||||||
|
e instanceof FrigateCardError
|
||||||
|
? (e as FrigateCardError).message
|
||||||
|
: localize('error.webrtc_reported_error') + ': ' + (e as Error).message);
|
||||||
}
|
}
|
||||||
return html`${this._webRTCElement}`;
|
return html`${webrtcElement}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updated lifecycle callback.
|
* Updated lifecycle callback.
|
||||||
*/
|
*/
|
||||||
public updated(): void {
|
public updated(): void {
|
||||||
if (this._callbacksAdded) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the video component after it has been rendered and generate the
|
// Extract the video component after it has been rendered and generate the
|
||||||
// media load event.
|
// media load event.
|
||||||
this.updateComplete.then(() => {
|
this.updateComplete.then(() => {
|
||||||
@@ -555,7 +569,6 @@ export class FrigateCardLiveWebRTC extends LitElement {
|
|||||||
}
|
}
|
||||||
dispatchPauseEvent(this);
|
dispatchPauseEvent(this);
|
||||||
};
|
};
|
||||||
this._callbacksAdded = true;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,7 +158,8 @@
|
|||||||
"invalid_configuration": "Invalid configuration",
|
"invalid_configuration": "Invalid configuration",
|
||||||
"invalid_configuration_no_hint": "No location hint available (bad or missing type?)",
|
"invalid_configuration_no_hint": "No location hint available (bad or missing type?)",
|
||||||
"upgrade_available": "An automated card configuration upgrade is available, please visit the visual card editor",
|
"upgrade_available": "An automated card configuration upgrade is available, please visit the visual card editor",
|
||||||
"missing_webrtc": "WebRTC component not found",
|
"webrtc_missing": "WebRTC component not found",
|
||||||
|
"webrtc_reported_error": "WebRTC component reported an error",
|
||||||
"no_cameras": "No valid cameras found, you must configure at least one camera with either a camera_entity or camera_name",
|
"no_cameras": "No valid cameras found, you must configure at least one camera with either a camera_entity or camera_name",
|
||||||
"duplicate_camera_id": "Duplicate Frigate camera, use the 'id' parameter to uniquely identify cameras with the same 'camera_entity' or 'camera_name'",
|
"duplicate_camera_id": "Duplicate Frigate camera, use the 'id' parameter to uniquely identify cameras with the same 'camera_entity' or 'camera_name'",
|
||||||
"could_not_render_elements": "Could not render picture elements",
|
"could_not_render_elements": "Could not render picture elements",
|
||||||
|
|||||||
+13
-8
@@ -59,6 +59,8 @@ const FRIGATE_MENU_MODES = [
|
|||||||
] as const;
|
] as const;
|
||||||
const LIVE_PROVIDERS = ['frigate', 'frigate-jsmpeg', 'webrtc'] as const;
|
const LIVE_PROVIDERS = ['frigate', 'frigate-jsmpeg', 'webrtc'] as const;
|
||||||
|
|
||||||
|
export class FrigateCardError extends Error {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action Types (for "Picture Elements" / Menu)
|
* Action Types (for "Picture Elements" / Menu)
|
||||||
*/
|
*/
|
||||||
@@ -349,11 +351,16 @@ const pictureElementsSchema = pictureElementSchema.array().optional();
|
|||||||
export type PictureElements = z.infer<typeof pictureElementsSchema>;
|
export type PictureElements = z.infer<typeof pictureElementsSchema>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Frigate configuration section.
|
* Camera configuration section
|
||||||
*/
|
*/
|
||||||
export const cameraConfigDefault = {
|
export const cameraConfigDefault = {
|
||||||
client_id: 'frigate' as const,
|
client_id: 'frigate' as const,
|
||||||
};
|
};
|
||||||
|
const webrtcCameraConfigSchema = z
|
||||||
|
.object({
|
||||||
|
entity: z.string().optional(),
|
||||||
|
url: z.string().optional(),
|
||||||
|
});
|
||||||
const cameraConfigDefaultSchema = z
|
const cameraConfigDefaultSchema = z
|
||||||
.object({
|
.object({
|
||||||
// No URL validation to allow relative URLs within HA (e.g. Frigate addon).
|
// No URL validation to allow relative URLs within HA (e.g. Frigate addon).
|
||||||
@@ -372,6 +379,9 @@ const cameraConfigDefaultSchema = z
|
|||||||
// Optional identifier to separate different camera configurations used in
|
// Optional identifier to separate different camera configurations used in
|
||||||
// this card.
|
// this card.
|
||||||
id: z.string().optional(),
|
id: z.string().optional(),
|
||||||
|
|
||||||
|
// Camera identifiers for WebRTC.
|
||||||
|
webrtc: webrtcCameraConfigSchema.optional(),
|
||||||
})
|
})
|
||||||
.default(cameraConfigDefault);
|
.default(cameraConfigDefault);
|
||||||
export type CameraConfig = z.infer<typeof cameraConfigDefaultSchema>;
|
export type CameraConfig = z.infer<typeof cameraConfigDefaultSchema>;
|
||||||
@@ -459,13 +469,8 @@ const liveConfigDefault = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const webrtcConfigSchema = z
|
|
||||||
.object({
|
const webrtcConfigSchema = webrtcCameraConfigSchema.passthrough().optional();
|
||||||
entity: z.string().optional(),
|
|
||||||
url: z.string().optional(),
|
|
||||||
})
|
|
||||||
.passthrough()
|
|
||||||
.optional();
|
|
||||||
export type WebRTCConfig = z.infer<typeof webrtcConfigSchema>;
|
export type WebRTCConfig = z.infer<typeof webrtcConfigSchema>;
|
||||||
|
|
||||||
const jsmpegConfigSchema = z
|
const jsmpegConfigSchema = z
|
||||||
|
|||||||
Reference in New Issue
Block a user