Improve handling of non-Frigate cameras.
This commit is contained in:
@@ -96,7 +96,7 @@ cameras:
|
||||
| `title` | Autodetected from `camera_entity` if that is specified. | :heavy_multiplication_x: | A friendly name for this camera to use in the card. |
|
||||
| `icon` | Autodetected from `camera_entity` if that is specified. | :heavy_multiplication_x: | The icon to use for this camera in the camera menu and in the next & previous controls when using the `icon` style. |
|
||||
| `webrtc` | | :heavy_multiplication_x: | The WebRTC entity/URL to use for this camera. See below. |
|
||||
| `id` | `camera_entity`, or `camera_name` if set (in that preference order). | :heavy_multiplication_x: | An optional identifier to use throughout the card configuration to refer unambiguously to this camera. See [camera IDs](#camera-ids). |
|
||||
| `id` | `camera_entity`, `webrtc.entity` or `camera_name` if set (in that preference order). | :heavy_multiplication_x: | An optional identifier to use throughout the card configuration to refer unambiguously to this camera. See [camera IDs](#camera-ids). |
|
||||
|
||||
#### Camera WebRTC configuration
|
||||
|
||||
@@ -118,7 +118,7 @@ See [Using WebRTC](#webrtc) below for more details on how to use WebRTC with thi
|
||||
|
||||
#### Camera IDs: Refering to cameras in card configuration
|
||||
|
||||
Each camera configured in the card has a single identifier (`id`). For a given camera, this will be one of the camera {`id`, `camera_entity` or `camera_name`} parameters for that camera -- in that order of precedence. These ids may be used in conditions or custom actions to refer to a given camera unambiguously. |
|
||||
Each camera configured in the card has a single identifier (`id`). For a given camera, this will be one of the camera {`id`, `camera_entity`, `webrtc.entity` or `camera_name`} parameters for that camera -- in that order of precedence. These ids may be used in conditions or custom actions to refer to a given camera unambiguously. |
|
||||
|
||||
#### Example
|
||||
|
||||
|
||||
@@ -147,13 +147,25 @@ export class BrowseMediaUtil {
|
||||
* Get the parameters to search for media related to the current view.
|
||||
* @returns A BrowseMediaQueryParameters object.
|
||||
*/
|
||||
static getBrowseMediaQueryParametersFromView(
|
||||
static getBrowseMediaQueryParametersOrDispatchError(
|
||||
node: HTMLElement,
|
||||
view: View,
|
||||
cameraConfig: CameraConfig,
|
||||
): BrowseMediaQueryParameters | undefined {
|
||||
if (!view.isClipRelatedView() && !view.isSnapshotRelatedView()) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Verify there is a camera name, otherwise getBrowseMediaQueryParameters()
|
||||
// will return undefined.
|
||||
if (!cameraConfig.camera_name) {
|
||||
dispatchErrorMessageEvent(
|
||||
node,
|
||||
localize('error.no_camera_name') + `: ${JSON.stringify(cameraConfig)}`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return BrowseMediaUtil.getBrowseMediaQueryParameters(
|
||||
view.isClipRelatedView() ? 'clips' : 'snapshots',
|
||||
cameraConfig,
|
||||
|
||||
+26
-26
@@ -432,17 +432,23 @@ export class FrigateCard extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
if (config.camera_name) {
|
||||
const id = config.id || config.camera_entity || config.camera_name;
|
||||
if (cameras.has(id)) {
|
||||
this._setMessageAndUpdate({
|
||||
message: localize('error.duplicate_camera_id'),
|
||||
type: 'error',
|
||||
});
|
||||
errorFree = false;
|
||||
} else {
|
||||
cameras.set(id, config);
|
||||
}
|
||||
const id =
|
||||
config.id || config.camera_entity || config.webrtc?.entity || config.camera_name;
|
||||
|
||||
if (!id) {
|
||||
this._setMessageAndUpdate({
|
||||
message: localize('error.no_camera_id') + `: ${JSON.stringify(config)}`,
|
||||
type: 'error',
|
||||
});
|
||||
errorFree = false;
|
||||
} else if (cameras.has(id)) {
|
||||
this._setMessageAndUpdate({
|
||||
message: localize('error.duplicate_camera_id') + `: ${JSON.stringify(config)}`,
|
||||
type: 'error',
|
||||
});
|
||||
errorFree = false;
|
||||
} else {
|
||||
cameras.set(id, config);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -506,11 +512,6 @@ export class FrigateCard extends LitElement {
|
||||
// Pass.
|
||||
}
|
||||
|
||||
// Fallback: Guess from the entity_id.
|
||||
if (entity.includes('.')) {
|
||||
return entity.split('.', 2)[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -842,6 +843,9 @@ export class FrigateCard extends LitElement {
|
||||
if (!cameraConfig || !cameraConfig.frigate_url || !this._view) {
|
||||
return null;
|
||||
}
|
||||
if (!cameraConfig.camera_name) {
|
||||
return cameraConfig.frigate_url;
|
||||
}
|
||||
if (this._view.isViewerView() || this._view.isGalleryView()) {
|
||||
return `${cameraConfig.frigate_url}/events?camera=${cameraConfig.camera_name}`;
|
||||
}
|
||||
@@ -1015,12 +1019,14 @@ export class FrigateCard extends LitElement {
|
||||
// Do not artifically constrain aspect ratio if:
|
||||
// - It's fullscreen.
|
||||
// - Aspect ratio enforcement is disabled.
|
||||
// - Or aspect ratio enforcement is dynamic and it's a media view (i.e. not the gallery).
|
||||
// - Aspect ratio enforcement is dynamic and it's a media view (i.e. not the gallery).
|
||||
// - There is a message to display to the user.
|
||||
|
||||
return !(
|
||||
(screenfull.isEnabled && screenfull.isFullscreen) ||
|
||||
aspectRatioMode == 'unconstrained' ||
|
||||
(aspectRatioMode == 'dynamic' && this._view?.isMediaView())
|
||||
(aspectRatioMode == 'dynamic' && this._view?.isMediaView() ||
|
||||
this._message != null)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1187,10 +1193,7 @@ export class FrigateCard extends LitElement {
|
||||
? html` <frigate-card-gallery
|
||||
.hass=${this._hass}
|
||||
.view=${this._view}
|
||||
.browseMediaQueryParameters=${BrowseMediaUtil.getBrowseMediaQueryParametersFromView(
|
||||
this._view,
|
||||
cameraConfig,
|
||||
)}
|
||||
.cameraConfig=${cameraConfig}
|
||||
class="${classMap(galleryClasses)}"
|
||||
>
|
||||
</frigate-card-gallery>`
|
||||
@@ -1199,10 +1202,7 @@ export class FrigateCard extends LitElement {
|
||||
? html` <frigate-card-viewer
|
||||
.hass=${this._hass}
|
||||
.view=${this._view}
|
||||
.browseMediaQueryParameters=${BrowseMediaUtil.getBrowseMediaQueryParametersFromView(
|
||||
this._view,
|
||||
cameraConfig,
|
||||
)}
|
||||
.cameraConfig=${cameraConfig}
|
||||
.viewerConfig=${this._getConfig().event_viewer}
|
||||
.resolvedMediaCache=${this._resolvedMediaCache}
|
||||
class="${classMap(viewerClasses)}"
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HomeAssistant } from 'custom-card-helpers';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import type { BrowseMediaQueryParameters, ExtendedHomeAssistant } from '../types.js';
|
||||
import type { CameraConfig, ExtendedHomeAssistant } from '../types.js';
|
||||
import { BrowseMediaUtil } from '../browse-media-util.js';
|
||||
import { View } from '../view.js';
|
||||
import { renderProgressIndicator } from './message.js';
|
||||
@@ -23,23 +23,33 @@ export class FrigateCardGallery extends LitElement {
|
||||
protected view?: Readonly<View>;
|
||||
|
||||
@property({ attribute: false })
|
||||
protected browseMediaQueryParameters?: BrowseMediaQueryParameters;
|
||||
protected cameraConfig?: CameraConfig;
|
||||
|
||||
/**
|
||||
* Master render method.
|
||||
* @returns A rendered template.
|
||||
*/
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this.hass || !this.view || !this.browseMediaQueryParameters) {
|
||||
if (!this.hass || !this.view || !this.cameraConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.view.target) {
|
||||
const browseMediaQueryParameters =
|
||||
BrowseMediaUtil.getBrowseMediaQueryParametersOrDispatchError(
|
||||
this,
|
||||
this.view,
|
||||
this.cameraConfig,
|
||||
);
|
||||
if (!browseMediaQueryParameters) {
|
||||
return;
|
||||
}
|
||||
|
||||
BrowseMediaUtil.fetchLatestMediaAndDispatchViewChange(
|
||||
this,
|
||||
this.hass,
|
||||
this.view,
|
||||
this.browseMediaQueryParameters,
|
||||
browseMediaQueryParameters,
|
||||
);
|
||||
return renderProgressIndicator();
|
||||
}
|
||||
|
||||
+11
-2
@@ -1,4 +1,5 @@
|
||||
// TODO readme
|
||||
// TODO autodetect live provider from cameras configuration, or allow explicit setting.
|
||||
// TODO verify README links worked correctly (e.g. basic cameras configuration)
|
||||
|
||||
import {
|
||||
CSSResultGroup,
|
||||
@@ -371,7 +372,8 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
|
||||
const config = getOverriddenConfig(
|
||||
this.liveConfig,
|
||||
this.liveOverrides,
|
||||
conditionState) as LiveConfig;
|
||||
conditionState,
|
||||
) as LiveConfig;
|
||||
|
||||
return html` <div class="embla__slide">
|
||||
<frigate-card-live-provider
|
||||
@@ -832,6 +834,13 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
||||
this._jsmpegCanvasElement = document.createElement('canvas');
|
||||
this._jsmpegCanvasElement.className = 'media';
|
||||
|
||||
if (!this.cameraConfig?.camera_name) {
|
||||
return dispatchErrorMessageEvent(
|
||||
this,
|
||||
localize('error.no_camera_name') + `: ${JSON.stringify(this.cameraConfig)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const url = await this._getURL();
|
||||
if (url) {
|
||||
this._jsmpegVideoPlayer = this._createJSMPEGPlayer(url);
|
||||
|
||||
@@ -18,6 +18,7 @@ import type {
|
||||
BrowseMediaNeighbors,
|
||||
BrowseMediaQueryParameters,
|
||||
BrowseMediaSource,
|
||||
CameraConfig,
|
||||
ExtendedHomeAssistant,
|
||||
MediaShowInfo,
|
||||
ViewerConfig,
|
||||
@@ -30,10 +31,7 @@ import {
|
||||
} from './thumbnail-carousel.js';
|
||||
import { ResolvedMediaCache, ResolvedMediaUtil } from '../resolved-media.js';
|
||||
import { View } from '../view.js';
|
||||
import {
|
||||
createMediaShowInfo,
|
||||
dispatchErrorMessageEvent,
|
||||
} from '../common.js';
|
||||
import { createMediaShowInfo, dispatchErrorMessageEvent } from '../common.js';
|
||||
import { renderProgressIndicator } from '../components/message.js';
|
||||
|
||||
import './next-prev-control.js';
|
||||
@@ -53,7 +51,7 @@ export class FrigateCardViewer extends LitElement {
|
||||
protected viewerConfig?: ViewerConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
protected browseMediaQueryParameters?: BrowseMediaQueryParameters;
|
||||
protected cameraConfig?: CameraConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
protected resolvedMediaCache?: ResolvedMediaCache;
|
||||
@@ -63,7 +61,17 @@ export class FrigateCardViewer extends LitElement {
|
||||
* @returns A rendered template.
|
||||
*/
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this.hass || !this.view || !this.browseMediaQueryParameters) {
|
||||
if (!this.hass || !this.view || !this.cameraConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
const browseMediaQueryParameters =
|
||||
BrowseMediaUtil.getBrowseMediaQueryParametersOrDispatchError(
|
||||
this,
|
||||
this.view,
|
||||
this.cameraConfig,
|
||||
);
|
||||
if (!browseMediaQueryParameters) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -72,7 +80,7 @@ export class FrigateCardViewer extends LitElement {
|
||||
this,
|
||||
this.hass,
|
||||
this.view,
|
||||
this.browseMediaQueryParameters,
|
||||
browseMediaQueryParameters,
|
||||
);
|
||||
return renderProgressIndicator();
|
||||
}
|
||||
@@ -82,7 +90,7 @@ export class FrigateCardViewer extends LitElement {
|
||||
.viewerConfig=${this.viewerConfig}
|
||||
.resolvedMediaCache=${this.resolvedMediaCache}
|
||||
.hass=${this.hass}
|
||||
.browseMediaQueryParameters=${this.browseMediaQueryParameters}
|
||||
.browseMediaQueryParameters=${browseMediaQueryParameters}
|
||||
>
|
||||
</frigate-card-viewer-core>`;
|
||||
}
|
||||
|
||||
@@ -171,12 +171,14 @@
|
||||
"upgrade_available": "An automated card configuration upgrade is available, please visit the visual card editor",
|
||||
"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",
|
||||
"duplicate_camera_id": "Duplicate Frigate camera, use the 'id' parameter to uniquely identify cameras with the same 'camera_entity' or 'camera_name'",
|
||||
"no_cameras": "No valid cameras found, you must configure at least one camera entry",
|
||||
"no_camera_id": "Could not determine camera id for the following camera, may need to set 'id' parameter manually",
|
||||
"duplicate_camera_id": "Duplicate Frigate camera id for the following camera, use the 'id' parameter to uniquely identify cameras",
|
||||
"could_not_render_elements": "Could not render picture elements",
|
||||
"invalid_elements_config": "Invalid picture elements configuration",
|
||||
"jsmpeg_no_sign": "Could not retrieve or sign JSMPEG websocket path",
|
||||
"jsmpeg_no_player": "Could not start JSMPEG player",
|
||||
"no_camera_name": "Could not determine Frigate camera name for camera, please specify either 'camera_entity' or 'camera_name' for the following camera",
|
||||
"download_no_media": "No media to download",
|
||||
"download_no_event_id": "Could not extract Frigate event id from media",
|
||||
"download_sign_failed": "Could not sign media URL for download"
|
||||
|
||||
@@ -19,4 +19,5 @@
|
||||
|
||||
span {
|
||||
padding: 10px;
|
||||
word-break: break-word;
|
||||
}
|
||||
Reference in New Issue
Block a user