Add live preload option.
This commit is contained in:
@@ -80,6 +80,7 @@ lovelace:
|
|||||||
| `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. |
|
||||||
| `autoplay_clip` | `false` | Whether or not to autoplay clips in the 'clip' [view](#views). Clips manually chosen in the clips gallery will still autoplay.|
|
| `autoplay_clip` | `false` | Whether or not to autoplay clips in the 'clip' [view](#views). Clips manually chosen in the clips gallery will still autoplay.|
|
||||||
|
| `live_preload` | `false` | Whether or not to preload the live view. Preloading causes the live view to render in the background so it's instantly available when requested. This consumes additional network/CPU resources continually.|
|
||||||
|
|
||||||
|
|
||||||
#### Live Provider
|
#### Live Provider
|
||||||
|
|||||||
+50
-25
@@ -33,10 +33,7 @@ import type {
|
|||||||
import { CARD_VERSION, REPO_URL } from './const';
|
import { CARD_VERSION, REPO_URL } 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 { homeAssistantWSRequest, shouldUpdateBasedOnHass } from './common';
|
||||||
homeAssistantWSRequest,
|
|
||||||
shouldUpdateBasedOnHass,
|
|
||||||
} from './common';
|
|
||||||
import { localize } from './localize/localize';
|
import { localize } from './localize/localize';
|
||||||
import { renderMessage, renderProgressIndicator } from './components/message';
|
import { renderMessage, renderProgressIndicator } from './components/message';
|
||||||
|
|
||||||
@@ -108,7 +105,7 @@ export class FrigateCard extends LitElement {
|
|||||||
_menu!: FrigateCardMenu;
|
_menu!: FrigateCardMenu;
|
||||||
|
|
||||||
@query('frigate-card-elements')
|
@query('frigate-card-elements')
|
||||||
_elements!: FrigateCardElements;
|
_elements?: FrigateCardElements;
|
||||||
|
|
||||||
// Whether or not media is actively playing (live or clip).
|
// Whether or not media is actively playing (live or clip).
|
||||||
protected _mediaPlaying = false;
|
protected _mediaPlaying = false;
|
||||||
@@ -122,7 +119,11 @@ export class FrigateCard extends LitElement {
|
|||||||
|
|
||||||
// The frigate camera name to use (may be manually specified or automatically
|
// The frigate camera name to use (may be manually specified or automatically
|
||||||
// derived).
|
// derived).
|
||||||
protected _frigateCameraName: string | null = null;
|
// Values:
|
||||||
|
// - string: Camera name on the Frigate backend.
|
||||||
|
// - null: Attempted to find name, but failed.
|
||||||
|
// - undefined: Have not yet attempted to find name.
|
||||||
|
protected _frigateCameraName?: string | null;
|
||||||
|
|
||||||
// Error/info message to render.
|
// Error/info message to render.
|
||||||
protected _message: Message | null = null;
|
protected _message: Message | null = null;
|
||||||
@@ -153,7 +154,7 @@ export class FrigateCard extends LitElement {
|
|||||||
_hass: HomeAssistant,
|
_hass: HomeAssistant,
|
||||||
entities: string[],
|
entities: string[],
|
||||||
): FrigateCardConfig {
|
): FrigateCardConfig {
|
||||||
const cameraEntity = entities.find(element => element.startsWith('camera.'));
|
const cameraEntity = entities.find((element) => element.startsWith('camera.'));
|
||||||
return {
|
return {
|
||||||
camera_entity: cameraEntity,
|
camera_entity: cameraEntity,
|
||||||
} as FrigateCardConfig;
|
} as FrigateCardConfig;
|
||||||
@@ -297,7 +298,7 @@ export class FrigateCard extends LitElement {
|
|||||||
getLovelace().setEditMode(true);
|
getLovelace().setEditMode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._frigateCameraName = null;
|
this._frigateCameraName = undefined;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
|
||||||
this._entitiesToMonitor = this.config.update_entities || [];
|
this._entitiesToMonitor = this.config.update_entities || [];
|
||||||
@@ -441,11 +442,13 @@ export class FrigateCard extends LitElement {
|
|||||||
this._mediaPlaying = false;
|
this._mediaPlaying = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _setMessageAndUpdate(message: Message): void {
|
protected _setMessageAndUpdate(message: Message, skipUpdate?: boolean): void {
|
||||||
// Register the first message, or prioritize errors if there's pre-render competition.
|
// Register the first message, or prioritize errors if there's pre-render competition.
|
||||||
if (!this._message || (message.type == 'error' && this._message.type != 'error')) {
|
if (!this._message || (message.type == 'error' && this._message.type != 'error')) {
|
||||||
this._message = message;
|
this._message = message;
|
||||||
this.requestUpdate();
|
if (!skipUpdate) {
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,51 +584,70 @@ export class FrigateCard extends LitElement {
|
|||||||
${this.config.menu_mode == 'above' ? this._renderMenu() : ''}
|
${this.config.menu_mode == 'above' ? this._renderMenu() : ''}
|
||||||
<div class="container outer" style="${styleMap(outerStyle)}">
|
<div class="container outer" style="${styleMap(outerStyle)}">
|
||||||
<div class="${classMap(contentClasses)}" style="${styleMap(innerStyle)}">
|
<div class="${classMap(contentClasses)}" style="${styleMap(innerStyle)}">
|
||||||
${this._message
|
${this._frigateCameraName == undefined
|
||||||
? renderMessage(this._message)
|
? until(
|
||||||
: until(this._render(), renderProgressIndicator())}
|
(async () => {
|
||||||
|
this._frigateCameraName = await this._getFrigateCameraName();
|
||||||
|
return this._render();
|
||||||
|
})(),
|
||||||
|
renderProgressIndicator(),
|
||||||
|
)
|
||||||
|
: this._render()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${this.config.menu_mode != 'above' ? this._renderMenu() : ''}
|
${this.config.menu_mode != 'above' ? this._renderMenu() : ''}
|
||||||
</ha-card>`;
|
</ha-card>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async _render(): Promise<TemplateResult | void> {
|
protected _render(): TemplateResult | void {
|
||||||
if (!this._frigateCameraName) {
|
|
||||||
this._frigateCameraName = await this._getFrigateCameraName();
|
|
||||||
}
|
|
||||||
const mediaQueryParameters = this._getBrowseMediaQueryParameters();
|
const mediaQueryParameters = this._getBrowseMediaQueryParameters();
|
||||||
if (!this._hass || !this._frigateCameraName || !mediaQueryParameters) {
|
if (!this._hass || !this._frigateCameraName || !mediaQueryParameters) {
|
||||||
return this._setMessageAndUpdate({
|
this._setMessageAndUpdate(
|
||||||
message: localize('error.no_frigate_camera_name'),
|
{
|
||||||
type: 'error',
|
message: localize('error.no_frigate_camera_name'),
|
||||||
});
|
type: 'error',
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const pictureElementsClasses = {
|
const pictureElementsClasses = {
|
||||||
'picture-elements': true,
|
'picture-elements': true,
|
||||||
gallery: this._view.isGalleryView(),
|
gallery: this._view.isGalleryView(),
|
||||||
};
|
};
|
||||||
|
const galleryClasses = {
|
||||||
|
hidden: this.config.live_preload && !this._view.isGalleryView(),
|
||||||
|
};
|
||||||
|
const viewerClasses = {
|
||||||
|
hidden:
|
||||||
|
this.config.live_preload && !['clip', 'snapshot'].includes(this._view.view),
|
||||||
|
};
|
||||||
|
const liveClasses = {
|
||||||
|
hidden: this.config.live_preload && this._view.view != 'live',
|
||||||
|
};
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="${classMap(pictureElementsClasses)}">
|
<div class="${classMap(pictureElementsClasses)}">
|
||||||
${this._view.is('clips') || this._view.is('snapshots')
|
${this._message ? renderMessage(this._message) : ``}
|
||||||
|
${!this._message && this._view.isGalleryView()
|
||||||
? html` <frigate-card-gallery
|
? html` <frigate-card-gallery
|
||||||
.hass=${this._hass}
|
.hass=${this._hass}
|
||||||
.view=${this._view}
|
.view=${this._view}
|
||||||
.browseMediaQueryParameters=${mediaQueryParameters}
|
.browseMediaQueryParameters=${mediaQueryParameters}
|
||||||
|
class="${classMap(galleryClasses)}"
|
||||||
@frigate-card:change-view=${this._changeViewHandler}
|
@frigate-card:change-view=${this._changeViewHandler}
|
||||||
@frigate-card:message=${this._messageHandler}
|
@frigate-card:message=${this._messageHandler}
|
||||||
>
|
>
|
||||||
</frigate-card-gallery>`
|
</frigate-card-gallery>`
|
||||||
: ``}
|
: ``}
|
||||||
${this._view.is('clip') || this._view.is('snapshot')
|
${!this._message && (this._view.is('clip') || this._view.is('snapshot'))
|
||||||
? html` <frigate-card-viewer
|
? html` <frigate-card-viewer
|
||||||
.hass=${this._hass}
|
.hass=${this._hass}
|
||||||
.view=${this._view}
|
.view=${this._view}
|
||||||
.browseMediaQueryParameters=${mediaQueryParameters}
|
.browseMediaQueryParameters=${mediaQueryParameters}
|
||||||
.nextPreviousControlStyle=${this.config.controls?.nextprev ?? 'thumbnails'}
|
.nextPreviousControlStyle=${this.config.controls?.nextprev ?? 'thumbnails'}
|
||||||
.autoplayClip=${this.config.autoplay_clip}
|
.autoplayClip=${this.config.autoplay_clip}
|
||||||
|
class="${classMap(viewerClasses)}"
|
||||||
@frigate-card:change-view=${this._changeViewHandler}
|
@frigate-card:change-view=${this._changeViewHandler}
|
||||||
@frigate-card:media-load=${this._mediaLoadHandler}
|
@frigate-card:media-load=${this._mediaLoadHandler}
|
||||||
@frigate-card:pause=${this._pauseHandler}
|
@frigate-card:pause=${this._pauseHandler}
|
||||||
@@ -634,12 +656,15 @@ export class FrigateCard extends LitElement {
|
|||||||
>
|
>
|
||||||
</frigate-card-viewer>`
|
</frigate-card-viewer>`
|
||||||
: ``}
|
: ``}
|
||||||
${this._view.is('live')
|
<!-- Note the subtle difference in condition below vs the other views in order
|
||||||
|
to always render the live view for live_preload mode -->
|
||||||
|
${(!this._message && this._view.is('live')) || this.config.live_preload
|
||||||
? html`
|
? html`
|
||||||
<frigate-card-live
|
<frigate-card-live
|
||||||
.hass=${this._hass}
|
.hass=${this._hass}
|
||||||
.config=${this.config}
|
.config=${this.config}
|
||||||
.frigateCameraName=${this._frigateCameraName}
|
.frigateCameraName=${this._frigateCameraName}
|
||||||
|
class="${classMap(liveClasses)}"
|
||||||
@frigate-card:media-load=${this._mediaLoadHandler}
|
@frigate-card:media-load=${this._mediaLoadHandler}
|
||||||
@frigate-card:pause=${this._pauseHandler}
|
@frigate-card:pause=${this._pauseHandler}
|
||||||
@frigate-card:play=${this._playHandler}
|
@frigate-card:play=${this._playHandler}
|
||||||
@@ -648,7 +673,7 @@ export class FrigateCard extends LitElement {
|
|||||||
</frigate-card-live>
|
</frigate-card-live>
|
||||||
`
|
`
|
||||||
: ``}
|
: ``}
|
||||||
${this.config.elements
|
${!this._message && this.config.elements
|
||||||
? html`
|
? html`
|
||||||
<frigate-card-elements
|
<frigate-card-elements
|
||||||
.hass=${this._hass}
|
.hass=${this._hass}
|
||||||
|
|||||||
+72
-66
@@ -22,7 +22,7 @@ import JSMpeg from '@cycjimmy/jsmpeg-player';
|
|||||||
import liveStyle from '../scss/live.scss';
|
import liveStyle from '../scss/live.scss';
|
||||||
|
|
||||||
@customElement('frigate-card-live')
|
@customElement('frigate-card-live')
|
||||||
export class FrigateCardViewer extends LitElement {
|
export class FrigateCardLive extends LitElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected hass!: HomeAssistant & ExtendedHomeAssistant;
|
protected hass!: HomeAssistant & ExtendedHomeAssistant;
|
||||||
|
|
||||||
@@ -33,10 +33,6 @@ export class FrigateCardViewer extends LitElement {
|
|||||||
protected frigateCameraName!: string;
|
protected frigateCameraName!: string;
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
return html`${until(this._render(), renderProgressIndicator())}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async _render(): Promise<TemplateResult> {
|
|
||||||
return html` ${this.config.live_provider == 'frigate'
|
return html` ${this.config.live_provider == 'frigate'
|
||||||
? html` <frigate-card-live-frigate
|
? html` <frigate-card-live-frigate
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@@ -63,7 +59,7 @@ export class FrigateCardViewer extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@customElement('frigate-card-live-frigate')
|
@customElement('frigate-card-live-frigate')
|
||||||
export class FrigateCardViewerFrigate extends LitElement {
|
export class FrigateCardLiveFrigate extends LitElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected hass!: HomeAssistant & ExtendedHomeAssistant;
|
protected hass!: HomeAssistant & ExtendedHomeAssistant;
|
||||||
|
|
||||||
@@ -95,13 +91,11 @@ export class FrigateCardViewerFrigate extends LitElement {
|
|||||||
// Create a wrapper for the WebRTC element
|
// Create a wrapper for the WebRTC element
|
||||||
// - https://github.com/AlexxIT/WebRTC
|
// - https://github.com/AlexxIT/WebRTC
|
||||||
@customElement('frigate-card-live-webrtc')
|
@customElement('frigate-card-live-webrtc')
|
||||||
export class FrigateCardViewerWebRTC extends LitElement {
|
export class FrigateCardLiveWebRTC extends LitElement {
|
||||||
@property({ attribute: false })
|
|
||||||
protected hass!: HomeAssistant & ExtendedHomeAssistant;
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected webRTCConfig!: Record<string, unknown>;
|
protected webRTCConfig!: Record<string, unknown>;
|
||||||
|
|
||||||
|
protected hass!: HomeAssistant & ExtendedHomeAssistant;
|
||||||
protected _webRTCElement: HTMLElement | null = null;
|
protected _webRTCElement: HTMLElement | null = null;
|
||||||
|
|
||||||
protected _createWebRTC(): TemplateResult | void {
|
protected _createWebRTC(): TemplateResult | void {
|
||||||
@@ -166,20 +160,17 @@ export class FrigateCardViewerWebRTC extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@customElement('frigate-card-live-jsmpeg')
|
@customElement('frigate-card-live-jsmpeg')
|
||||||
export class FrigateCardViewerJSMPEG extends LitElement {
|
export class FrigateCardLiveJSMPEG extends LitElement {
|
||||||
@property({ attribute: false })
|
|
||||||
protected hass!: HomeAssistant & ExtendedHomeAssistant;
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected cameraName!: string;
|
protected cameraName!: string;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected clientId!: string;
|
protected clientId!: string;
|
||||||
|
|
||||||
protected _jsmpegCanvasElement: HTMLCanvasElement | null = null;
|
protected hass!: HomeAssistant & ExtendedHomeAssistant;
|
||||||
|
protected _jsmpegCanvasElement?: HTMLCanvasElement;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
protected _jsmpegVideoPlayer?;
|
||||||
protected _jsmpegVideoPlayer: any | null = null;
|
protected _jsmpegURL?: string | null;
|
||||||
|
|
||||||
protected async _getURL(): Promise<string | null> {
|
protected async _getURL(): Promise<string | null> {
|
||||||
if (!this.hass) {
|
if (!this.hass) {
|
||||||
@@ -202,62 +193,77 @@ export class FrigateCardViewerJSMPEG extends LitElement {
|
|||||||
return url.replace(/^http/i, 'ws');
|
return url.replace(/^http/i, 'ws');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected async _createJSMPEGPlayer(): Promise<void> {
|
||||||
return html`${until(this._render(), renderProgressIndicator())}`;
|
let videoDecoded = false;
|
||||||
|
return new Promise<void>((resolve) => {
|
||||||
|
this._jsmpegVideoPlayer = new JSMpeg.VideoElement(
|
||||||
|
this,
|
||||||
|
this._jsmpegURL,
|
||||||
|
{
|
||||||
|
preserveDrawingBuffer: true,
|
||||||
|
canvas: this._jsmpegCanvasElement,
|
||||||
|
hooks: {
|
||||||
|
// Don't resolve the promise until it's playing to minimize the
|
||||||
|
// amount of time the canvas is empty (and show the spinner
|
||||||
|
// instead).
|
||||||
|
play: () => {
|
||||||
|
dispatchPlayEvent(this);
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
pause: () => {
|
||||||
|
dispatchPauseEvent(this);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocols: [],
|
||||||
|
videoBufferSize: 1024 * 1024 * 4,
|
||||||
|
onVideoDecode: () => {
|
||||||
|
// This is the only callback that is called after the dimensions
|
||||||
|
// are available. It's called on every frame decode, so just
|
||||||
|
// ignore any subsequent calls.
|
||||||
|
if (!videoDecoded && this._jsmpegCanvasElement) {
|
||||||
|
videoDecoded = true;
|
||||||
|
dispatchMediaLoadEvent(this, this._jsmpegCanvasElement);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async _render(): Promise<TemplateResult | void> {
|
protected render(): TemplateResult | void {
|
||||||
if (!this._jsmpegCanvasElement) {
|
if (!this._jsmpegCanvasElement) {
|
||||||
this._jsmpegCanvasElement = document.createElement('canvas');
|
this._jsmpegCanvasElement = document.createElement('canvas');
|
||||||
this._jsmpegCanvasElement.className = 'media';
|
this._jsmpegCanvasElement.className = 'media';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._jsmpegVideoPlayer) {
|
if (this._jsmpegURL === undefined) {
|
||||||
const jsmpeg_url = await this._getURL();
|
return html`${until(
|
||||||
|
(async () => {
|
||||||
if (!jsmpeg_url) {
|
this._jsmpegURL = await this._getURL();
|
||||||
return dispatchErrorMessageEvent(
|
this.requestUpdate();
|
||||||
this,
|
})(),
|
||||||
'Could not retrieve or sign JSMPEG websocket path',
|
renderProgressIndicator(),
|
||||||
);
|
)}`;
|
||||||
}
|
|
||||||
|
|
||||||
let videoDecoded = false;
|
|
||||||
return new Promise<TemplateResult>((resolve) => {
|
|
||||||
this._jsmpegVideoPlayer = new JSMpeg.VideoElement(
|
|
||||||
this,
|
|
||||||
jsmpeg_url,
|
|
||||||
{
|
|
||||||
canvas: this._jsmpegCanvasElement,
|
|
||||||
hooks: {
|
|
||||||
// Don't resolve the promise until it's playing to minimize the
|
|
||||||
// amount of time the canvas is empty (and show the spinner
|
|
||||||
// instead).
|
|
||||||
play: () => {
|
|
||||||
dispatchPlayEvent(this);
|
|
||||||
resolve(html`${this._jsmpegCanvasElement}`);
|
|
||||||
},
|
|
||||||
pause: () => {
|
|
||||||
dispatchPauseEvent(this);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
protocols: [],
|
|
||||||
videoBufferSize: 1024 * 1024 * 4,
|
|
||||||
onVideoDecode: () => {
|
|
||||||
// This is the only callback that is called after the dimensions
|
|
||||||
// are available. It's called on every frame decode, so just
|
|
||||||
// ignore any subsequent calls.
|
|
||||||
if (!videoDecoded && this._jsmpegCanvasElement) {
|
|
||||||
videoDecoded = true;
|
|
||||||
dispatchMediaLoadEvent(this, this._jsmpegCanvasElement);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
if (!this._jsmpegURL) {
|
||||||
|
return dispatchErrorMessageEvent(
|
||||||
|
this,
|
||||||
|
'Could not retrieve or sign JSMPEG websocket path',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._jsmpegVideoPlayer) {
|
||||||
|
return html`${until(
|
||||||
|
(async () => {
|
||||||
|
await this._createJSMPEGPlayer();
|
||||||
|
this.requestUpdate();
|
||||||
|
})(),
|
||||||
|
renderProgressIndicator(),
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
|
||||||
return html`${this._jsmpegCanvasElement}`;
|
return html`${this._jsmpegCanvasElement}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -255,6 +255,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
|
<ha-formfield .label=${localize('editor.live_preload')}>
|
||||||
|
<ha-switch
|
||||||
|
.checked=${this._config?.live_preload === true}
|
||||||
|
.configValue=${'live_preload'}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
</div>`
|
</div>`
|
||||||
: ''}
|
: ''}
|
||||||
<div class="option" @click=${this._toggleOption} .option=${'appearance'}>
|
<div class="option" @click=${this._toggleOption} .option=${'appearance'}>
|
||||||
|
|||||||
@@ -32,7 +32,8 @@
|
|||||||
"show_button": "Show button",
|
"show_button": "Show button",
|
||||||
"zone": "Zone",
|
"zone": "Zone",
|
||||||
"label": "Frigate label/object filter (Optional)",
|
"label": "Frigate label/object filter (Optional)",
|
||||||
"live_provider": "Live view provider (Optional)"
|
"live_provider": "Live view provider (Optional)",
|
||||||
|
"live_preload": "Preload live view (Optional)"
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"frigate": "Frigate Menu / Default View",
|
"frigate": "Frigate Menu / Default View",
|
||||||
|
|||||||
@@ -75,6 +75,9 @@ frigate-card-gallery, frigate-card-viewer, frigate-card-live, frigate-card-messa
|
|||||||
frigate-card-gallery {
|
frigate-card-gallery {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
frigate-card-gallery.hidden,frigate-card-viewer.hidden,frigate-card-live.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
// Browsers will reject invalid whole CSS selectors if one selector is bad, so
|
// Browsers will reject invalid whole CSS selectors if one selector is bad, so
|
||||||
// need to use mixin here instead of just comma-separated selectors.
|
// need to use mixin here instead of just comma-separated selectors.
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
:host {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -262,6 +262,7 @@ export const frigateCardConfigSchema = z.object({
|
|||||||
.optional()
|
.optional()
|
||||||
.default(180),
|
.default(180),
|
||||||
live_provider: z.enum(LIVE_PROVIDERS).default('frigate'),
|
live_provider: z.enum(LIVE_PROVIDERS).default('frigate'),
|
||||||
|
live_preload: z.boolean().default(false),
|
||||||
webrtc: z
|
webrtc: z
|
||||||
.object({
|
.object({
|
||||||
entity: z.string().optional(),
|
entity: z.string().optional(),
|
||||||
|
|||||||
Reference in New Issue
Block a user