Change configuration path for Frigate options.

This commit is contained in:
Dermot Duffy
2022-06-10 13:44:10 -07:00
parent 2d8f3adb7d
commit a62eaf4bc9
14 changed files with 247 additions and 173 deletions
+48 -29
View File
@@ -103,18 +103,14 @@ See the [fully expanded cameras configuration example](#config-expanded-cameras)
| Option | Default | Overridable | Description |
| - | - | - | - |
| `camera_entity` | | :heavy_multiplication_x: | The Home Assistant camera entity to use with the `frigate` live provider view. Also used to automatically detect the name of the underlying Frigate camera, and the title/icon of the camera. |
| `camera_name` | Autodetected from `camera_entity` if that is specified. | :heavy_multiplication_x: | The Frigate camera name to use when communicating with the Frigate server, e.g. for viewing clips/snapshots or the JSMPEG live view. To view the birdseye view set this to `birdseye` and use the `frigate-jsmpeg` live provider.|
| `live_provider` | `auto` | :heavy_multiplication_x: | The choice of live stream provider. See [Live Providers](#live-providers) below.|
| `frigate_url` | | :heavy_multiplication_x: | The URL of the frigate server. If set, this value will be (exclusively) used for a `Frigate UI` menu button. |
| `label` | | :heavy_multiplication_x: | A Frigate label / object filter used to filter events (clips & snapshots), e.g. 'person'.|
| `zone` | | :heavy_multiplication_x: | A Frigate zone used to filter events (clips & snapshots), e.g. 'front_door'.|
| `client_id` | `frigate` | :heavy_multiplication_x: | The Frigate client id to use. If this Home Assistant server has multiple Frigate server backends configured, this selects which server should be used. It should be set to the MQTT client id configured for this server, see [Frigate Integration Multiple Instance Support](https://docs.frigate.video/integrations/home-assistant/#multiple-instance-support).|
| `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_card` | | :heavy_multiplication_x: | The WebRTC entity/URL to use for this camera with the `webrtc-card` live provider. See below. |
| `id` | `camera_entity`, `webrtc_card.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). |
| `dependencies` | | :heavy_multiplication_x: | Other cameras that this camera should depend upon. See [camera dependencies](#camera-dependency-configuration) below. |
| `id` | `camera_entity`, `webrtc_card.entity` or `frigate.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). |
| `frigate` | | :heavy_multiplication_x: | Options for a Frigate camera. See [Frigate configuration](#camera-frigate-configuration) below. |
| `dependencies` | | :heavy_multiplication_x: | Other cameras that this camera should depend upon. See [camera dependencies](#camera-dependencies-configuration) below. |
| `triggers` | | :heavy_multiplication_x: | Define what should cause this camera to update/trigger. See [camera triggers](#camera-trigger-configuration) below. |
| `webrtc_card` | | :heavy_multiplication_x: | The WebRTC entity/URL to use for this camera with the `webrtc-card` live provider. See below. |
<a name="live-providers"></a>
@@ -128,6 +124,26 @@ See the [fully expanded cameras configuration example](#config-expanded-cameras)
|`frigate-jsmpeg`|Better|Low|Builtin|Stream the JSMPEG stream from Frigate (proxied via the Frigate integration). See [note below on the required integration version](#jsmpeg-troubleshooting) for this live provider to function. This is the only live provider that can view the Frigate `birdseye` view.|
|`webrtc-card`|Best|High|Separate installation required|Embed's [AlexxIT's WebRTC Card](https://github.com/AlexxIT/WebRTC) to stream live feed, requires manual extra setup, see [below](#webrtc). Not to be confused with native Home Assistant WebRTC (use `ha` provider above).|
<a name="camera-frigate-configuration"></a>
#### Camera Frigate configuration
The `frigate` block configures options for a Frigate camera. This configuration is included as part of a camera entry in the `cameras` array.
```yaml
cameras:
- frigate:
```
| Option | Default | Overridable | Description |
| - | - | - | - |
| `camera_name` | Autodetected from `camera_entity` if that is specified. | :heavy_multiplication_x: | The Frigate camera name to use when communicating with the Frigate server, e.g. for viewing clips/snapshots or the JSMPEG live view. To view the birdseye view set this to `birdseye` and use the `frigate-jsmpeg` live provider.|
| `url` | | :heavy_multiplication_x: | The URL of the frigate server. If set, this value will be (exclusively) used for a `Frigate UI` menu button. All other communication with Frigate goes via Home Assistant. |
| `label` | | :heavy_multiplication_x: | A Frigate label / object filter used to filter events (clips & snapshots), e.g. `person`.|
| `zone` | | :heavy_multiplication_x: | A Frigate zone used to filter events (clips & snapshots), e.g. `front_door`.|
| `client_id` | `frigate` | :heavy_multiplication_x: | The Frigate client id to use. If this Home Assistant server has multiple Frigate server backends configured, this selects which server should be used. It should be set to the MQTT client id configured for this server, see [Frigate Integration Multiple Instance Support](https://docs.frigate.video/integrations/home-assistant/#multiple-instance-support).|
#### Camera WebRTC Card configuration
The `webrtc_card` block configures only the entity/URL for this camera to be used with the WebRTC Card live provider. This configuration is included as part of a camera entry in the `cameras` array.
@@ -144,7 +160,7 @@ cameras:
See [Using the WebRTC Card](#webrtc) below for more details on how to use the WebRTC Card live provider.
<a name="camera-dependencies"></a>
<a name="camera-dependencies-configuration"></a>
#### Camera Dependency Configuration
@@ -160,7 +176,7 @@ cameras:
| `cameras` | | :heavy_multiplication_x: | An optional array of other camera identifiers (see [camera IDs](#camera-ids)). If specified the card will fetch events for this camera and *also* recursively events for the named cameras. All dependent cameras must themselves be a configured camera in the card. This can be useful to group events for cameras that are close together, to always have clips/snapshots show fully merged events across all cameras or to show events for the `birdseye` camera that otherwise would not have events itself.|
| `all_cameras` | `false` | :heavy_multiplication_x: | Shortcut to specify all other cameras as dependent cameras.|
<a name="camera-triggers"></a>
<a name="camera-triggers-configuration"></a>
#### Camera Trigger Configuration
@@ -181,7 +197,7 @@ cameras:
#### 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`, `webrtc_card.entity` or `camera_name`} parameters for that camera -- in that order of precedence. These ids may be used in conditions, dependencies 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_card.entity` or `frigate.camera_name`} parameters for that camera -- in that order of precedence. These ids may be used in conditions, dependencies or custom actions to refer to a given camera unambiguously. |
#### Example
@@ -205,7 +221,7 @@ See the [fully expanded view configuration example](#config-expanded-view) for h
| `timeout_seconds` | `300` | :white_check_mark: | A numbers of seconds of inactivity after user interaction, after which the card will reset to the default configured view (i.e. 'screensaver' functionality). Inactivity is defined as lack of mouse/touch interaction with the Frigate card. If the default view occurs sooner (e.g. via `update_seconds` or manually) the timer will be stopped. `0` means disable this functionality. |
| `update_seconds` | `0` | :white_check_mark: | A number of seconds after which to automatically update/refresh the default view. See [card updates](#card-updates) below for behavior and usecases. If the default view occurs sooner (e.g. manually) the timer will start over. `0` disables this functionality.|
| `update_force` | `false` | :white_check_mark: | Whether automated card updates/refreshes should ignore user interaction. See [card updates](#card-updates) below for behavior and usecases.|
| `update_entities` | | :white_check_mark: | **YAML only**: A card-wide list of entities that should cause the view to reset to the default (if the entity only pertains to a particular camera use `triggers` for the selected camera instead, see [Trigger Configuration](#camera-triggers)). See [card updates](#card-updates) below for behavior and usecases.|
| `update_entities` | | :white_check_mark: | **YAML only**: A card-wide list of entities that should cause the view to reset to the default (if the entity only pertains to a particular camera use `triggers` for the selected camera instead, see [Trigger Configuration](#camera-triggers-configuration)). See [card updates](#card-updates) below for behavior and usecases.|
| `update_cycle_camera` | `false` | :white_check_mark: | When set to `true` the selected camera is cycled on each default view change. |
| `render_entities` | | :white_check_mark: | **YAML only**: A list of entity ids that should cause the card to re-render 'in-place'. The view/camera is not changed. `update_*` flags do not pertain/relate to the behavior of this flag. This should **very** rarely be needed, but could be useful if the card is both setting and changing HA state of the same object as could be the case for some complex `card_mod` scenarios ([example](https://github.com/dermotduffy/frigate-hass-card/issues/343)). |
| `scan` | | :white_check_mark: | Configuration for [scan mode](#scan-mode). |
@@ -272,8 +288,8 @@ menu:
| `frigate` | :white_check_mark: | The `Frigate` menu button: brings the user to the default configured view (`view.default`), or collapses/expands the menu if the `menu.style` is `hidden` . |
| `cameras` | :white_check_mark: | The camera selection submenu. Will only appear if multiple cameras are configured. |
| `live` | :white_check_mark: | The `live` view menu button: brings the user to the `live` view. See [views](#views) below.|
| `clips` | :white_check_mark: | The `clips` view menu button: brings the user to the `clips` view on tap and the most-recent `clip` view on hold. See [views](#views) below. This button will never be shown if the `camera_name` for the selected camera is not auto-detected/specified (e.g. non-Frigate cameras), or if the `camera_name` is `birdseye`.|
| `snapshots` | :white_check_mark: | The `snapshots` view menu button: brings the user to the `clips` view on tap and the most-recent `snapshot` view on hold. See [views](#views) below. This button will never be shown if the `camera_name` for the selected camera is not auto-detected/specified (e.g. non-Frigate cameras), or if the `camera_name` is `birdseye`.|
| `clips` | :white_check_mark: | The `clips` view menu button: brings the user to the `clips` view on tap and the most-recent `clip` view on hold. See [views](#views) below. This button will never be shown if the `frigate.camera_name` for the selected camera is not auto-detected/specified (e.g. non-Frigate cameras), or if the `frigate.camera_name` is `birdseye`.|
| `snapshots` | :white_check_mark: | The `snapshots` view menu button: brings the user to the `clips` view on tap and the most-recent `snapshot` view on hold. See [views](#views) below. This button will never be shown if the `frigate.camera_name` for the selected camera is not auto-detected/specified (e.g. non-Frigate cameras), or if the `frigate.camera_name` is `birdseye`.|
| `image` | :white_check_mark: | The `image` view menu button: brings the user to the static `image` view. See [views](#views) below.|
| `download` | :white_check_mark: | The `download` menu button: allow direct download of the media being displayed.|
| `frigate_ui` | :white_check_mark: | The `frigate_ui` menu button: brings the user to a context-appropriate page on the Frigate UI (e.g. the camera homepage). Will only appear if the `frigate.url` option is set.|
@@ -1039,13 +1055,14 @@ Reference: [Camera Options](#camera-options).
```yaml
cameras:
- frigate_url: http://my.frigate.local
client_id: frigate
camera_name: front_door
label: person
zone: steps
camera_entity: camera.front_Door
- camera_entity: camera.front_Door
live_provider: ha
frigate:
url: http://my.frigate.local
client_id: frigate
camera_name: front_door
label: person
zone: steps
# Show events for camera-2 when this camera is viewed.
dependencies:
all_cameras: false
@@ -1056,13 +1073,14 @@ cameras:
occupancy: true
entities:
- binary_sensor.front_door_sensor
- frigate_url: http://my-other.frigate.local
client_id: frigate-other
camera_name: entrance
label: car
zone: driveway
camera_entity: camera.entrance
- camera_entity: camera.entrance
live_provider: webrtc-card
frigate:
url: http://my-other.frigate.local
client_id: frigate-other
camera_name: entrance
label: car
zone: driveway
icon: 'mdi:car'
title: 'Front entrance'
# Custom identifier for the camera to refer to it above.
@@ -2383,7 +2401,7 @@ the new overriden default immediately. Alternatives to trigger the card to
change view but without `update_entities` would just be having an
`update_seconds` parameter which reloads the default view that many seconds
after user interaction stops or through the use of the `triggers` option for a
given camera (see [Trigger Configuration](#camera-triggers)).
given camera (see [Trigger Configuration](#camera-triggers-configuration)).
```yaml
view:
@@ -2525,7 +2543,8 @@ This example shows events for all other cameras when `birdseye` is selected. Thi
cameras:
- camera_entity: camera.kitchen
- camera_entity: camera.sitting_room
- camera_name: birdseye
- frigate:
camera_name: birdseye
dependencies:
all_cameras: true
```
@@ -2569,7 +2588,7 @@ The following table describes the behavior these flags have.
Note that no (other) automated updates are permitted when [scan mode](#scan-mode) is being triggered.
In the below "Trigger Entities" refers to the combination of `view.update_entities` and the `triggers.entities` for the currently selected camera (which in turn will also include the occupancy and motion sensor entities for Frigate cameras if `triggers.occupancy` and `triggers.motion` options are enabled, see [Trigger Configuration](#camera-triggers)).
In the below "Trigger Entities" refers to the combination of `view.update_entities` and the `triggers.entities` for the currently selected camera (which in turn will also include the occupancy and motion sensor entities for Frigate cameras if `triggers.occupancy` and `triggers.motion` options are enabled, see [Trigger Configuration](#camera-triggers-configuration)).
| `view . update_seconds` | `view . timeout_seconds` | `view . update_force` | Trigger Entities | Behavior |
| :-: | :-: | :-: | :-: | - |
+24 -21
View File
@@ -396,8 +396,8 @@ export class FrigateCard extends LitElement {
// Don't show `clips` button if there's no `camera_name` (e.g. non-Frigate
// cameras), or is birdseye (unless there are dependent cameras).
if (
cameraConfig?.camera_name &&
(cameraConfig?.camera_name !== CAMERA_BIRDSEYE ||
cameraConfig?.frigate.camera_name &&
(cameraConfig?.frigate.camera_name !== CAMERA_BIRDSEYE ||
cameraConfig.dependencies.cameras.length ||
cameraConfig.dependencies.all_cameras)
) {
@@ -415,8 +415,8 @@ export class FrigateCard extends LitElement {
// Don't show `snapshots` button if there's no `camera_name` (e.g. non-Frigate
// cameras), or is birdseye (unless there are dependent cameras).
if (
cameraConfig?.camera_name &&
(cameraConfig?.camera_name !== CAMERA_BIRDSEYE ||
cameraConfig?.frigate.camera_name &&
(cameraConfig?.frigate.camera_name !== CAMERA_BIRDSEYE ||
cameraConfig?.dependencies.cameras.length ||
cameraConfig?.dependencies.all_cameras)
) {
@@ -449,7 +449,8 @@ export class FrigateCard extends LitElement {
if (
this._cameras &&
[...this._cameras.values()].some(
(config) => config.camera_name && config.camera_name !== CAMERA_BIRDSEYE,
(config) =>
config.frigate.camera_name && config.frigate.camera_name !== CAMERA_BIRDSEYE,
)
) {
buttons.push({
@@ -475,7 +476,7 @@ export class FrigateCard extends LitElement {
});
}
if (cameraConfig?.frigate_url) {
if (cameraConfig?.frigate.url) {
buttons.push({
icon: 'mdi:web',
...this._getConfig().menu.buttons.frigate_ui,
@@ -598,13 +599,15 @@ export class FrigateCard extends LitElement {
cache: ExtendedEntityCache,
cameraConfig: CameraConfig,
): string | null {
if (cameraConfig.camera_name) {
if (cameraConfig.frigate.camera_name) {
return (
cache.getMatch(
(ent) =>
!!ent.unique_id?.match(
new RegExp(
`:motion_sensor:${cameraConfig.zone || cameraConfig.camera_name}`,
`:motion_sensor:${
cameraConfig.frigate.zone || cameraConfig.frigate.camera_name
}`,
),
),
)?.entity_id ?? null
@@ -623,15 +626,15 @@ export class FrigateCard extends LitElement {
cache: ExtendedEntityCache,
cameraConfig: CameraConfig,
): string | null {
if (cameraConfig.camera_name) {
if (cameraConfig.frigate.camera_name) {
return (
cache.getMatch(
(ent) =>
!!ent.unique_id?.match(
new RegExp(
`:occupancy_sensor:${cameraConfig.zone || cameraConfig.camera_name}_${
cameraConfig.label || 'all'
}`,
`:occupancy_sensor:${
cameraConfig.frigate.zone || cameraConfig.frigate.camera_name
}_${cameraConfig.frigate.label || 'all'}`,
),
),
)?.entity_id ?? null
@@ -673,10 +676,10 @@ export class FrigateCard extends LitElement {
}
}
if (!config.camera_name && entity) {
if (!config.frigate.camera_name && entity) {
const resolvedName = this._getFrigateCameraNameFromEntity(entity);
if (resolvedName) {
config.camera_name = resolvedName;
config.frigate.camera_name = resolvedName;
}
}
@@ -1146,7 +1149,7 @@ export class FrigateCard extends LitElement {
}
const path =
`/api/frigate/${cameraConfig.client_id}` +
`/api/frigate/${cameraConfig.frigate.client_id}` +
`/notifications/${event_id}/` +
`${this._view.isClipRelatedView() ? 'clip.mp4' : 'snapshot.jpg'}` +
`?download=true`;
@@ -1299,7 +1302,7 @@ export class FrigateCard extends LitElement {
: (this._getConfig().view.camera_select as FrigateCardView);
this._changeView({
view: new View({
view: this._cameras?.get(camera)?.camera_name
view: this._cameras?.get(camera)?.frigate.camera_name
? targetView
: // Fallback to supported views for non-Frigate cameras.
View.selectBestViewForNonFrigateCameras(targetView),
@@ -1368,16 +1371,16 @@ export class FrigateCard extends LitElement {
*/
protected _getFrigateURLFromContext(): string | null {
const cameraConfig = this._getSelectedCameraConfig();
if (!cameraConfig || !cameraConfig.frigate_url || !this._view) {
if (!cameraConfig || !cameraConfig.frigate.url || !this._view) {
return null;
}
if (!cameraConfig.camera_name) {
return cameraConfig.frigate_url;
if (!cameraConfig.frigate.camera_name) {
return cameraConfig.frigate.url;
}
if (this._view.isViewerView() || this._view.isGalleryView()) {
return `${cameraConfig.frigate_url}/events?camera=${cameraConfig.camera_name}`;
return `${cameraConfig.frigate.url}/events?camera=${cameraConfig.frigate.camera_name}`;
}
return `${cameraConfig.frigate_url}/cameras/${cameraConfig.camera_name}`;
return `${cameraConfig.frigate.url}/cameras/${cameraConfig.frigate.camera_name}`;
}
/**
+5 -5
View File
@@ -639,7 +639,7 @@ export class FrigateCardLiveProvider extends LitElement {
return 'webrtc-card';
} else if (this.cameraConfig?.camera_entity) {
return 'ha';
} else if (this.cameraConfig?.camera_name) {
} else if (this.cameraConfig?.frigate.camera_name) {
return 'frigate-jsmpeg';
}
return frigateCardConfigDefaults.cameras.live_provider;
@@ -1013,7 +1013,7 @@ export class FrigateCardLiveJSMPEG extends LitElement {
* @returns A URL or null.
*/
protected async _getURL(): Promise<string | null> {
if (!this.hass || !this.cameraConfig?.client_id || !this.cameraConfig?.camera_name) {
if (!this.hass || !this.cameraConfig?.frigate.client_id || !this.cameraConfig?.frigate.camera_name) {
return null;
}
@@ -1021,8 +1021,8 @@ export class FrigateCardLiveJSMPEG extends LitElement {
try {
response = await homeAssistantSignPath(
this.hass,
`/api/frigate/${this.cameraConfig.client_id}` +
`/jsmpeg/${this.cameraConfig.camera_name}`,
`/api/frigate/${this.cameraConfig.frigate.client_id}` +
`/jsmpeg/${this.cameraConfig.frigate.camera_name}`,
URL_SIGN_EXPIRY_SECONDS,
);
} catch (err) {
@@ -1131,7 +1131,7 @@ export class FrigateCardLiveJSMPEG extends LitElement {
this._jsmpegCanvasElement = document.createElement('canvas');
this._jsmpegCanvasElement.className = 'media';
if (!this.cameraConfig?.camera_name) {
if (!this.cameraConfig?.frigate.camera_name) {
return dispatchErrorMessageEvent(
this,
localize('error.no_camera_name'),
+2 -21
View File
@@ -27,30 +27,11 @@ import {
frigateCardHasAction,
getActionConfigGivenAction
} from '../utils/action.js';
import { FRIGATE_ICON_SVG_PATH } from '../utils/frigate.js';
import { refreshDynamicStateParameters } from '../utils/ha';
import './submenu.js';
export const FRIGATE_BUTTON_MENU_ICON = 'frigate';
export const FRIGATE_ICON_FILLED =
'm 4.8759466,22.743573 c 0.0866,0.69274 0.811811,1.16359 0.37885,1.27183 ' +
'-0.43297,0.10824 -2.32718,-3.43665 -2.7601492,-4.95202 -0.4329602,-1.51538 ' +
'-0.6764993,-3.22017 -0.5682593,-4.19434 0.1082301,-0.97417 5.7097085,-2.48955 ' +
'5.7097085,-2.89545 0,-0.4059 -1.81304,-0.0271 -1.89422,-0.35178 -0.0812,-0.32472 ' +
'1.36925,-0.12989 1.75892,-0.64945 0.60885,-0.81181 1.3800713,-0.6765 1.8671505,' +
'-1.1094696 0.4870902,-0.4329599 1.0824089,-2.0836399 1.1906589,-2.7871996 0.108241,' +
'-0.70357 -1.0824084,-1.51538 -1.4071389,-2.05658 -0.3247195,-0.54121 0.7035702,' +
'-0.92005 3.1931099,-1.94834 2.48954,-1.02829 10.39114,-3.30134994 10.49938,' +
'-3.03074994 0.10824,0.27061 -2.59779,1.40713994 -4.492,2.11069994 -1.89422,0.70357 ' +
'-4.97909,2.05658 -4.97909,2.43542 0,0.37885 0.16236,0.67651 0.0541,1.54244 -0.10824,' +
'0.86593 -0.12123,1.2702597 -0.32472,1.8400997 -0.1353,0.37884 -0.2706,1.27183 ' +
'0,2.0836295 0.21648,0.64945 0.92005,1.13653 1.24477,1.24478 0.2706,0.018 1.01746,' +
'0.0433 1.8401,0 1.02829,-0.0541 2.48954,0.0541 2.48954,0.32472 0,0.2706 -2.21894,' +
'0.10824 -2.21894,0.48708 0,0.37885 2.27306,-0.0541 2.21894,0.32473 -0.0541,0.37884 ' +
'-1.89422,0.21648 -2.86839,0.21648 -0.77933,0 -1.93031,-0.0361 -2.43542,-0.21648 ' +
'l -0.10824,0.37884 c -0.18038,0 -0.55744,0.10824 -0.94711,0.10824 -0.48708,0 ' +
'-0.51414,0.16236 -1.40713,0.16236 -0.892989,0 -0.622391,-0.0541 -1.4341894,-0.10824 ' +
'-0.81181,-0.0541 -3.842561,2.27306 -4.383761,3.03075 -0.54121,0.75768 ' +
'-0.21649,2.59778 -0.21649,3.43665 0,0.75379 -0.10824,2.43542 0,3.30135 z';
/**
* A menu for the FrigateCard.
@@ -266,7 +247,7 @@ export class FrigateCardMenu extends LitElement {
let stateParameters: StateParameters = { ...button };
const svgPath =
stateParameters.icon === FRIGATE_BUTTON_MENU_ICON ? FRIGATE_ICON_FILLED : '';
stateParameters.icon === FRIGATE_BUTTON_MENU_ICON ? FRIGATE_ICON_SVG_PATH : '';
if (this.hass && button.type === 'custom:frigate-card-menu-state-icon') {
stateParameters = refreshDynamicStateParameters(this.hass, stateParameters);
+26 -15
View File
@@ -324,12 +324,16 @@ class TimelineDataManager {
camera: string,
config: CameraConfig,
): Promise<void> => {
if (!config.camera_name) {
if (!config.frigate.camera_name) {
return;
}
let summary: RecordingSummary;
try {
summary = await getRecordingsSummary(hass, config.client_id, config.camera_name);
summary = await getRecordingsSummary(
hass,
config.frigate.client_id,
config.frigate.camera_name,
);
} catch (e) {
return dispatchFrigateCardErrorEvent(element, e as FrigateCardError);
}
@@ -382,7 +386,7 @@ class TimelineDataManager {
const params: BrowseMediaQueryParameters[] = [];
cameras.forEach((cameraConfig, cameraID) => {
(media === 'all' ? ['clips', 'snapshots'] : [media]).forEach((mediaType) => {
if (cameraConfig.camera_name !== CAMERA_BIRDSEYE) {
if (cameraConfig.frigate.camera_name !== CAMERA_BIRDSEYE) {
const param = getBrowseMediaQueryParameters(hass, cameraID, cameraConfig, {
before: end.getTime() / 1000,
after: start.getTime() / 1000,
@@ -584,10 +588,10 @@ export class FrigateCardTimelineCore extends LitElement {
const processedCameras: Set<string> = new Set();
// Get results in the order the cameras are specified in the configuration.
for (const camera of (this.cameras?.keys() || [])) {
for (const camera of this.cameras?.keys() || []) {
const recording = results.get(camera);
const config = this.cameras?.get(camera);
if (!recording || !config?.camera_name) {
if (!recording || !config?.frigate.camera_name) {
continue;
}
@@ -595,7 +599,7 @@ export class FrigateCardTimelineCore extends LitElement {
// Zones on that same camera do not get separate recordings. The card may
// have multiple instances of the same camera for different zoness, so
// need to enforce uniqueness here.
const uniqueID = `${config.client_id}/${config.camera_name}`;
const uniqueID = `${config.frigate.client_id}/${config.frigate.camera_name}`;
if (processedCameras.has(uniqueID)) {
continue;
}
@@ -616,21 +620,21 @@ export class FrigateCardTimelineCore extends LitElement {
if (!onlyMatchingHour || isMatchingHour) {
children.push(
createVideoChild(
`${prettifyTitle(config.camera_name)} ${format(
`${prettifyTitle(config.frigate.camera_name)} ${format(
hour,
'yyyy-MM-dd HH:mm',
)}`,
generateRecordingIdentifier({
clientId: config.client_id,
clientId: config.frigate.client_id,
year: dayData.day.getFullYear(),
month: dayData.day.getMonth() + 1,
day: dayData.day.getDate(),
hour: hourData.hour,
cameraName: config.camera_name,
cameraName: config.frigate.camera_name,
}),
{
recording: {
camera: config.camera_name,
camera: config.frigate.camera_name,
start_time: getUnixTime(startHour),
end_time: getUnixTime(endHour),
events: hourData.events,
@@ -665,7 +669,7 @@ export class FrigateCardTimelineCore extends LitElement {
const results: Map<string, CameraRecordings> = new Map();
const fetch = async (camera: string, config?: CameraConfig): Promise<void> => {
if (!config || !config.camera_name || !this.hass) {
if (!config || !config.frigate.camera_name || !this.hass) {
return;
}
@@ -673,12 +677,16 @@ export class FrigateCardTimelineCore extends LitElement {
const cameraResults = await Promise.all([
getRecordingSegments(
this.hass,
config.client_id,
config.camera_name,
config.frigate.client_id,
config.frigate.camera_name,
before,
after,
),
getRecordingsSummary(this.hass, config.client_id, config.camera_name),
getRecordingsSummary(
this.hass,
config.frigate.client_id,
config.frigate.camera_name,
),
]);
results.set(camera, { segments: cameraResults[0], summary: cameraResults[1] });
} catch (e) {}
@@ -879,7 +887,10 @@ export class FrigateCardTimelineCore extends LitElement {
protected _getGroups(): DataGroupCollectionType {
const groups: FrigateCardGroupData[] = [];
this.cameras?.forEach((cameraConfig, camera) => {
if (cameraConfig.camera_name && cameraConfig.camera_name !== CAMERA_BIRDSEYE) {
if (
cameraConfig.frigate.camera_name &&
cameraConfig.frigate.camera_name !== CAMERA_BIRDSEYE
) {
groups.push({
id: camera,
content: getCameraTitle(this.hass, cameraConfig),
+10 -10
View File
@@ -2,12 +2,7 @@ import { cloneDeep, get, isEqual, set } from 'lodash-es';
import {
CONF_CAMERAS,
CONF_CAMERAS_ARRAY_CAMERA_ENTITY,
CONF_CAMERAS_ARRAY_CAMERA_NAME,
CONF_CAMERAS_ARRAY_CLIENT_ID,
CONF_CAMERAS_ARRAY_LABEL,
CONF_CAMERAS_ARRAY_LIVE_PROVIDER,
CONF_CAMERAS_ARRAY_URL,
CONF_CAMERAS_ARRAY_ZONE,
CONF_IMAGE_URL,
CONF_LIVE_AUTO_UNMUTE,
CONF_LIVE_CONTROLS_NEXT_PREVIOUS_SIZE,
@@ -378,11 +373,11 @@ const upgradeToMultipleCameras = (): ((obj: RawFrigateCardConfig) => boolean) =>
const imports = {
camera_entity: CONF_CAMERAS_ARRAY_CAMERA_ENTITY,
'frigate.camera_name': CONF_CAMERAS_ARRAY_CAMERA_NAME,
'frigate.client_id': CONF_CAMERAS_ARRAY_CLIENT_ID,
'frigate.label': CONF_CAMERAS_ARRAY_LABEL,
'frigate.url': CONF_CAMERAS_ARRAY_URL,
'frigate.zone': CONF_CAMERAS_ARRAY_ZONE,
'frigate.camera_name': 'cameras.#.camera_name',
'frigate.client_id': 'cameras.#.client_id',
'frigate.label': 'cameras.#.label',
'frigate.url': 'cameras.#.frigate_url',
'frigate.zone': 'cameras.#.zone',
'live.webrtc.entity': `cameras.#.webrtc.entity`,
'live.webrtc.url': `cameras.#.webrtc.url`,
'live.provider': CONF_CAMERAS_ARRAY_LIVE_PROVIDER,
@@ -600,4 +595,9 @@ const UPGRADES = [
typeof val === 'boolean' ? (val ? 'all' : 'never') : undefined,
),
upgradeMoveToWithOverrides('event_viewer', CONF_MEDIA_VIEWER),
upgradeArrayValue(CONF_CAMERAS, upgradeMoveTo('camera_name', 'frigate.camera_name')),
upgradeArrayValue(CONF_CAMERAS, upgradeMoveTo('client_id', 'frigate.client_id')),
upgradeArrayValue(CONF_CAMERAS, upgradeMoveTo('label', 'frigate.label')),
upgradeArrayValue(CONF_CAMERAS, upgradeMoveTo('frigate_url', 'frigate.url')),
upgradeArrayValue(CONF_CAMERAS, upgradeMoveTo('zone', 'frigate.zone')),
];
+5 -5
View File
@@ -7,11 +7,11 @@ export const TROUBLESHOOTING_URL = `${REPO_URL}#troubleshooting` as const;
export const CONF_CAMERAS = 'cameras' as const;
export const CONF_CAMERAS_ARRAY_CAMERA_ENTITY =
`${CONF_CAMERAS}.#.camera_entity` as const;
export const CONF_CAMERAS_ARRAY_CAMERA_NAME = `${CONF_CAMERAS}.#.camera_name` as const;
export const CONF_CAMERAS_ARRAY_CLIENT_ID = `${CONF_CAMERAS}.#.client_id` as const;
export const CONF_CAMERAS_ARRAY_LABEL = `${CONF_CAMERAS}.#.label` as const;
export const CONF_CAMERAS_ARRAY_URL = `${CONF_CAMERAS}.#.frigate_url` as const;
export const CONF_CAMERAS_ARRAY_ZONE = `${CONF_CAMERAS}.#.zone` as const;
export const CONF_CAMERAS_ARRAY_FRIGATE_CAMERA_NAME = `${CONF_CAMERAS}.#.frigate.camera_name` as const;
export const CONF_CAMERAS_ARRAY_FRIGATE_CLIENT_ID = `${CONF_CAMERAS}.#.frigate.client_id` as const;
export const CONF_CAMERAS_ARRAY_FRIGATE_LABEL = `${CONF_CAMERAS}.#.frigate.label` as const;
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_ID = `${CONF_CAMERAS}.#.id` as const;
export const CONF_CAMERAS_ARRAY_TITLE = `${CONF_CAMERAS}.#.title` as const;
export const CONF_CAMERAS_ARRAY_ICON = `${CONF_CAMERAS}.#.icon` as const;
+65 -42
View File
@@ -14,22 +14,22 @@ import {
import {
CONF_CAMERAS,
CONF_CAMERAS_ARRAY_CAMERA_ENTITY,
CONF_CAMERAS_ARRAY_CAMERA_NAME,
CONF_CAMERAS_ARRAY_CLIENT_ID,
CONF_CAMERAS_ARRAY_DEPENDENCIES_ALL_CAMERAS,
CONF_CAMERAS_ARRAY_DEPENDENCIES_CAMERAS,
CONF_CAMERAS_ARRAY_FRIGATE_CAMERA_NAME,
CONF_CAMERAS_ARRAY_FRIGATE_CLIENT_ID,
CONF_CAMERAS_ARRAY_FRIGATE_LABEL,
CONF_CAMERAS_ARRAY_FRIGATE_URL,
CONF_CAMERAS_ARRAY_FRIGATE_ZONE,
CONF_CAMERAS_ARRAY_ICON,
CONF_CAMERAS_ARRAY_ID,
CONF_CAMERAS_ARRAY_LABEL,
CONF_CAMERAS_ARRAY_LIVE_PROVIDER,
CONF_CAMERAS_ARRAY_TITLE,
CONF_CAMERAS_ARRAY_TRIGGERS_ENTITIES,
CONF_CAMERAS_ARRAY_TRIGGERS_MOTION,
CONF_CAMERAS_ARRAY_TRIGGERS_OCCUPANCY,
CONF_CAMERAS_ARRAY_URL,
CONF_CAMERAS_ARRAY_WEBRTC_CARD_ENTITY,
CONF_CAMERAS_ARRAY_WEBRTC_CARD_URL,
CONF_CAMERAS_ARRAY_ZONE,
CONF_DIMENSIONS_ASPECT_RATIO,
CONF_DIMENSIONS_ASPECT_RATIO_MODE,
CONF_EVENT_GALLERY_CONTROLS_THUMBNAILS_SHOW_CONTROLS,
@@ -110,11 +110,13 @@ import {
} from './types.js';
import { arrayMove } from './utils/basic.js';
import { getCameraID, getCameraTitle } from './utils/camera.js';
import { FRIGATE_ICON_SVG_PATH } from './utils/frigate.js';
import { getEntitiesFromHASS, sideLoadHomeAssistantElements } from './utils/ha';
const MENU_BUTTONS = 'buttons';
const MENU_CAMERAS = 'cameras';
const MENU_CAMERAS_DEPENDENCIES = 'cameras.dependencies';
const MENU_CAMERAS_FRIGATE = 'cameras.frigate';
const MENU_CAMERAS_TRIGGERS = 'cameras.triggers';
const MENU_CAMERAS_WEBRTC = 'cameras.webrtc';
const MENU_OPTIONS = 'options';
@@ -721,8 +723,11 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
protected _putInSubmenu(
domain: string,
key: unknown,
icon: string,
labelPath: string,
icon: {
name?: string;
path?: string;
},
template: TemplateResult,
): TemplateResult {
const submenuClasses = {
@@ -737,15 +742,16 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
.domain=${domain}
.key=${key}
>
<ha-icon .icon=${icon}></ha-icon>
<span>${localize(labelPath)}</span>
</div>
${
this._expandedMenus[domain] === key
? html`<div class="values">${template}</div>`
: ''
}
${icon.name
? html` <ha-icon .icon=${icon.name}></ha-icon> `
: icon.path
? html` <ha-svg-icon .path="${icon.path}"></ha-svg-icon> `
: ``}
<span>${localize(labelPath)}</span>
</div>
${this._expandedMenus[domain] === key
? html`<div class="values">${template}</div>`
: ''}
</div>`;
}
@@ -891,52 +897,56 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
getArrayConfigPath(CONF_CAMERAS_ARRAY_CAMERA_ENTITY, cameraIndex),
'camera',
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_CAMERA_NAME, cameraIndex),
)}
${this._renderOptionSelector(
getArrayConfigPath(CONF_CAMERAS_ARRAY_LIVE_PROVIDER, cameraIndex),
liveProviders,
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_URL, cameraIndex),
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_LABEL, cameraIndex),
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_ZONE, cameraIndex),
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_CLIENT_ID, cameraIndex),
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_TITLE, cameraIndex),
)}
${this._renderStringInput(
${this._renderIconSelector(
getArrayConfigPath(CONF_CAMERAS_ARRAY_ICON, cameraIndex),
{
label: localize('config.cameras.icon'),
},
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_ID, cameraIndex),
)}
${this._putInSubmenu(
MENU_CAMERAS_WEBRTC,
MENU_CAMERAS_FRIGATE,
cameraIndex,
'mdi:webrtc',
'config.cameras.webrtc_card.options',
html`${this._renderEntitySelector(
getArrayConfigPath(CONF_CAMERAS_ARRAY_WEBRTC_CARD_ENTITY, cameraIndex),
'camera',
)}
${this._renderStringInput(
getArrayConfigPath(CONF_CAMERAS_ARRAY_WEBRTC_CARD_URL, cameraIndex),
)}`,
'config.cameras.frigate.options',
{ 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_DEPENDENCIES,
cameraIndex,
'mdi:graph',
'config.cameras.dependencies.options',
{ name: 'mdi:graph' },
html` ${this._renderSwitch(
getArrayConfigPath(
CONF_CAMERAS_ARRAY_DEPENDENCIES_ALL_CAMERAS,
@@ -958,8 +968,8 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
${this._putInSubmenu(
MENU_CAMERAS_TRIGGERS,
cameraIndex,
'mdi:magnify-scan',
'config.cameras.triggers.options',
{ name: 'mdi:magnify-scan' },
html` ${this._renderSwitch(
getArrayConfigPath(CONF_CAMERAS_ARRAY_TRIGGERS_OCCUPANCY, cameraIndex),
frigateCardConfigDefaults.cameras.triggers.occupancy,
@@ -976,6 +986,19 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
},
)}`,
)}
${this._putInSubmenu(
MENU_CAMERAS_WEBRTC,
cameraIndex,
'config.cameras.webrtc_card.options',
{ 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>
+12 -9
View File
@@ -13,18 +13,22 @@
"config": {
"cameras": {
"camera_entity": "Camera Entity",
"camera_name": "Frigate camera name (Autodetected from entity)",
"client_id": "Frigate client id (For >1 Frigate server)",
"icon": "Icon for this camera (Autodetected from entity)",
"id": "Unique id for this camera in this card",
"title": "Title for this camera (Autodetected from entity)",
"dependencies": {
"options": "Dependency Options",
"cameras": "Show events for specific cameras with this camera",
"all_cameras": "Show events for all cameras with this camera"
},
"id": "Unique id for this camera in this card",
"label": "Frigate label/object filter",
"frigate_url": "Frigate server URL",
"title": "Title for this camera (Autodetected from entity)",
"icon": "Icon for this camera (Autodetected from entity)",
"frigate": {
"options": "Frigate Options",
"camera_name": "Frigate camera name (Autodetected from entity)",
"client_id": "Frigate client id (For >1 Frigate server)",
"label": "Frigate label/object filter",
"url": "Frigate server URL",
"zone": "Frigate zone"
},
"live_provider": "Live view provider for this camera",
"live_providers": {
"auto": "Automatic",
@@ -42,8 +46,7 @@
"options": "WebRTC Card Options",
"entity": "WebRTC Card Camera Entity (Not a Frigate camera)",
"url": "WebRTC Card Camera URL"
},
"zone": "Frigate zone"
}
},
"common": {
"media_action_conditions": {
+2 -2
View File
@@ -62,7 +62,8 @@ div.upgrade span {
color: var(--secondary-text-color, 'black');
}
.submenu-header ha-icon {
.submenu-header ha-icon,
.submenu-header ha-svg-icon {
padding-right: 15px;
}
@@ -78,7 +79,6 @@ div.upgrade span {
width: calc(100% - 10px * 2);
margin: auto;
margin-bottom: 10px;
}
.submenu :not(.submenu.selected) {
border: none;
+12 -7
View File
@@ -370,8 +370,10 @@ const customSchema = z
* Camera configuration section
*/
export const cameraConfigDefault = {
client_id: 'frigate' as const,
live_provider: 'auto' as const,
frigate: {
client_id: 'frigate' as const,
},
dependencies: {
all_cameras: false,
cameras: [],
@@ -388,12 +390,6 @@ const webrtcCardCameraConfigSchema = z.object({
});
const cameraConfigSchema = z
.object({
// No URL validation to allow relative URLs within HA (e.g. Frigate addon).
frigate_url: z.string().optional(),
client_id: z.string().default(cameraConfigDefault.client_id),
camera_name: z.string().optional(),
label: z.string().optional(),
zone: z.string().optional(),
camera_entity: z.string().optional(),
live_provider: z.enum(LIVE_PROVIDERS).default(cameraConfigDefault.live_provider),
@@ -405,6 +401,15 @@ const cameraConfigSchema = z
// Optional identifier to separate different camera configurations used in
// this card.
id: z.string().optional(),
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),
// Camera identifiers for WebRTC.
webrtc_card: webrtcCardCameraConfigSchema.optional(),
+10 -2
View File
@@ -18,7 +18,10 @@ export function getCameraID(
config.webrtc_card &&
typeof config.webrtc_card['entity'] === 'string' &&
config.webrtc_card['entity']) ||
(typeof config?.camera_name === 'string' && config.camera_name) ||
(typeof config?.frigate === 'object' &&
config.frigate &&
typeof config?.frigate['camera_name'] === 'string' &&
config.frigate['camera_name']) ||
''
);
}
@@ -46,7 +49,12 @@ export function getCameraTitle(
config.webrtc_card &&
typeof config.webrtc_card['entity'] === 'string' &&
config.webrtc_card['entity']) ||
(typeof config?.camera_name === 'string' ? prettifyTitle(config.camera_name) : '') ||
(typeof config?.frigate === 'object' &&
config.frigate &&
typeof config?.frigate['camera_name'] === 'string' &&
config.frigate['camera_name']
? prettifyTitle(config.frigate['camera_name'])
: '') ||
(typeof config?.id === 'string' && config.id) ||
''
);
+21
View File
@@ -2,6 +2,27 @@ import { z } from 'zod';
import { ExtendedHomeAssistant } from '../types';
import { homeAssistantHTTPRequest } from './ha';
export const FRIGATE_ICON_SVG_PATH =
'm 4.8759466,22.743573 c 0.0866,0.69274 0.811811,1.16359 0.37885,1.27183 ' +
'-0.43297,0.10824 -2.32718,-3.43665 -2.7601492,-4.95202 -0.4329602,-1.51538 ' +
'-0.6764993,-3.22017 -0.5682593,-4.19434 0.1082301,-0.97417 5.7097085,-2.48955 ' +
'5.7097085,-2.89545 0,-0.4059 -1.81304,-0.0271 -1.89422,-0.35178 -0.0812,-0.32472 ' +
'1.36925,-0.12989 1.75892,-0.64945 0.60885,-0.81181 1.3800713,-0.6765 1.8671505,' +
'-1.1094696 0.4870902,-0.4329599 1.0824089,-2.0836399 1.1906589,-2.7871996 0.108241,' +
'-0.70357 -1.0824084,-1.51538 -1.4071389,-2.05658 -0.3247195,-0.54121 0.7035702,' +
'-0.92005 3.1931099,-1.94834 2.48954,-1.02829 10.39114,-3.30134994 10.49938,' +
'-3.03074994 0.10824,0.27061 -2.59779,1.40713994 -4.492,2.11069994 -1.89422,0.70357 ' +
'-4.97909,2.05658 -4.97909,2.43542 0,0.37885 0.16236,0.67651 0.0541,1.54244 -0.10824,' +
'0.86593 -0.12123,1.2702597 -0.32472,1.8400997 -0.1353,0.37884 -0.2706,1.27183 ' +
'0,2.0836295 0.21648,0.64945 0.92005,1.13653 1.24477,1.24478 0.2706,0.018 1.01746,' +
'0.0433 1.8401,0 1.02829,-0.0541 2.48954,0.0541 2.48954,0.32472 0,0.2706 -2.21894,' +
'0.10824 -2.21894,0.48708 0,0.37885 2.27306,-0.0541 2.21894,0.32473 -0.0541,0.37884 ' +
'-1.89422,0.21648 -2.86839,0.21648 -0.77933,0 -1.93031,-0.0361 -2.43542,-0.21648 ' +
'l -0.10824,0.37884 c -0.18038,0 -0.55744,0.10824 -0.94711,0.10824 -0.48708,0 ' +
'-0.51414,0.16236 -1.40713,0.16236 -0.892989,0 -0.622391,-0.0541 -1.4341894,-0.10824 ' +
'-0.81181,-0.0541 -3.842561,2.27306 -4.383761,3.03075 -0.54121,0.75768 ' +
'-0.21649,2.59778 -0.21649,3.43665 0,0.75379 -0.10824,2.43542 0,3.30135 z';
const recordingSummaryHourSchema = z.object({
hour: z.preprocess((arg) => Number(arg), z.number().min(0).max(23)),
duration: z.number().min(0),
+5 -5
View File
@@ -214,14 +214,14 @@ export const getBrowseMediaQueryParameters = (
cameraConfig?: CameraConfig,
overrides?: Partial<BrowseMediaQueryParameters>,
): BrowseMediaQueryParameters | null => {
if (!cameraConfig || !cameraConfig.camera_name) {
if (!cameraConfig || !cameraConfig.frigate.camera_name) {
return null;
}
return {
clientId: cameraConfig.client_id,
cameraName: cameraConfig.camera_name,
label: cameraConfig.label,
zone: cameraConfig.zone,
clientId: cameraConfig.frigate.client_id,
cameraName: cameraConfig.frigate.camera_name,
label: cameraConfig.frigate.label,
zone: cameraConfig.frigate.zone,
title: getCameraTitle(hass, cameraConfig),
cameraID: cameraID,
...overrides,