Merge pull request #117 from dermotduffy/birdseye
Add support for birdseye mode
This commit is contained in:
@@ -63,19 +63,19 @@ lovelace:
|
|||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
### Required
|
### Basic
|
||||||
|
|
||||||
| Option | Default | Description |
|
| 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
|
### Optional
|
||||||
|
|
||||||
| Option | Default | Description |
|
| 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.|
|
| `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).|
|
| `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.|
|
| `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. |
|
| `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|
|
|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`|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).|
|
|`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
|
### Appearance
|
||||||
|
|||||||
+6
-6
@@ -19,17 +19,17 @@
|
|||||||
"custom-card-helpers": "^1.8.0",
|
"custom-card-helpers": "^1.8.0",
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
"home-assistant-js-websocket": "^5.11.1",
|
"home-assistant-js-websocket": "^5.11.1",
|
||||||
"lit": "^2.0.0",
|
"lit": "^2.0.2",
|
||||||
"screenfull": "^5.1.0",
|
"screenfull": "^5.1.0",
|
||||||
"zod": "^3.9.5"
|
"zod": "^3.9.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.15.5",
|
"@babel/core": "^7.15.8",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
"@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",
|
"@rollup/plugin-json": "^4.1.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.32.0",
|
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
||||||
"@typescript-eslint/parser": "^4.32.0",
|
"@typescript-eslint/parser": "^4.33.0",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-config-airbnb-base": "^14.2.1",
|
"eslint-config-airbnb-base": "^14.2.1",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
|
|||||||
+12
-5
@@ -33,7 +33,6 @@ import { CARD_VERSION } from './const';
|
|||||||
import { FrigateCardMenu, MENU_HEIGHT } from './components/menu';
|
import { FrigateCardMenu, MENU_HEIGHT } from './components/menu';
|
||||||
import { View } from './view';
|
import { View } from './view';
|
||||||
import {
|
import {
|
||||||
getParseErrorKeys,
|
|
||||||
homeAssistantWSRequest,
|
homeAssistantWSRequest,
|
||||||
shouldUpdateBasedOnHass,
|
shouldUpdateBasedOnHass,
|
||||||
} from './common';
|
} from './common';
|
||||||
@@ -221,6 +220,7 @@ export class FrigateCard extends LitElement {
|
|||||||
return this.config.frigate_camera_name;
|
return this.config.frigate_camera_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.config.camera_entity) {
|
||||||
// Option 2: Find entity unique_id in registry.
|
// Option 2: Find entity unique_id in registry.
|
||||||
const request = {
|
const request = {
|
||||||
type: 'config/entity_registry/get',
|
type: 'config/entity_registry/get',
|
||||||
@@ -246,6 +246,7 @@ export class FrigateCard extends LitElement {
|
|||||||
if (this.config.camera_entity.includes('.')) {
|
if (this.config.camera_entity.includes('.')) {
|
||||||
return this.config.camera_entity.split('.', 2)[1];
|
return this.config.camera_entity.split('.', 2)[1];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -287,11 +288,13 @@ export class FrigateCard extends LitElement {
|
|||||||
getLovelace().setEditMode(true);
|
getLovelace().setEditMode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._frigateCameraName = null;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this._entitiesToMonitor = [
|
|
||||||
...(this.config.update_entities || []),
|
this._entitiesToMonitor = this.config.update_entities || [];
|
||||||
this.config.camera_entity,
|
if (this.config.camera_entity) {
|
||||||
];
|
this._entitiesToMonitor.push(this.config.camera_entity);
|
||||||
|
}
|
||||||
this._changeView();
|
this._changeView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -636,6 +639,8 @@ export class FrigateCard extends LitElement {
|
|||||||
</frigate-card-live>
|
</frigate-card-live>
|
||||||
`
|
`
|
||||||
: ``}
|
: ``}
|
||||||
|
${this.config.elements
|
||||||
|
? html`
|
||||||
<frigate-card-elements
|
<frigate-card-elements
|
||||||
.hass=${this._hass}
|
.hass=${this._hass}
|
||||||
.elements=${this.config.elements}
|
.elements=${this.config.elements}
|
||||||
@@ -651,6 +656,8 @@ export class FrigateCard extends LitElement {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
</frigate-card-elements>
|
</frigate-card-elements>
|
||||||
|
`
|
||||||
|
: ``}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,10 +68,10 @@ export class FrigateCardViewerFrigate extends LitElement {
|
|||||||
protected hass!: HomeAssistant & ExtendedHomeAssistant;
|
protected hass!: HomeAssistant & ExtendedHomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected cameraEntity!: string;
|
protected cameraEntity?: string;
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
if (!(this.cameraEntity in this.hass.states)) {
|
if (!this.cameraEntity || !(this.cameraEntity in this.hass.states)) {
|
||||||
return dispatchMessageEvent(
|
return dispatchMessageEvent(
|
||||||
this,
|
this,
|
||||||
localize('error.no_live_camera'),
|
localize('error.no_live_camera'),
|
||||||
|
|||||||
+38
-38
@@ -9,10 +9,10 @@ import type { FrigateCardConfig } from './types';
|
|||||||
import frigate_card_editor_style from './scss/editor.scss';
|
import frigate_card_editor_style from './scss/editor.scss';
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
required: {
|
basic: {
|
||||||
icon: 'cog',
|
icon: 'cog',
|
||||||
name: localize('editor.required'),
|
name: localize('editor.basic'),
|
||||||
secondary: localize('editor.required_secondary'),
|
secondary: localize('editor.basic_secondary'),
|
||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
optional: {
|
optional: {
|
||||||
@@ -146,14 +146,14 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="card-config">
|
<div class="card-config">
|
||||||
<div class="option" @click=${this._toggleOption} .option=${'required'}>
|
<div class="option" @click=${this._toggleOption} .option=${'basic'}>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ha-icon .icon=${`mdi:${options.required.icon}`}></ha-icon>
|
<ha-icon .icon=${`mdi:${options.basic.icon}`}></ha-icon>
|
||||||
<div class="title">${options.required.name}</div>
|
<div class="title">${options.basic.name}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="secondary">${options.required.secondary}</div>
|
<div class="secondary">${options.basic.secondary}</div>
|
||||||
</div>
|
</div>
|
||||||
${options.required.show
|
${options.basic.show
|
||||||
? html`
|
? html`
|
||||||
<div class="values">
|
<div class="values">
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
@@ -172,42 +172,12 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
})}
|
})}
|
||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ''}
|
|
||||||
<div class="option" @click=${this._toggleOption} .option=${'optional'}>
|
|
||||||
<div class="row">
|
|
||||||
<ha-icon .icon=${`mdi:${options.optional.icon}`}></ha-icon>
|
|
||||||
<div class="title">${options.optional.name}</div>
|
|
||||||
</div>
|
|
||||||
<div class="secondary">${options.optional.secondary}</div>
|
|
||||||
</div>
|
|
||||||
${options.optional.show
|
|
||||||
? html` <div class="values">
|
|
||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('editor.frigate_camera_name')}
|
label=${localize('editor.frigate_camera_name')}
|
||||||
.value=${this._config?.frigate_camera_name || ''}
|
.value=${this._config?.frigate_camera_name || ''}
|
||||||
.configValue=${'frigate_camera_name'}
|
.configValue=${'frigate_camera_name'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<paper-dropdown-menu
|
|
||||||
.label=${localize('editor.live_provider')}
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
.configValue=${'live_provider'}
|
|
||||||
>
|
|
||||||
<paper-listbox
|
|
||||||
slot="dropdown-content"
|
|
||||||
.selected=${Object.keys(liveProvider).indexOf(
|
|
||||||
this._config?.live_provider || '',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
${Object.keys(liveProvider).map((key) => {
|
|
||||||
return html`
|
|
||||||
<paper-item .label="${key}">${liveProvider[key]} </paper-item>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
</paper-listbox>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
label=${localize('editor.default_view')}
|
label=${localize('editor.default_view')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
@@ -226,6 +196,36 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
})}
|
})}
|
||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ''}
|
||||||
|
<div class="option" @click=${this._toggleOption} .option=${'optional'}>
|
||||||
|
<div class="row">
|
||||||
|
<ha-icon .icon=${`mdi:${options.optional.icon}`}></ha-icon>
|
||||||
|
<div class="title">${options.optional.name}</div>
|
||||||
|
</div>
|
||||||
|
<div class="secondary">${options.optional.secondary}</div>
|
||||||
|
</div>
|
||||||
|
${options.optional.show
|
||||||
|
? html` <div class="values">
|
||||||
|
<paper-dropdown-menu
|
||||||
|
.label=${localize('editor.live_provider')}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.configValue=${'live_provider'}
|
||||||
|
>
|
||||||
|
<paper-listbox
|
||||||
|
slot="dropdown-content"
|
||||||
|
.selected=${Object.keys(liveProvider).indexOf(
|
||||||
|
this._config?.live_provider || '',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
${Object.keys(liveProvider).map((key) => {
|
||||||
|
return html`
|
||||||
|
<paper-item .label="${key}">${liveProvider[key]} </paper-item>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
</paper-listbox>
|
||||||
|
</paper-dropdown-menu>
|
||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('editor.frigate_client_id')}
|
label=${localize('editor.frigate_client_id')}
|
||||||
.value=${this._config?.frigate_client_id || ''}
|
.value=${this._config?.frigate_client_id || ''}
|
||||||
|
|||||||
@@ -4,15 +4,15 @@
|
|||||||
"show_warning": "Show Warning",
|
"show_warning": "Show Warning",
|
||||||
"show_error": "Show Error",
|
"show_error": "Show Error",
|
||||||
"frigate_card": "Frigate Card",
|
"frigate_card": "Frigate Card",
|
||||||
"frigate_card_description": "A lovelace card for use with Frigate",
|
"frigate_card_description": "A Lovelace card for use with Frigate",
|
||||||
"no_snapshots": "No snapshots",
|
"no_snapshots": "No snapshots",
|
||||||
"no_clips": "No clips",
|
"no_clips": "No clips",
|
||||||
"no_snapshot": "No recent snapshot",
|
"no_snapshot": "No recent snapshot",
|
||||||
"no_clip": "No recent clip"
|
"no_clip": "No recent clip"
|
||||||
},
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
"required": "Required",
|
"basic": "Basic",
|
||||||
"required_secondary": "Required options for this card to function",
|
"basic_secondary": "Basic options for most users",
|
||||||
"optional": "Optional",
|
"optional": "Optional",
|
||||||
"optional_secondary": "Optional configuration to tweak card behavior",
|
"optional_secondary": "Optional configuration to tweak card behavior",
|
||||||
"appearance": "Appearance",
|
"appearance": "Appearance",
|
||||||
@@ -21,8 +21,8 @@
|
|||||||
"webrtc_secondary": "Optional WebRTC parameters",
|
"webrtc_secondary": "Optional WebRTC parameters",
|
||||||
"advanced": "Advanced",
|
"advanced": "Advanced",
|
||||||
"advanced_secondary": "Advanced options",
|
"advanced_secondary": "Advanced options",
|
||||||
"camera_entity": "Camera Entity (Required)",
|
"camera_entity": "Camera Entity (Optional)",
|
||||||
"frigate_camera_name": "Frigate camera name (Optional)",
|
"frigate_camera_name": "Frigate camera name (Optional, autodetected from entity)",
|
||||||
"default_view": "Default view (Optional)",
|
"default_view": "Default view (Optional)",
|
||||||
"frigate_client_id": "Frigate client id (Optional, for >1 Frigate server)",
|
"frigate_client_id": "Frigate client id (Optional, for >1 Frigate server)",
|
||||||
"view_timeout": "View timeout (seconds)",
|
"view_timeout": "View timeout (seconds)",
|
||||||
@@ -92,10 +92,10 @@
|
|||||||
"unknown": "Unknown error",
|
"unknown": "Unknown error",
|
||||||
"troubleshooting": "Check troubleshooting",
|
"troubleshooting": "Check troubleshooting",
|
||||||
"could_not_resolve": "Could not resolve media URL",
|
"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",
|
"invalid_configuration": "Invalid configuration",
|
||||||
"missing_webrtc": "WebRTC component not found",
|
"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",
|
"could_not_render_elements": "Could not render picture elements",
|
||||||
"invalid_elements_config": "Invalid picture elements configuration"
|
"invalid_elements_config": "Invalid picture elements configuration"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -244,7 +244,8 @@ const pictureElementsSchema = pictureElementSchema.array().optional();
|
|||||||
export type PictureElements = z.infer<typeof pictureElementsSchema>;
|
export type PictureElements = z.infer<typeof pictureElementsSchema>;
|
||||||
|
|
||||||
export const frigateCardConfigSchema = z.object({
|
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).
|
// No URL validation to allow relative URLs within HA (e.g. addons).
|
||||||
frigate_url: z.string().optional(),
|
frigate_url: z.string().optional(),
|
||||||
frigate_client_id: z.string().optional().default('frigate'),
|
frigate_client_id: z.string().optional().default('frigate'),
|
||||||
|
|||||||
Reference in New Issue
Block a user