From bb9c27e34c095b5122f748db2193680e88a884c1 Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Sun, 10 Oct 2021 20:21:21 -0700 Subject: [PATCH] Add birdseye support. --- README.md | 10 ++-- package.json | 12 ++--- src/card.ts | 89 ++++++++++++++++++---------------- src/components/live.ts | 4 +- src/editor.ts | 64 ++++++++++++------------ src/localize/languages/en.json | 12 ++--- src/types.ts | 3 +- 7 files changed, 101 insertions(+), 93 deletions(-) diff --git a/README.md b/README.md index 9f0225d8..849137dc 100644 --- a/README.md +++ b/README.md @@ -63,19 +63,19 @@ lovelace: ## Options -### Required +### Basic | Option | Default | Description | | ------------- | - | --------------------------------------------- | -| `camera_entity` | | The Frigate camera entity to use in the live camera view.| +| `camera_entity` | | The optional Frigate camera entity to use in the `frigate` live provider view. Also used to automatically detect the `frigate_camera_name`.| +| `frigate_camera_name` | Autodetected from `camera_entity` if that is specified. | 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.| +| `view_default` | `live` | The view to show by default. See [views](#views) below.| ### Optional | Option | Default | Description | | ------------- | --------------------------------------------- | - | -| `frigate_camera_name` | The Frigate camera name Home Assistant associates with that camera entity, if none then the string after the `camera.` in the `camera_entity` field. | This parameter allows the Frigate camera name to be overriden. This name is used for communicating with the Frigate backend, e.g. for fetching events. | | `live_provider` | `frigate` | The means through which the live camera view is displayed. See [Live Provider](#live-provider) below.| -| `view_default` | `live` | The view to show by default. See [views](#views) below.| | `frigate_client_id` | `frigate` | 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://blakeblackshear.github.io/frigate/usage/home-assistant/#multiple-instance-support).| | `view_timeout` | | A numbers of seconds of inactivity after which the card will reset to the default configured view. Inactivity is defined as lack of interaction with the Frigate menu.| | `frigate_url` | | The URL of the frigate server. If set, this value will be (exclusively) used for a `Frigate UI` menu button. | @@ -87,7 +87,7 @@ lovelace: |Live Provider|Latency|Frame Rate|Installation|Description| | -- | -- | -- | -- | -- | |`frigate`|High|High|Builtin|Use the built-in Home Assistant camera stream from Frigate (RTMP). The camera doesn't even need to be a Frigate camera! | -|`frigate-jsmpeg`|Lower|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.| +|`frigate-jsmpeg`|Lower|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`|Lowest|High|Separate installation required|Uses [WebRTC](https://github.com/AlexxIT/WebRTC) to stream live feed, requires manual extra setup, see [below](#webrtc).| ### Appearance diff --git a/package.json b/package.json index 437e41a7..b00f9da5 100644 --- a/package.json +++ b/package.json @@ -19,17 +19,17 @@ "custom-card-helpers": "^1.8.0", "dayjs": "^1.10.7", "home-assistant-js-websocket": "^5.11.1", - "lit": "^2.0.0", + "lit": "^2.0.2", "screenfull": "^5.1.0", - "zod": "^3.9.5" + "zod": "^3.9.8" }, "devDependencies": { - "@babel/core": "^7.15.5", + "@babel/core": "^7.15.8", "@babel/plugin-proposal-class-properties": "^7.14.5", - "@babel/plugin-proposal-decorators": "^7.15.4", + "@babel/plugin-proposal-decorators": "^7.15.8", "@rollup/plugin-json": "^4.1.0", - "@typescript-eslint/eslint-plugin": "^4.32.0", - "@typescript-eslint/parser": "^4.32.0", + "@typescript-eslint/eslint-plugin": "^4.33.0", + "@typescript-eslint/parser": "^4.33.0", "eslint": "^7.32.0", "eslint-config-airbnb-base": "^14.2.1", "eslint-config-prettier": "^8.3.0", diff --git a/src/card.ts b/src/card.ts index 727b364a..0948df53 100644 --- a/src/card.ts +++ b/src/card.ts @@ -33,7 +33,6 @@ import { CARD_VERSION } from './const'; import { FrigateCardMenu, MENU_HEIGHT } from './components/menu'; import { View } from './view'; import { - getParseErrorKeys, homeAssistantWSRequest, shouldUpdateBasedOnHass, } from './common'; @@ -221,30 +220,32 @@ export class FrigateCard extends LitElement { return this.config.frigate_camera_name; } - // Option 2: Find entity unique_id in registry. - const request = { - type: 'config/entity_registry/get', - entity_id: this.config.camera_entity, - }; - try { - const entityResult = await homeAssistantWSRequest( - this._hass, - entitySchema, - request, - ); - if (entityResult && entityResult.platform == 'frigate') { - const match = entityResult.unique_id.match(/:camera:(?[^:]+)$/); - if (match && match.groups) { - return match.groups['camera']; + if (this.config.camera_entity) { + // Option 2: Find entity unique_id in registry. + const request = { + type: 'config/entity_registry/get', + entity_id: this.config.camera_entity, + }; + try { + const entityResult = await homeAssistantWSRequest( + this._hass, + entitySchema, + request, + ); + if (entityResult && entityResult.platform == 'frigate') { + const match = entityResult.unique_id.match(/:camera:(?[^:]+)$/); + if (match && match.groups) { + return match.groups['camera']; + } } + } catch (e: any) { + // Pass. } - } catch (e: any) { - // Pass. - } - // Option 3: Guess from the entity_id. - if (this.config.camera_entity.includes('.')) { - return this.config.camera_entity.split('.', 2)[1]; + // Option 3: Guess from the entity_id. + if (this.config.camera_entity.includes('.')) { + return this.config.camera_entity.split('.', 2)[1]; + } } return null; @@ -287,11 +288,13 @@ export class FrigateCard extends LitElement { getLovelace().setEditMode(true); } + this._frigateCameraName = null; this.config = config; - this._entitiesToMonitor = [ - ...(this.config.update_entities || []), - this.config.camera_entity, - ]; + + this._entitiesToMonitor = this.config.update_entities || []; + if (this.config.camera_entity) { + this._entitiesToMonitor.push(this.config.camera_entity); + } this._changeView(); } @@ -636,21 +639,25 @@ export class FrigateCard extends LitElement { ` : ``} - { - this._menu.addButton(e.detail); - }} - @frigate-card:menu-remove=${(e) => { - this._menu.removeButton(e.detail); - }} - @frigate-card:state-request=${(e) => { - e.view = this._view; - }} - > - + ${this.config.elements + ? html` + { + this._menu.addButton(e.detail); + }} + @frigate-card:menu-remove=${(e) => { + this._menu.removeButton(e.detail); + }} + @frigate-card:state-request=${(e) => { + e.view = this._view; + }} + > + + ` + : ``} `; } diff --git a/src/components/live.ts b/src/components/live.ts index 6c6fdd0f..bedfa8f1 100644 --- a/src/components/live.ts +++ b/src/components/live.ts @@ -68,10 +68,10 @@ export class FrigateCardViewerFrigate extends LitElement { protected hass!: HomeAssistant & ExtendedHomeAssistant; @property({ attribute: false }) - protected cameraEntity!: string; + protected cameraEntity?: string; protected render(): TemplateResult | void { - if (!(this.cameraEntity in this.hass.states)) { + if (!this.cameraEntity || !(this.cameraEntity in this.hass.states)) { return dispatchMessageEvent( this, localize('error.no_live_camera'), diff --git a/src/editor.ts b/src/editor.ts index ff0618b6..fc2a1378 100644 --- a/src/editor.ts +++ b/src/editor.ts @@ -9,10 +9,10 @@ import type { FrigateCardConfig } from './types'; import frigate_card_editor_style from './scss/editor.scss'; const options = { - required: { + basic: { icon: 'cog', - name: localize('editor.required'), - secondary: localize('editor.required_secondary'), + name: localize('editor.basic'), + secondary: localize('editor.basic_secondary'), show: true, }, optional: { @@ -146,14 +146,14 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor return html`
-
+
- -
${options.required.name}
+ +
${options.basic.name}
-
${options.required.secondary}
+
${options.basic.secondary}
- ${options.required.show + ${options.basic.show ? html`
+ + + + ${Object.keys(viewModes).map((key) => { + return html` + ${viewModes[key]} + `; + })} + +
` : ''} @@ -184,12 +208,6 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
${options.optional.show ? html`
- - - - ${Object.keys(viewModes).map((key) => { - return html` - ${viewModes[key]} - `; - })} - - 1 Frigate server)", "view_timeout": "View timeout (seconds)", @@ -92,10 +92,10 @@ "unknown": "Unknown error", "troubleshooting": "Check troubleshooting", "could_not_resolve": "Could not resolve media URL", - "no_live_camera": "No live camera", + "no_live_camera": "The camera_entity parameter must be set and valid for this live provider", "invalid_configuration": "Invalid configuration", "missing_webrtc": "WebRTC component not found", - "no_frigate_camera_name": "Cannot derive frigate_camera_name, you may need to set it manually", + "no_frigate_camera_name": "Cannot autodetect Frigate camera name, you need to either set camera_entity and / or frigate_camera_name", "could_not_render_elements": "Could not render picture elements", "invalid_elements_config": "Invalid picture elements configuration" } diff --git a/src/types.ts b/src/types.ts index 2fe0ea71..1ceb8b55 100644 --- a/src/types.ts +++ b/src/types.ts @@ -244,7 +244,8 @@ const pictureElementsSchema = pictureElementSchema.array().optional(); export type PictureElements = z.infer; export const frigateCardConfigSchema = z.object({ - camera_entity: z.string(), + camera_entity: z.string().optional(), + // No URL validation to allow relative URLs within HA (e.g. addons). frigate_url: z.string().optional(), frigate_client_id: z.string().optional().default('frigate'),