Initial configuration reorganization.
This commit is contained in:
+52
-56
@@ -192,72 +192,72 @@ export class FrigateCard extends LitElement {
|
|||||||
protected _getMenuButtons(): MenuButton[] {
|
protected _getMenuButtons(): MenuButton[] {
|
||||||
const buttons: MenuButton[] = [];
|
const buttons: MenuButton[] = [];
|
||||||
|
|
||||||
if (this.config.menu_buttons?.frigate ?? true) {
|
if (this.config.menu.buttons.frigate) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'internal-menu-icon',
|
type: 'internal-menu-icon',
|
||||||
tap_action: 'frigate',
|
tap_action: 'frigate',
|
||||||
title: localize('menu.frigate'),
|
title: localize('config.menu.buttons.frigate'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this.config.menu_buttons?.live ?? true) {
|
if (this.config.menu.buttons.live) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'internal-menu-icon',
|
type: 'internal-menu-icon',
|
||||||
tap_action: 'live',
|
tap_action: 'live',
|
||||||
title: localize('menu.live'),
|
title: localize('config.view.views.live'),
|
||||||
icon: 'mdi:cctv',
|
icon: 'mdi:cctv',
|
||||||
emphasize: this._view.is('live'),
|
emphasize: this._view.is('live'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this.config.menu_buttons?.clips ?? true) {
|
if (this.config.menu.buttons.clips) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'internal-menu-icon',
|
type: 'internal-menu-icon',
|
||||||
tap_action: 'clips',
|
tap_action: 'clips',
|
||||||
hold_action: 'clip',
|
hold_action: 'clip',
|
||||||
title: localize('menu.clips'),
|
title: localize('config.view.views.clips'),
|
||||||
icon: 'mdi:filmstrip',
|
icon: 'mdi:filmstrip',
|
||||||
emphasize: this._view.is('clips'),
|
emphasize: this._view.is('clips'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this.config.menu_buttons?.snapshots ?? true) {
|
if (this.config.menu.buttons.snapshots) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'internal-menu-icon',
|
type: 'internal-menu-icon',
|
||||||
tap_action: 'snapshots',
|
tap_action: 'snapshots',
|
||||||
hold_action: 'snapshot',
|
hold_action: 'snapshot',
|
||||||
title: localize('menu.snapshots'),
|
title: localize('config.view.views.snapshots'),
|
||||||
icon: 'mdi:camera',
|
icon: 'mdi:camera',
|
||||||
emphasize: this._view.is('snapshots'),
|
emphasize: this._view.is('snapshots'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this.config.menu_buttons?.image ?? false) {
|
if (this.config.menu.buttons.image) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'internal-menu-icon',
|
type: 'internal-menu-icon',
|
||||||
tap_action: 'image',
|
tap_action: 'image',
|
||||||
title: localize('menu.image'),
|
title: localize('config.view.views.image'),
|
||||||
icon: 'mdi:image',
|
icon: 'mdi:image',
|
||||||
emphasize: this._view.is('image'),
|
emphasize: this._view.is('image'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this._view.isViewerView() && (this.config.menu_buttons?.download ?? true)) {
|
if (this.config.menu.buttons.download && this._view.isViewerView()) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'internal-menu-icon',
|
type: 'internal-menu-icon',
|
||||||
tap_action: 'download',
|
tap_action: 'download',
|
||||||
title: localize('menu.download'),
|
title: localize('config.menu.buttons.download'),
|
||||||
icon: 'mdi:download',
|
icon: 'mdi:download',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ((this.config.menu_buttons?.frigate_ui ?? true) && this.config.frigate_url) {
|
if (this.config.menu.buttons.frigate_ui && this.config.frigate.url) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'internal-menu-icon',
|
type: 'internal-menu-icon',
|
||||||
tap_action: 'frigate_ui',
|
tap_action: 'frigate_ui',
|
||||||
title: localize('menu.frigate_ui'),
|
title: localize('config.menu.buttons.frigate_ui'),
|
||||||
icon: 'mdi:web',
|
icon: 'mdi:web',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ((this.config.menu_buttons?.fullscreen ?? true) && screenfull.isEnabled) {
|
if (this.config.menu.buttons.fullscreen && screenfull.isEnabled) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
type: 'internal-menu-icon',
|
type: 'internal-menu-icon',
|
||||||
tap_action: 'fullscreen',
|
tap_action: 'fullscreen',
|
||||||
title: localize('menu.fullscreen'),
|
title: localize('config.menu.buttons.fullscreen'),
|
||||||
icon: screenfull.isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen',
|
icon: screenfull.isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -300,8 +300,8 @@ export class FrigateCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Option 1: Name specified in config -> done!
|
// Option 1: Name specified in config -> done!
|
||||||
if (this.config.frigate_camera_name) {
|
if (this.config.frigate.camera_name) {
|
||||||
return this.config.frigate_camera_name;
|
return this.config.frigate.camera_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.config.camera_entity) {
|
if (this.config.camera_entity) {
|
||||||
@@ -436,7 +436,7 @@ export class FrigateCard extends LitElement {
|
|||||||
this._message = null;
|
this._message = null;
|
||||||
|
|
||||||
if (view === undefined) {
|
if (view === undefined) {
|
||||||
this._view = new View({ view: this.config.view_default });
|
this._view = new View({ view: this.config.view.default });
|
||||||
} else {
|
} else {
|
||||||
this._view = view;
|
this._view = view;
|
||||||
}
|
}
|
||||||
@@ -505,7 +505,7 @@ export class FrigateCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const path =
|
const path =
|
||||||
`/api/frigate/${this.config.frigate_client_id}` +
|
`/api/frigate/${this.config.frigate.client_id}` +
|
||||||
`/notifications/${event_id}/` +
|
`/notifications/${event_id}/` +
|
||||||
`${this._view.isClipRelatedView() ? 'clip.mp4' : 'snapshot.jpg'}` +
|
`${this._view.isClipRelatedView() ? 'clip.mp4' : 'snapshot.jpg'}` +
|
||||||
`?download=true`;
|
`?download=true`;
|
||||||
@@ -595,22 +595,22 @@ export class FrigateCard extends LitElement {
|
|||||||
* @returns The URL or null if unavailable.
|
* @returns The URL or null if unavailable.
|
||||||
*/
|
*/
|
||||||
protected _getFrigateURLFromContext(): string | null {
|
protected _getFrigateURLFromContext(): string | null {
|
||||||
if (!this.config.frigate_url) {
|
if (!this.config.frigate.url) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!this._frigateCameraName) {
|
if (!this._frigateCameraName) {
|
||||||
return this.config.frigate_url;
|
return this.config.frigate.url;
|
||||||
} else if (this._view.is('live')) {
|
} else if (this._view.is('live')) {
|
||||||
return `${this.config.frigate_url}/cameras/${this._frigateCameraName}`;
|
return `${this.config.frigate.url}/cameras/${this._frigateCameraName}`;
|
||||||
}
|
}
|
||||||
return `${this.config.frigate_url}/events?camera=${this._frigateCameraName}`;
|
return `${this.config.frigate.url}/events?camera=${this._frigateCameraName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle interaction with the card.
|
* Handle interaction with the card.
|
||||||
*/
|
*/
|
||||||
protected _interactionHandler(): void {
|
protected _interactionHandler(): void {
|
||||||
if (!this.config.view_timeout) {
|
if (!this.config.view.timeout) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this._interactionTimerID) {
|
if (this._interactionTimerID) {
|
||||||
@@ -619,7 +619,7 @@ export class FrigateCard extends LitElement {
|
|||||||
this._interactionTimerID = window.setTimeout(() => {
|
this._interactionTimerID = window.setTimeout(() => {
|
||||||
this._interactionTimerID = null;
|
this._interactionTimerID = null;
|
||||||
this._changeView();
|
this._changeView();
|
||||||
}, this.config.view_timeout * 1000);
|
}, this.config.view.timeout * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -628,15 +628,14 @@ export class FrigateCard extends LitElement {
|
|||||||
*/
|
*/
|
||||||
protected _renderMenu(): TemplateResult | void {
|
protected _renderMenu(): TemplateResult | void {
|
||||||
const classes = {
|
const classes = {
|
||||||
'hover-menu': this.config.menu_mode.startsWith('hover-'),
|
'hover-menu': this.config.menu.mode.startsWith('hover-'),
|
||||||
};
|
};
|
||||||
return html`
|
return html`
|
||||||
<frigate-card-menu
|
<frigate-card-menu
|
||||||
class="${classMap(classes)}"
|
|
||||||
.hass=${this._hass}
|
.hass=${this._hass}
|
||||||
.menuMode=${this.config.menu_mode}
|
.menuConfig=${this.config.menu}
|
||||||
.buttons=${this._getMenuButtons()}
|
.buttons=${this._getMenuButtons()}
|
||||||
.buttonSize=${this.config.menu_button_size}
|
class="${classMap(classes)}"
|
||||||
@frigate-card:menu-interaction=${this._menuInteractionHandler.bind(this)}
|
@frigate-card:menu-interaction=${this._menuInteractionHandler.bind(this)}
|
||||||
></frigate-card-menu>
|
></frigate-card-menu>
|
||||||
`;
|
`;
|
||||||
@@ -655,10 +654,10 @@ export class FrigateCard extends LitElement {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
mediaType: this._view.isClipRelatedView() ? 'clips' : 'snapshots',
|
mediaType: this._view.isClipRelatedView() ? 'clips' : 'snapshots',
|
||||||
clientId: this.config.frigate_client_id,
|
clientId: this.config.frigate.client_id,
|
||||||
cameraName: this._frigateCameraName,
|
cameraName: this._frigateCameraName,
|
||||||
label: this.config.label,
|
label: this.config.frigate.label,
|
||||||
zone: this.config.zone,
|
zone: this.config.frigate.zone,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -760,7 +759,7 @@ export class FrigateCard extends LitElement {
|
|||||||
* context.
|
* context.
|
||||||
*/
|
*/
|
||||||
protected _isAspectRatioEnforced(): boolean {
|
protected _isAspectRatioEnforced(): boolean {
|
||||||
const aspect_ratio_mode = this.config.dimensions?.aspect_ratio_mode ?? 'dynamic';
|
const aspectRatioMode = this.config.dimensions.aspect_ratio_mode;
|
||||||
|
|
||||||
// Do not artifically constrain aspect ratio if:
|
// Do not artifically constrain aspect ratio if:
|
||||||
// - It's fullscreen.
|
// - It's fullscreen.
|
||||||
@@ -769,8 +768,8 @@ export class FrigateCard extends LitElement {
|
|||||||
|
|
||||||
return !(
|
return !(
|
||||||
(screenfull.isEnabled && screenfull.isFullscreen) ||
|
(screenfull.isEnabled && screenfull.isFullscreen) ||
|
||||||
aspect_ratio_mode == 'unconstrained' ||
|
aspectRatioMode == 'unconstrained' ||
|
||||||
(aspect_ratio_mode == 'dynamic' && this._view.isMediaView())
|
(aspectRatioMode == 'dynamic' && this._view.isMediaView())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -784,14 +783,14 @@ export class FrigateCard extends LitElement {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const aspect_ratio_mode = this.config.dimensions?.aspect_ratio_mode ?? 'dynamic';
|
const aspectRatioMode = this.config.dimensions.aspect_ratio_mode;
|
||||||
if (aspect_ratio_mode == 'dynamic' && this._mediaShowInfo) {
|
if (aspectRatioMode == 'dynamic' && this._mediaShowInfo) {
|
||||||
return (this._mediaShowInfo.height / this._mediaShowInfo.width) * 100;
|
return (this._mediaShowInfo.height / this._mediaShowInfo.width) * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
const default_aspect_ratio = this.config.dimensions?.aspect_ratio;
|
const defaultAspectRatio = this.config.dimensions.aspect_ratio;
|
||||||
if (default_aspect_ratio) {
|
if (defaultAspectRatio) {
|
||||||
return (default_aspect_ratio[1] / default_aspect_ratio[0]) * 100;
|
return (defaultAspectRatio[1] / defaultAspectRatio[0]) * 100;
|
||||||
} else {
|
} else {
|
||||||
return (9 / 16) * 100;
|
return (9 / 16) * 100;
|
||||||
}
|
}
|
||||||
@@ -836,7 +835,7 @@ export class FrigateCard extends LitElement {
|
|||||||
window.innerWidth / window.innerHeight
|
window.innerWidth / window.innerHeight
|
||||||
) {
|
) {
|
||||||
// If the menu is outside the media (i.e. above/below) allow space for it.
|
// If the menu is outside the media (i.e. above/below) allow space for it.
|
||||||
const allowance = ['above', 'below'].includes(this.config.menu_mode)
|
const allowance = ['above', 'below'].includes(this.config.menu.mode)
|
||||||
? MENU_HEIGHT
|
? MENU_HEIGHT
|
||||||
: 0;
|
: 0;
|
||||||
innerStyle['max-width'] = `calc(${
|
innerStyle['max-width'] = `calc(${
|
||||||
@@ -850,7 +849,7 @@ export class FrigateCard extends LitElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return html` <ha-card @click=${this._interactionHandler}>
|
return html` <ha-card @click=${this._interactionHandler}>
|
||||||
${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._frigateCameraName == undefined
|
${this._frigateCameraName == undefined
|
||||||
@@ -864,7 +863,7 @@ export class FrigateCard extends LitElement {
|
|||||||
: this._render()}
|
: this._render()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${this.config.menu_mode != 'above' ? this._renderMenu() : ''}
|
${this.config.menu.mode != 'above' ? this._renderMenu() : ''}
|
||||||
</ha-card>`;
|
</ha-card>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -891,16 +890,16 @@ export class FrigateCard extends LitElement {
|
|||||||
gallery: this._view.isGalleryView(),
|
gallery: this._view.isGalleryView(),
|
||||||
};
|
};
|
||||||
const galleryClasses = {
|
const galleryClasses = {
|
||||||
hidden: this.config.live_preload && !this._view.isGalleryView(),
|
hidden: this.config.live.preload && !this._view.isGalleryView(),
|
||||||
};
|
};
|
||||||
const viewerClasses = {
|
const viewerClasses = {
|
||||||
hidden: this.config.live_preload && !this._view.isViewerView(),
|
hidden: this.config.live.preload && !this._view.isViewerView(),
|
||||||
};
|
};
|
||||||
const liveClasses = {
|
const liveClasses = {
|
||||||
hidden: this.config.live_preload && this._view.view != 'live',
|
hidden: this.config.live.preload && this._view.view != 'live',
|
||||||
};
|
};
|
||||||
const imageClasses = {
|
const imageClasses = {
|
||||||
hidden: this.config.live_preload && this._view.view != 'image',
|
hidden: this.config.live.preload && this._view.view != 'image',
|
||||||
};
|
};
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
@@ -908,7 +907,7 @@ export class FrigateCard extends LitElement {
|
|||||||
${this._message ? renderMessage(this._message) : ``}
|
${this._message ? renderMessage(this._message) : ``}
|
||||||
${!this._message && this._view.is('image')
|
${!this._message && this._view.is('image')
|
||||||
? html` <frigate-card-image
|
? html` <frigate-card-image
|
||||||
.image=${this.config.image}
|
.imageConfig=${this.config.image}
|
||||||
class="${classMap(imageClasses)}"
|
class="${classMap(imageClasses)}"
|
||||||
@frigate-card:media-show=${this._mediaShowHandler}
|
@frigate-card:media-show=${this._mediaShowHandler}
|
||||||
@frigate-card:message=${this._messageHandler}
|
@frigate-card:message=${this._messageHandler}
|
||||||
@@ -931,11 +930,8 @@ export class FrigateCard extends LitElement {
|
|||||||
.hass=${this._hass}
|
.hass=${this._hass}
|
||||||
.view=${this._view}
|
.view=${this._view}
|
||||||
.browseMediaQueryParameters=${mediaQueryParameters}
|
.browseMediaQueryParameters=${mediaQueryParameters}
|
||||||
.nextPreviousControlStyle=${this.config.controls?.nextprev ?? 'thumbnails'}
|
.viewerConfig=${this.config.event_viewer}
|
||||||
.nextPreviousControlSize=${this.config.controls?.nextprev_size}
|
|
||||||
.autoplayClip=${this.config.autoplay_clip}
|
|
||||||
.resolvedMediaCache=${this._resolvedMediaCache}
|
.resolvedMediaCache=${this._resolvedMediaCache}
|
||||||
.lazyLoad=${this.config.event_viewer?.lazy_load ?? true}
|
|
||||||
class="${classMap(viewerClasses)}"
|
class="${classMap(viewerClasses)}"
|
||||||
@frigate-card:change-view=${this._changeViewHandler}
|
@frigate-card:change-view=${this._changeViewHandler}
|
||||||
@frigate-card:media-show=${this._mediaShowHandler}
|
@frigate-card:media-show=${this._mediaShowHandler}
|
||||||
@@ -947,14 +943,14 @@ export class FrigateCard extends LitElement {
|
|||||||
: ``}
|
: ``}
|
||||||
${
|
${
|
||||||
// Note the subtle difference in condition below vs the other views in order
|
// Note the subtle difference in condition below vs the other views in order
|
||||||
// to always render the live view for live_preload mode.
|
// to always render the live view for live.preload mode.
|
||||||
(!this._message && this._view.is('live')) || this.config.live_preload
|
(!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}
|
||||||
.preload=${this.config.live_preload && !this._view.is('live')}
|
.preload=${this.config.live.preload && !this._view.is('live')}
|
||||||
class="${classMap(liveClasses)}"
|
class="${classMap(liveClasses)}"
|
||||||
@frigate-card:media-show=${this._mediaShowHandler}
|
@frigate-card:media-show=${this._mediaShowHandler}
|
||||||
@frigate-card:pause=${this._pauseHandler}
|
@frigate-card:pause=${this._pauseHandler}
|
||||||
|
|||||||
+2
-2
@@ -214,8 +214,8 @@ export function dispatchErrorMessageEvent(element: HTMLElement, message: string)
|
|||||||
* @returns A boolean indicating whether or not to allow an update.
|
* @returns A boolean indicating whether or not to allow an update.
|
||||||
*/
|
*/
|
||||||
export function shouldUpdateBasedOnHass(
|
export function shouldUpdateBasedOnHass(
|
||||||
newHass: HomeAssistant | null,
|
newHass: HomeAssistant | undefined | null,
|
||||||
oldHass: HomeAssistant | undefined,
|
oldHass: HomeAssistant | undefined | null,
|
||||||
entities: string[] | null,
|
entities: string[] | null,
|
||||||
): boolean {
|
): boolean {
|
||||||
if (!newHass || !entities) {
|
if (!newHass || !entities) {
|
||||||
|
|||||||
@@ -3,17 +3,19 @@ import { customElement, property } from 'lit/decorators.js';
|
|||||||
|
|
||||||
import { dispatchMediaShowEvent } from '../common.js';
|
import { dispatchMediaShowEvent } from '../common.js';
|
||||||
|
|
||||||
import imageStyle from '../scss/image.scss';
|
import type { ImageViewConfig } from '../types.js';
|
||||||
import defaultImage from '../images/frigate-bird-in-sky.jpg'
|
import defaultImage from '../images/frigate-bird-in-sky.jpg'
|
||||||
|
|
||||||
|
import imageStyle from '../scss/image.scss';
|
||||||
|
|
||||||
@customElement('frigate-card-image')
|
@customElement('frigate-card-image')
|
||||||
export class FrigateCardImage extends LitElement {
|
export class FrigateCardImage extends LitElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected image?: string;
|
protected imageConfig?: ImageViewConfig;
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
return html` <img
|
return html` <img
|
||||||
src=${this.image || defaultImage}
|
src=${this.imageConfig?.src || defaultImage}
|
||||||
@load=${(e) => {
|
@load=${(e) => {
|
||||||
dispatchMediaShowEvent(this, e);
|
dispatchMediaShowEvent(this, e);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
||||||
import type { ExtendedHomeAssistant, FrigateCardConfig, MediaShowInfo } from '../types.js';
|
import type { ExtendedHomeAssistant, FrigateCardConfig, MediaShowInfo, WebRTCConfig } from '../types.js';
|
||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from 'custom-card-helpers';
|
||||||
import { customElement, property } from 'lit/decorators.js';
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
import { until } from 'lit/directives/until.js';
|
import { until } from 'lit/directives/until.js';
|
||||||
@@ -75,24 +75,24 @@ export class FrigateCardLive extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
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}
|
||||||
.cameraEntity=${this.config.camera_entity}
|
.cameraEntity=${this.config.camera_entity}
|
||||||
@frigate-card:media-show=${this._mediaShowHandler}
|
@frigate-card:media-show=${this._mediaShowHandler}
|
||||||
>
|
>
|
||||||
</frigate-card-live-frigate>`
|
</frigate-card-live-frigate>`
|
||||||
: this.config.live_provider == 'webrtc'
|
: this.config.live.provider == 'webrtc'
|
||||||
? html`<frigate-card-live-webrtc
|
? html`<frigate-card-live-webrtc
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.webRTCConfig=${this.config.webrtc || {}}
|
.webRTCConfig=${this.config.live.webrtc || {}}
|
||||||
@frigate-card:media-show=${this._mediaShowHandler}
|
@frigate-card:media-show=${this._mediaShowHandler}
|
||||||
>
|
>
|
||||||
</frigate-card-live-webrtc>`
|
</frigate-card-live-webrtc>`
|
||||||
: html` <frigate-card-live-jsmpeg
|
: html` <frigate-card-live-jsmpeg
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.cameraName=${this.frigateCameraName}
|
.cameraName=${this.frigateCameraName}
|
||||||
.clientId=${this.config.frigate_client_id}
|
.clientId=${this.config.frigate.client_id}
|
||||||
@frigate-card:media-show=${this._mediaShowHandler}
|
@frigate-card:media-show=${this._mediaShowHandler}
|
||||||
>
|
>
|
||||||
</frigate-card-live-jsmpeg>`}`;
|
</frigate-card-live-jsmpeg>`}`;
|
||||||
@@ -152,7 +152,7 @@ export class FrigateCardLiveFrigate extends LitElement {
|
|||||||
@customElement('frigate-card-live-webrtc')
|
@customElement('frigate-card-live-webrtc')
|
||||||
export class FrigateCardLiveWebRTC extends LitElement {
|
export class FrigateCardLiveWebRTC extends LitElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected webRTCConfig?: Record<string, unknown>;
|
protected webRTCConfig?: WebRTCConfig;
|
||||||
|
|
||||||
protected hass?: HomeAssistant & ExtendedHomeAssistant;
|
protected hass?: HomeAssistant & ExtendedHomeAssistant;
|
||||||
protected _webRTCElement: HTMLElement | null = null;
|
protected _webRTCElement: HTMLElement | null = null;
|
||||||
|
|||||||
+37
-32
@@ -16,8 +16,8 @@ import { actionHandler } from '../action-handler-directive.js';
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
ExtendedHomeAssistant,
|
ExtendedHomeAssistant,
|
||||||
FrigateMenuMode,
|
|
||||||
MenuButton,
|
MenuButton,
|
||||||
|
MenuConfig,
|
||||||
MenuInteraction,
|
MenuInteraction,
|
||||||
} from '../types.js';
|
} from '../types.js';
|
||||||
import { dispatchFrigateCardEvent, shouldUpdateBasedOnHass } from '../common.js';
|
import { dispatchFrigateCardEvent, shouldUpdateBasedOnHass } from '../common.js';
|
||||||
@@ -30,10 +30,16 @@ export const MENU_HEIGHT = 46;
|
|||||||
@customElement('frigate-card-menu')
|
@customElement('frigate-card-menu')
|
||||||
export class FrigateCardMenu extends LitElement {
|
export class FrigateCardMenu extends LitElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public hass!: HomeAssistant & ExtendedHomeAssistant;
|
public hass?: HomeAssistant & ExtendedHomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected menuMode: FrigateMenuMode = 'hidden-top';
|
set menuConfig(menuConfig: MenuConfig) {
|
||||||
|
this._menuConfig = menuConfig;
|
||||||
|
if (menuConfig) {
|
||||||
|
this.style.setProperty('--frigate-card-menu-button-size', menuConfig.button_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public _menuConfig?: MenuConfig;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected expand = false;
|
protected expand = false;
|
||||||
@@ -41,13 +47,8 @@ export class FrigateCardMenu extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public buttons: MenuButton[] = [];
|
public buttons: MenuButton[] = [];
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
set buttonSize(buttonSize: string) {
|
|
||||||
this.style.setProperty('--frigate-card-menu-button-size', buttonSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected _interactionHandler(ev: CustomEvent, button: MenuButton): void {
|
protected _interactionHandler(ev: CustomEvent, button: MenuButton): void {
|
||||||
if (this.menuMode.startsWith('hidden-')) {
|
if (this._menuConfig?.mode.startsWith('hidden-')) {
|
||||||
if (button.type == 'internal-menu-icon' && button.tap_action === 'frigate') {
|
if (button.type == 'internal-menu-icon' && button.tap_action === 'frigate') {
|
||||||
this.expand = !this.expand;
|
this.expand = !this.expand;
|
||||||
return;
|
return;
|
||||||
@@ -82,7 +83,7 @@ export class FrigateCardMenu extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render a menu button.
|
// Render a menu button.
|
||||||
protected _renderButton(button: MenuButton): TemplateResult {
|
protected _renderButton(button: MenuButton): TemplateResult | void {
|
||||||
let state: HassEntity | null = null;
|
let state: HassEntity | null = null;
|
||||||
let emphasize = false;
|
let emphasize = false;
|
||||||
let title = button.title;
|
let title = button.title;
|
||||||
@@ -90,6 +91,9 @@ export class FrigateCardMenu extends LitElement {
|
|||||||
const style = ('style' in button ? button.style : {}) || {};
|
const style = ('style' in button ? button.style : {}) || {};
|
||||||
|
|
||||||
if (button.type === 'custom:frigate-card-menu-state-icon') {
|
if (button.type === 'custom:frigate-card-menu-state-icon') {
|
||||||
|
if (!this.hass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
state = this.hass.states[button.entity];
|
state = this.hass.states[button.entity];
|
||||||
emphasize =
|
emphasize =
|
||||||
!!state && button.state_color && ['on', 'active', 'home'].includes(state.state);
|
!!state && button.state_color && ['on', 'active', 'home'].includes(state.state);
|
||||||
@@ -133,9 +137,9 @@ export class FrigateCardMenu extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render the Frigate menu button.
|
// Render the Frigate menu button.
|
||||||
protected _renderFrigateButton(button: MenuButton): TemplateResult {
|
protected _renderFrigateButton(button: MenuButton): TemplateResult | void {
|
||||||
const icon =
|
const icon =
|
||||||
this.menuMode.startsWith('hidden-') && !this.expand
|
this._menuConfig?.mode.startsWith('hidden-') && !this.expand
|
||||||
? 'mdi:alpha-f-box-outline'
|
? 'mdi:alpha-f-box-outline'
|
||||||
: 'mdi:alpha-f-box';
|
: 'mdi:alpha-f-box';
|
||||||
|
|
||||||
@@ -143,7 +147,12 @@ export class FrigateCardMenu extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render the menu.
|
// Render the menu.
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult | void {
|
||||||
|
if (!this._menuConfig) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const mode = this._menuConfig.mode;
|
||||||
|
|
||||||
const isFrigateButton = function (button: MenuButton): boolean {
|
const isFrigateButton = function (button: MenuButton): boolean {
|
||||||
return button.type === 'internal-menu-icon' && button.tap_action === 'frigate';
|
return button.type === 'internal-menu-icon' && button.tap_action === 'frigate';
|
||||||
};
|
};
|
||||||
@@ -151,33 +160,29 @@ export class FrigateCardMenu extends LitElement {
|
|||||||
// If the menu is off, or if it's in hidden mode but there's no button to
|
// If the menu is off, or if it's in hidden mode but there's no button to
|
||||||
// unhide it, just show nothing.
|
// unhide it, just show nothing.
|
||||||
if (
|
if (
|
||||||
this.menuMode == 'none' ||
|
mode == 'none' ||
|
||||||
(this.menuMode.startsWith('hidden-') && !this.buttons.find(isFrigateButton))
|
(mode.startsWith('hidden-') && !this.buttons.find(isFrigateButton))
|
||||||
) {
|
) {
|
||||||
return html``;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const classes = {
|
const classes = {
|
||||||
'frigate-card-menu': true,
|
'frigate-card-menu': true,
|
||||||
'overlay-hidden':
|
'overlay-hidden':
|
||||||
this.menuMode.startsWith('hidden-') ||
|
mode.startsWith('hidden-') ||
|
||||||
this.menuMode.startsWith('overlay-') ||
|
mode.startsWith('overlay-') ||
|
||||||
this.menuMode.startsWith('hover-'),
|
mode.startsWith('hover-'),
|
||||||
'expanded-horizontal':
|
'expanded-horizontal':
|
||||||
(this.menuMode.startsWith('overlay-') ||
|
(mode.startsWith('overlay-') || mode.startsWith('hover-') || this.expand) &&
|
||||||
this.menuMode.startsWith('hover-') ||
|
(mode.endsWith('-top') || mode.endsWith('-bottom')),
|
||||||
this.expand) &&
|
|
||||||
(this.menuMode.endsWith('-top') || this.menuMode.endsWith('-bottom')),
|
|
||||||
'expanded-vertical':
|
'expanded-vertical':
|
||||||
(this.menuMode.startsWith('overlay-') ||
|
(mode.startsWith('overlay-') || mode.startsWith('hover-') || this.expand) &&
|
||||||
this.menuMode.startsWith('hover-') ||
|
(mode.endsWith('-left') || mode.endsWith('-right')),
|
||||||
this.expand) &&
|
full: mode == 'above' || mode == 'below',
|
||||||
(this.menuMode.endsWith('-left') || this.menuMode.endsWith('-right')),
|
left: mode.endsWith('-left'),
|
||||||
full: ['above', 'below'].includes(this.menuMode),
|
right: mode.endsWith('-right'),
|
||||||
left: this.menuMode.endsWith('-left'),
|
top: mode.endsWith('-top'),
|
||||||
right: this.menuMode.endsWith('-right'),
|
bottom: mode.endsWith('-bottom'),
|
||||||
top: this.menuMode.endsWith('-top'),
|
|
||||||
bottom: this.menuMode.endsWith('-bottom'),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
|
|||||||
@@ -2,30 +2,29 @@ import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit
|
|||||||
import { customElement, property } from 'lit/decorators.js';
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
|
|
||||||
import { NextPreviousControlStyle } from '../types.js';
|
import { NextPreviousControlConfig } from '../types.js';
|
||||||
|
|
||||||
import controlStyle from '../scss/next-previous-control.scss';
|
import controlStyle from '../scss/next-previous-control.scss';
|
||||||
|
|
||||||
@customElement('frigate-card-next-previous-control')
|
@customElement('frigate-card-next-previous-control')
|
||||||
export class FrigateCardMessage extends LitElement {
|
export class FrigateCardNextPreviousControl extends LitElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected direction?: 'next' | 'previous';
|
protected direction?: 'next' | 'previous';
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected controlStyle?: NextPreviousControlStyle;
|
set controlConfig(controlConfig: NextPreviousControlConfig | undefined) {
|
||||||
|
if (controlConfig?.size) {
|
||||||
|
this.style.setProperty('--frigate-card-next-prev-size', controlConfig.size);
|
||||||
|
}
|
||||||
|
this._controlConfig = controlConfig;
|
||||||
|
}
|
||||||
|
protected _controlConfig?: NextPreviousControlConfig;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected thumbnail?: string;
|
protected thumbnail?: string;
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
set controlSize(controlSize: string | undefined) {
|
|
||||||
if (controlSize) {
|
|
||||||
this.style.setProperty('--frigate-card-next-prev-size', controlSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.controlStyle || this.controlStyle == 'none') {
|
if (!this._controlConfig || this._controlConfig.style == 'none') {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,12 +32,12 @@ export class FrigateCardMessage extends LitElement {
|
|||||||
controls: true,
|
controls: true,
|
||||||
previous: this.direction == 'previous',
|
previous: this.direction == 'previous',
|
||||||
next: this.direction == 'next',
|
next: this.direction == 'next',
|
||||||
thumbnails: this.controlStyle == 'thumbnails',
|
thumbnails: this._controlConfig.style == 'thumbnails',
|
||||||
chevrons: this.controlStyle == 'chevrons',
|
chevrons: this._controlConfig.style == 'chevrons',
|
||||||
button: this.controlStyle == 'chevrons',
|
button: this._controlConfig.style == 'chevrons',
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.controlStyle == 'chevrons') {
|
if (this._controlConfig.style == 'chevrons') {
|
||||||
const icon = this.direction == 'previous' ? 'mdi:chevron-left' : 'mdi:chevron-right';
|
const icon = this.direction == 'previous' ? 'mdi:chevron-left' : 'mdi:chevron-right';
|
||||||
|
|
||||||
// TODO: Upon a safe distance from the release of HA 2021.11 these
|
// TODO: Upon a safe distance from the release of HA 2021.11 these
|
||||||
|
|||||||
+39
-55
@@ -19,7 +19,7 @@ import type {
|
|||||||
BrowseMediaSource,
|
BrowseMediaSource,
|
||||||
ExtendedHomeAssistant,
|
ExtendedHomeAssistant,
|
||||||
MediaShowInfo,
|
MediaShowInfo,
|
||||||
NextPreviousControlStyle,
|
ViewerConfig,
|
||||||
} from '../types.js';
|
} from '../types.js';
|
||||||
import { ResolvedMediaCache, ResolvedMediaUtil } from '../resolved-media.js';
|
import { ResolvedMediaCache, ResolvedMediaUtil } from '../resolved-media.js';
|
||||||
import { View } from '../view.js';
|
import { View } from '../view.js';
|
||||||
@@ -49,24 +49,15 @@ export class FrigateCardViewer extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected view?: View;
|
protected view?: View;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
protected viewerConfig?: ViewerConfig;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected browseMediaQueryParameters?: BrowseMediaQueryParameters;
|
protected browseMediaQueryParameters?: BrowseMediaQueryParameters;
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
protected nextPreviousControlStyle?: NextPreviousControlStyle;
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
protected nextPreviousControlSize?: string;
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
protected autoplayClip?: boolean;
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected resolvedMediaCache?: ResolvedMediaCache;
|
protected resolvedMediaCache?: ResolvedMediaCache;
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
protected lazyLoad?: boolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve all the given media for a target.
|
* Resolve all the given media for a target.
|
||||||
* @param target The target to resolve media from.
|
* @param target The target to resolve media from.
|
||||||
@@ -92,21 +83,23 @@ export class FrigateCardViewer extends LitElement {
|
|||||||
return errorFree;
|
return errorFree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Master render method.
|
||||||
|
* @returns A rendered template.
|
||||||
|
*/
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
return html`${until(this._render(), renderProgressIndicator())}`;
|
return html`${until(this._render(), renderProgressIndicator())}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asyncronously render the element.
|
* Asyncronously render the element.
|
||||||
* @returns A template to render.
|
* @returns A rendered template.
|
||||||
*/
|
*/
|
||||||
protected async _render(): Promise<TemplateResult | void> {
|
protected async _render(): Promise<TemplateResult | void> {
|
||||||
if (!this.hass || !this.view || !this.browseMediaQueryParameters) {
|
if (!this.hass || !this.view || !this.browseMediaQueryParameters) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
let autoplay = true;
|
|
||||||
|
|
||||||
if (this.view.is('clip') || this.view.is('snapshot')) {
|
if (this.view.is('clip') || this.view.is('snapshot')) {
|
||||||
let parent: BrowseMediaSource | null = null;
|
let parent: BrowseMediaSource | null = null;
|
||||||
try {
|
try {
|
||||||
@@ -129,13 +122,6 @@ export class FrigateCardViewer extends LitElement {
|
|||||||
}
|
}
|
||||||
this.view.target = parent;
|
this.view.target = parent;
|
||||||
this.view.childIndex = childIndex;
|
this.view.childIndex = childIndex;
|
||||||
|
|
||||||
// In this block, no clip has been manually selected, so this is loading
|
|
||||||
// the most recent clip on card load. In this mode, autoplay of the clip
|
|
||||||
// may be disabled by configuration. If does not make sense to disable
|
|
||||||
// autoplay when the user has explicitly picked an event to play in the
|
|
||||||
// gallery.
|
|
||||||
autoplay = this.autoplayClip ?? true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.view.target && !(await this._resolveAllMediaForTarget(this.view.target))) {
|
if (this.view.target && !(await this._resolveAllMediaForTarget(this.view.target))) {
|
||||||
@@ -144,13 +130,10 @@ export class FrigateCardViewer extends LitElement {
|
|||||||
|
|
||||||
return html` <frigate-card-viewer-core
|
return html` <frigate-card-viewer-core
|
||||||
.view=${this.view}
|
.view=${this.view}
|
||||||
.nextPreviousControlStyle=${this.nextPreviousControlStyle}
|
.viewerConfig=${this.viewerConfig}
|
||||||
.nextPreviousControlSize=${this.nextPreviousControlSize}
|
|
||||||
.resolvedMediaCache=${this.resolvedMediaCache}
|
.resolvedMediaCache=${this.resolvedMediaCache}
|
||||||
.autoplayClip=${autoplay}
|
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.browseMediaQueryParameters=${this.browseMediaQueryParameters}
|
.browseMediaQueryParameters=${this.browseMediaQueryParameters}
|
||||||
.lazyLoad=${this.lazyLoad}
|
|
||||||
>
|
>
|
||||||
</frigate-card-viewer-core>`;
|
</frigate-card-viewer-core>`;
|
||||||
}
|
}
|
||||||
@@ -165,29 +148,20 @@ export class FrigateCardViewer extends LitElement {
|
|||||||
|
|
||||||
@customElement('frigate-card-viewer-core')
|
@customElement('frigate-card-viewer-core')
|
||||||
export class FrigateCardViewerCore extends LitElement {
|
export class FrigateCardViewerCore extends LitElement {
|
||||||
|
@property({ attribute: false })
|
||||||
|
protected hass?: HomeAssistant & ExtendedHomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected view?: View;
|
protected view?: View;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected nextPreviousControlStyle?: NextPreviousControlStyle;
|
protected viewerConfig?: ViewerConfig;
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
protected nextPreviousControlSize?: string;
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
protected resolvedMediaCache?: ResolvedMediaCache;
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
protected autoplayClip?: boolean;
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
protected hass?: HomeAssistant & ExtendedHomeAssistant;
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected browseMediaQueryParameters?: BrowseMediaQueryParameters;
|
protected browseMediaQueryParameters?: BrowseMediaQueryParameters;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected lazyLoad?: boolean;
|
protected resolvedMediaCache?: ResolvedMediaCache;
|
||||||
|
|
||||||
// Media carousel object.
|
// Media carousel object.
|
||||||
protected _carousel?: EmblaCarouselType;
|
protected _carousel?: EmblaCarouselType;
|
||||||
@@ -418,7 +392,7 @@ export class FrigateCardViewerCore extends LitElement {
|
|||||||
* Lazily load media in the carousel.
|
* Lazily load media in the carousel.
|
||||||
*/
|
*/
|
||||||
protected _lazyLoadMediaHandler(): void {
|
protected _lazyLoadMediaHandler(): void {
|
||||||
if (!this.lazyLoad || !this._carousel) {
|
if (!this.viewerConfig?.lazy_load || !this._carousel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const slides = this._carousel.slideNodes();
|
const slides = this._carousel.slideNodes();
|
||||||
@@ -504,8 +478,7 @@ export class FrigateCardViewerCore extends LitElement {
|
|||||||
${neighbors && neighbors.previous
|
${neighbors && neighbors.previous
|
||||||
? html`<frigate-card-next-previous-control
|
? html`<frigate-card-next-previous-control
|
||||||
.direction=${'previous'}
|
.direction=${'previous'}
|
||||||
.controlStyle=${this.nextPreviousControlStyle}
|
.controlConfig=${this.viewerConfig?.controls.next_previous}
|
||||||
.controlSize=${this.nextPreviousControlSize}
|
|
||||||
.thumbnail=${neighbors.previous.thumbnail}
|
.thumbnail=${neighbors.previous.thumbnail}
|
||||||
.title=${neighbors.previous.title}
|
.title=${neighbors.previous.title}
|
||||||
@click=${() => this._nextPreviousHandler('previous')}
|
@click=${() => this._nextPreviousHandler('previous')}
|
||||||
@@ -519,8 +492,7 @@ export class FrigateCardViewerCore extends LitElement {
|
|||||||
${neighbors && neighbors.next
|
${neighbors && neighbors.next
|
||||||
? html`<frigate-card-next-previous-control
|
? html`<frigate-card-next-previous-control
|
||||||
.direction=${'next'}
|
.direction=${'next'}
|
||||||
.controlStyle=${this.nextPreviousControlStyle}
|
.controlConfig=${this.viewerConfig?.controls.next_previous}
|
||||||
.controlSize=${this.nextPreviousControlSize}
|
|
||||||
.thumbnail=${neighbors.next.thumbnail}
|
.thumbnail=${neighbors.next.thumbnail}
|
||||||
.title=${neighbors.next.title}
|
.title=${neighbors.next.title}
|
||||||
@click=${() => this._nextPreviousHandler('next')}
|
@click=${() => this._nextPreviousHandler('next')}
|
||||||
@@ -594,7 +566,7 @@ export class FrigateCardViewerCore extends LitElement {
|
|||||||
): TemplateResult | void {
|
): TemplateResult | void {
|
||||||
// media that can be expanded (folders) cannot be resolved to a single media
|
// media that can be expanded (folders) cannot be resolved to a single media
|
||||||
// item, skip them.
|
// item, skip them.
|
||||||
if (!this.view || !BrowseMediaUtil.isTrueMedia(mediaToRender)) {
|
if (!this.view || !this.viewerConfig || !BrowseMediaUtil.isTrueMedia(mediaToRender)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -603,20 +575,32 @@ export class FrigateCardViewerCore extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In this block, no clip has been manually selected, so this is loading
|
||||||
|
// the most recent clip on card load. In this mode, autoplay of the clip
|
||||||
|
// may be disabled by configuration. If does not make sense to disable
|
||||||
|
// autoplay when the user has explicitly picked an event to play in the
|
||||||
|
// gallery.
|
||||||
|
let autoplay = true;
|
||||||
|
if (this.view.is('clip') || this.view.is('snapshot')) {
|
||||||
|
autoplay = this.viewerConfig.autoplay_clip;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lazyLoad = this.viewerConfig.lazy_load;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="embla__slide">
|
<div class="embla__slide">
|
||||||
${this.view.isClipRelatedView()
|
${this.view.isClipRelatedView()
|
||||||
? resolvedMedia?.mime_type.toLowerCase() == 'application/x-mpegurl'
|
? resolvedMedia?.mime_type.toLowerCase() == 'application/x-mpegurl'
|
||||||
? html`<frigate-card-ha-hls-player
|
? html`<frigate-card-ha-hls-player
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
url=${ifDefined(this.lazyLoad ? undefined : resolvedMedia.url)}
|
url=${ifDefined(lazyLoad ? undefined : resolvedMedia.url)}
|
||||||
data-url=${ifDefined(this.lazyLoad ? resolvedMedia.url : undefined)}
|
data-url=${ifDefined(lazyLoad ? resolvedMedia.url : undefined)}
|
||||||
title="${mediaToRender.title}"
|
title="${mediaToRender.title}"
|
||||||
muted
|
muted
|
||||||
controls
|
controls
|
||||||
playsinline
|
playsinline
|
||||||
allow-exoplayer
|
allow-exoplayer
|
||||||
?autoplay="${this.autoplayClip}"
|
?autoplay="${autoplay}"
|
||||||
@frigate-card:media-show=${(e: CustomEvent<MediaShowInfo>) =>
|
@frigate-card:media-show=${(e: CustomEvent<MediaShowInfo>) =>
|
||||||
this._mediaShowEventHandler(slideIndex, e)}
|
this._mediaShowEventHandler(slideIndex, e)}
|
||||||
>
|
>
|
||||||
@@ -626,7 +610,7 @@ export class FrigateCardViewerCore extends LitElement {
|
|||||||
muted
|
muted
|
||||||
controls
|
controls
|
||||||
playsinline
|
playsinline
|
||||||
?autoplay="${this.autoplayClip}"
|
?autoplay="${autoplay}"
|
||||||
@loadedmetadata="${(e: Event) => {
|
@loadedmetadata="${(e: Event) => {
|
||||||
this._mediaShowInfoHandler(slideIndex, createMediaShowInfo(e));
|
this._mediaShowInfoHandler(slideIndex, createMediaShowInfo(e));
|
||||||
}}"
|
}}"
|
||||||
@@ -634,14 +618,14 @@ export class FrigateCardViewerCore extends LitElement {
|
|||||||
@pause=${() => dispatchPauseEvent(this)}
|
@pause=${() => dispatchPauseEvent(this)}
|
||||||
>
|
>
|
||||||
<source
|
<source
|
||||||
src=${ifDefined(this.lazyLoad ? undefined : resolvedMedia.url)}
|
src=${ifDefined(lazyLoad ? undefined : resolvedMedia.url)}
|
||||||
data-src=${ifDefined(this.lazyLoad ? resolvedMedia.url : undefined)}
|
data-src=${ifDefined(lazyLoad ? resolvedMedia.url : undefined)}
|
||||||
type="${resolvedMedia.mime_type}"
|
type="${resolvedMedia.mime_type}"
|
||||||
/>
|
/>
|
||||||
</video>`
|
</video>`
|
||||||
: html`<img
|
: html`<img
|
||||||
src=${ifDefined(this.lazyLoad ? IMG_EMPTY : resolvedMedia.url)}
|
src=${ifDefined(lazyLoad ? IMG_EMPTY : resolvedMedia.url)}
|
||||||
data-src=${ifDefined(this.lazyLoad ? resolvedMedia.url : undefined)}
|
data-src=${ifDefined(lazyLoad ? resolvedMedia.url : undefined)}
|
||||||
title="${mediaToRender.title}"
|
title="${mediaToRender.title}"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
if (this._carousel?.clickAllowed()) {
|
if (this._carousel?.clickAllowed()) {
|
||||||
|
|||||||
+168
-139
@@ -1,10 +1,18 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
|
// TODO Add editor for control sizes
|
||||||
|
// TODO reorganizse editor by section
|
||||||
|
|
||||||
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
||||||
import { customElement, property, state } from 'lit/decorators.js';
|
import { customElement, property, state } from 'lit/decorators.js';
|
||||||
|
|
||||||
import { HomeAssistant, LovelaceCardEditor, fireEvent } from 'custom-card-helpers';
|
import { HomeAssistant, LovelaceCardEditor, fireEvent } from 'custom-card-helpers';
|
||||||
import { localize } from './localize/localize.js';
|
import { localize } from './localize/localize.js';
|
||||||
import type { FrigateCardConfig } from './types.js';
|
import {
|
||||||
|
FrigateCardConfig,
|
||||||
|
frigateCardConfigDefaults,
|
||||||
|
frigateCardConfigSchema,
|
||||||
|
} from './types.js';
|
||||||
|
|
||||||
import frigate_card_editor_style from './scss/editor.scss';
|
import frigate_card_editor_style from './scss/editor.scss';
|
||||||
|
|
||||||
@@ -84,66 +92,70 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The climate more-info has ha-switch and paper-dropdown-menu elements that are lazy loaded unless explicitly done here
|
// The climate more-info has ha-switch and paper-dropdown-menu elements that
|
||||||
|
// are lazy loaded unless explicitly loaded via climate here.
|
||||||
this._helpers.importMoreInfoControl('climate');
|
this._helpers.importMoreInfoControl('climate');
|
||||||
|
|
||||||
// You can restrict on domain type
|
|
||||||
const cameraEntities = this._getEntities('camera');
|
const cameraEntities = this._getEntities('camera');
|
||||||
|
|
||||||
const webrtcCameraEntity =
|
const webrtcCameraEntity =
|
||||||
this._config?.webrtc && (this._config?.webrtc as any).entity
|
this._config?.live?.webrtc && (this._config?.live.webrtc as any).entity
|
||||||
? (this._config?.webrtc as any).entity
|
? (this._config?.live.webrtc as any).entity
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
const viewModes = {
|
const viewModes = {
|
||||||
'': '',
|
'': '',
|
||||||
live: localize('menu.live'),
|
live: localize('config.view.views.live'),
|
||||||
clips: localize('menu.clips'),
|
clips: localize('config.view.views.clips'),
|
||||||
snapshots: localize('menu.snapshots'),
|
snapshots: localize('config.view.views.snapshots'),
|
||||||
clip: localize('menu.clip'),
|
clip: localize('config.view.views.clip'),
|
||||||
snapshot: localize('menu.snapshot'),
|
snapshot: localize('config.view.views.snapshot'),
|
||||||
image: localize('menu.image'),
|
image: localize('config.view.views.image'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const menuModes = {
|
const menuModes = {
|
||||||
'': '',
|
'': '',
|
||||||
none: localize('menu_mode.none'),
|
none: localize('config.menu.modes.none'),
|
||||||
'hidden-top': localize('menu_mode.hidden-top'),
|
'hidden-top': localize('config.menu.modes.hidden-top'),
|
||||||
'hidden-left': localize('menu_mode.hidden-left'),
|
'hidden-left': localize('config.menu.modes.hidden-left'),
|
||||||
'hidden-bottom': localize('menu_mode.hidden-bottom'),
|
'hidden-bottom': localize('config.menu.modes.hidden-bottom'),
|
||||||
'hidden-right': localize('menu_mode.hidden-right'),
|
'hidden-right': localize('config.menu.modes.hidden-right'),
|
||||||
'overlay-top': localize('menu_mode.overlay-top'),
|
'overlay-top': localize('config.menu.modes.overlay-top'),
|
||||||
'overlay-left': localize('menu_mode.overlay-left'),
|
'overlay-left': localize('config.menu.modes.overlay-left'),
|
||||||
'overlay-bottom': localize('menu_mode.overlay-bottom'),
|
'overlay-bottom': localize('config.menu.modes.overlay-bottom'),
|
||||||
'overlay-right': localize('menu_mode.overlay-right'),
|
'overlay-right': localize('config.menu.modes.overlay-right'),
|
||||||
'hover-top': localize('menu_mode.hover-top'),
|
'hover-top': localize('config.menu.modes.hover-top'),
|
||||||
'hover-left': localize('menu_mode.hover-left'),
|
'hover-left': localize('config.menu.modes.hover-left'),
|
||||||
'hover-bottom': localize('menu_mode.hover-bottom'),
|
'hover-bottom': localize('config.menu.modes.hover-bottom'),
|
||||||
'hover-right': localize('menu_mode.hover-right'),
|
'hover-right': localize('config.menu.modes.hover-right'),
|
||||||
above: localize('menu_mode.above'),
|
above: localize('config.menu.modes.above'),
|
||||||
below: localize('menu_mode.below'),
|
below: localize('config.menu.modes.below'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const liveProvider = {
|
const liveProviders = {
|
||||||
'': '',
|
'': '',
|
||||||
frigate: localize('live_provider.frigate'),
|
frigate: localize('config.live.providers.frigate'),
|
||||||
'frigate-jsmpeg': localize('live_provider.frigate-jsmpeg'),
|
'frigate-jsmpeg': localize('config.live.providers.frigate-jsmpeg'),
|
||||||
webrtc: localize('live_provider.webrtc'),
|
webrtc: localize('config.live.providers.webrtc'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const controlsNextPrev = {
|
const eventViewerNextPreviousControlStyles = {
|
||||||
'': '',
|
'': '',
|
||||||
thumbnails: localize('control.thumbnails'),
|
thumbnails: localize(
|
||||||
chevrons: localize('control.chevrons'),
|
'config.event_viewer.controls.next_previous.styles.thumbnails',
|
||||||
none: localize('control.none'),
|
),
|
||||||
|
chevrons: localize('config.event_viewer.controls.next_previous.styles.chevrons'),
|
||||||
|
none: localize('config.event_viewer.controls.next_previous.styles.none'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const aspectRatioModes = {
|
const aspectRatioModes = {
|
||||||
'': '',
|
'': '',
|
||||||
dynamic: localize('aspect_ratio_mode.dynamic'),
|
dynamic: localize('config.dimensions.aspect_ratio_modes.dynamic'),
|
||||||
static: localize('aspect_ratio_mode.static'),
|
static: localize('config.dimensions.aspect_ratio_modes.static'),
|
||||||
unconstrained: localize('aspect_ratio_mode.unconstrained'),
|
unconstrained: localize('config.dimensions.aspect_ratio_modes.unconstrained'),
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const defaults = frigateCardConfigDefaults;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="card-config">
|
<div class="card-config">
|
||||||
@@ -158,7 +170,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
? html`
|
? html`
|
||||||
<div class="values">
|
<div class="values">
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
label=${localize('editor.camera_entity')}
|
label=${localize('config.camera_entity')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
.configValue=${'camera_entity'}
|
.configValue=${'camera_entity'}
|
||||||
>
|
>
|
||||||
@@ -174,20 +186,20 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('editor.frigate_camera_name')}
|
label=${localize('config.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
|
<paper-dropdown-menu
|
||||||
label=${localize('editor.default_view')}
|
label=${localize('config.view.default')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
.configValue=${'view_default'}
|
.configValue=${'view.default'}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<paper-listbox
|
||||||
slot="dropdown-content"
|
slot="dropdown-content"
|
||||||
.selected=${Object.keys(viewModes).indexOf(
|
.selected=${Object.keys(viewModes).indexOf(
|
||||||
this._config?.view_default || '',
|
this._config?.view?.default || '',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${Object.keys(viewModes).map((key) => {
|
${Object.keys(viewModes).map((key) => {
|
||||||
@@ -210,62 +222,64 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
${options.optional.show
|
${options.optional.show
|
||||||
? html` <div class="values">
|
? html` <div class="values">
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
.label=${localize('editor.live_provider')}
|
.label=${localize('config.live.provider')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
.configValue=${'live_provider'}
|
.configValue=${'live.provider'}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<paper-listbox
|
||||||
slot="dropdown-content"
|
slot="dropdown-content"
|
||||||
.selected=${Object.keys(liveProvider).indexOf(
|
.selected=${Object.keys(liveProviders).indexOf(
|
||||||
this._config?.live_provider || '',
|
this._config?.live?.provider || '',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${Object.keys(liveProvider).map((key) => {
|
${Object.keys(liveProviders).map((key) => {
|
||||||
return html`
|
return html`
|
||||||
<paper-item .label="${key}">${liveProvider[key]} </paper-item>
|
<paper-item .label="${key}">${liveProviders[key]} </paper-item>
|
||||||
`;
|
`;
|
||||||
})}
|
})}
|
||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('editor.frigate_client_id')}
|
label=${localize('config.frigate.client_id')}
|
||||||
.value=${this._config?.frigate_client_id || ''}
|
.value=${this._config?.frigate?.client_id || ''}
|
||||||
.configValue=${'frigate_client_id'}
|
.configValue=${'frigate.client_id'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('editor.view_timeout')}
|
label=${localize('config.view.timeout')}
|
||||||
prevent-invalid-input
|
prevent-invalid-input
|
||||||
allowed-pattern="[0-9]"
|
allowed-pattern="[0-9]"
|
||||||
.value=${this._config?.view_timeout
|
.value=${this._config?.view?.timeout
|
||||||
? String(this._config.view_timeout)
|
? String(this._config?.view?.timeout)
|
||||||
: ''}
|
: ''}
|
||||||
.configValue=${'view_timeout'}
|
.configValue=${'view.timeout'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('editor.frigate_url')}
|
label=${localize('config.frigate.url')}
|
||||||
.value=${this._config?.frigate_url || ''}
|
.value=${this._config?.frigate?.url || ''}
|
||||||
.configValue=${'frigate_url'}
|
.configValue=${'frigate.url'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<ha-formfield .label=${localize('editor.autoplay_clip')}>
|
<ha-formfield .label=${localize('config.event_viewer.autoplay_clip')}>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.autoplay_clip === true}
|
.checked=${this._config?.event_viewer?.autoplay_clip ??
|
||||||
.configValue=${'autoplay_clip'}
|
defaults.event_viewer.autoplay_clip}
|
||||||
|
.configValue=${'event_viewer.autoplay_clip'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<ha-formfield .label=${localize('editor.live_preload')}>
|
<ha-formfield .label=${localize('config.live.preload')}>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.live_preload === true}
|
.checked=${this._config?.live?.preload ?? defaults.live.preload}
|
||||||
.configValue=${'live_preload'}
|
.configValue=${'live.preload'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<ha-formfield .label=${localize('editor.lazy_load')}>
|
<ha-formfield .label=${localize('config.event_viewer.lazy_load')}>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.event_viewer?.lazy_load !== false}
|
.checked=${this._config?.event_viewer?.lazy_load ??
|
||||||
|
defaults.event_viewer.lazy_load}
|
||||||
.configValue=${'event_viewer.lazy_load'}
|
.configValue=${'event_viewer.lazy_load'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
@@ -281,14 +295,14 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
</div>
|
</div>
|
||||||
${options.appearance.show
|
${options.appearance.show
|
||||||
? html`<paper-dropdown-menu
|
? html`<paper-dropdown-menu
|
||||||
.label=${localize('editor.menu_mode')}
|
.label=${localize('config.menu.mode')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
.configValue=${'menu_mode'}
|
.configValue=${'menu.mode'}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<paper-listbox
|
||||||
slot="dropdown-content"
|
slot="dropdown-content"
|
||||||
.selected=${Object.keys(menuModes).indexOf(
|
.selected=${Object.keys(menuModes).indexOf(
|
||||||
this._config?.menu_mode || '',
|
this._config?.menu?.mode || '',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${Object.keys(menuModes).map((key) => {
|
${Object.keys(menuModes).map((key) => {
|
||||||
@@ -300,26 +314,28 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
<br />
|
<br />
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
.label=${localize('control.nextprev')}
|
.label=${localize('config.event_viewer.controls.next_previous.style')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
.configValue=${'controls.nextprev'}
|
.configValue=${'event_viewer.controls.next_previous.style'}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<paper-listbox
|
||||||
slot="dropdown-content"
|
slot="dropdown-content"
|
||||||
.selected=${Object.keys(controlsNextPrev).indexOf(
|
.selected=${Object.keys(eventViewerNextPreviousControlStyles).indexOf(
|
||||||
this._config?.controls?.nextprev || '',
|
this._config?.event_viewer?.controls?.next_previous?.style || '',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${Object.keys(controlsNextPrev).map((key) => {
|
${Object.keys(eventViewerNextPreviousControlStyles).map((key) => {
|
||||||
return html`
|
return html`
|
||||||
<paper-item .label="${key}"> ${controlsNextPrev[key]} </paper-item>
|
<paper-item .label="${key}">
|
||||||
|
${eventViewerNextPreviousControlStyles[key]}
|
||||||
|
</paper-item>
|
||||||
`;
|
`;
|
||||||
})}
|
})}
|
||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
<br />
|
<br />
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
.label=${localize('dimensions.aspect_ratio_mode')}
|
.label=${localize('config.dimensions.aspect_ratio_mode')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
.configValue=${'dimensions.aspect_ratio_mode'}
|
.configValue=${'dimensions.aspect_ratio_mode'}
|
||||||
>
|
>
|
||||||
@@ -338,7 +354,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
<br />
|
<br />
|
||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('dimensions.aspect_ratio')}
|
label=${localize('config.dimensions.aspect_ratio')}
|
||||||
prevent-invalid-input
|
prevent-invalid-input
|
||||||
.value=${this._config?.dimensions?.aspect_ratio
|
.value=${this._config?.dimensions?.aspect_ratio
|
||||||
? String(this._config?.dimensions?.aspect_ratio)
|
? String(this._config?.dimensions?.aspect_ratio)
|
||||||
@@ -347,42 +363,23 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('editor.image')}
|
label=${localize('config.image.src')}
|
||||||
prevent-invalid-input
|
prevent-invalid-input
|
||||||
.value=${this._config?.image
|
.value=${this._config?.image?.src
|
||||||
? String(this._config?.image)
|
? String(this._config?.image?.src)
|
||||||
: ''}
|
: ''}
|
||||||
.configValue=${'image'}
|
.configValue=${'image.src'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<ha-formfield
|
<ha-formfield
|
||||||
.label=${localize('editor.show_button') +
|
.label=${localize('editor.show_button') +
|
||||||
': ' +
|
': ' +
|
||||||
localize('menu.frigate')}
|
localize('config.menu.buttons.frigate')}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.menu_buttons?.frigate ?? true}
|
.checked=${this._config?.menu?.buttons?.frigate ??
|
||||||
.configValue=${'menu_buttons.frigate'}
|
defaults.menu.buttons.frigate}
|
||||||
@change=${this._valueChanged}
|
.configValue=${'menu.buttons.frigate'}
|
||||||
></ha-switch>
|
|
||||||
</ha-formfield>
|
|
||||||
<br />
|
|
||||||
<ha-formfield
|
|
||||||
.label=${localize('editor.show_button') + ': ' + localize('menu.live')}
|
|
||||||
>
|
|
||||||
<ha-switch
|
|
||||||
.checked=${this._config?.menu_buttons?.live ?? true}
|
|
||||||
.configValue=${'menu_buttons.live'}
|
|
||||||
@change=${this._valueChanged}
|
|
||||||
></ha-switch>
|
|
||||||
</ha-formfield>
|
|
||||||
<br />
|
|
||||||
<ha-formfield
|
|
||||||
.label=${localize('editor.show_button') + ': ' + localize('menu.clips')}
|
|
||||||
>
|
|
||||||
<ha-switch
|
|
||||||
.checked=${this._config?.menu_buttons?.clips ?? true}
|
|
||||||
.configValue=${'menu_buttons.clips'}
|
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
@@ -390,11 +387,12 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
<ha-formfield
|
<ha-formfield
|
||||||
.label=${localize('editor.show_button') +
|
.label=${localize('editor.show_button') +
|
||||||
': ' +
|
': ' +
|
||||||
localize('menu.snapshots')}
|
localize('config.view.views.live')}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.menu_buttons?.snapshots ?? true}
|
.checked=${this._config?.menu?.buttons?.live ??
|
||||||
.configValue=${'menu_buttons.snapshots'}
|
defaults.menu.buttons.live}
|
||||||
|
.configValue=${'menu.buttons.live'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
@@ -402,11 +400,12 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
<ha-formfield
|
<ha-formfield
|
||||||
.label=${localize('editor.show_button') +
|
.label=${localize('editor.show_button') +
|
||||||
': ' +
|
': ' +
|
||||||
localize('menu.image')}
|
localize('config.view.views.clips')}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.menu_buttons?.image ?? false}
|
.checked=${this._config?.menu?.buttons?.clips ??
|
||||||
.configValue=${'menu_buttons.image'}
|
defaults.menu.buttons.clips}
|
||||||
|
.configValue=${'menu.buttons.clips'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
@@ -414,11 +413,12 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
<ha-formfield
|
<ha-formfield
|
||||||
.label=${localize('editor.show_button') +
|
.label=${localize('editor.show_button') +
|
||||||
': ' +
|
': ' +
|
||||||
localize('menu.download')}
|
localize('config.view.views.snapshots')}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.menu_buttons?.download ?? true}
|
.checked=${this._config?.menu?.buttons?.snapshots ??
|
||||||
.configValue=${'menu_buttons.download'}
|
defaults.menu.buttons.snapshots}
|
||||||
|
.configValue=${'menu.buttons.snapshots'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
@@ -426,11 +426,12 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
<ha-formfield
|
<ha-formfield
|
||||||
.label=${localize('editor.show_button') +
|
.label=${localize('editor.show_button') +
|
||||||
': ' +
|
': ' +
|
||||||
localize('menu.frigate_ui')}
|
localize('config.view.views.image')}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.menu_buttons?.frigate_ui ?? true}
|
.checked=${this._config?.menu?.buttons?.image ??
|
||||||
.configValue=${'menu_buttons.frigate_ui'}
|
defaults.menu.buttons.image}
|
||||||
|
.configValue=${'menu.buttons.image'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
@@ -438,11 +439,38 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
<ha-formfield
|
<ha-formfield
|
||||||
.label=${localize('editor.show_button') +
|
.label=${localize('editor.show_button') +
|
||||||
': ' +
|
': ' +
|
||||||
localize('menu.fullscreen')}
|
localize('config.menu.buttons.download')}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.menu_buttons?.frigate_ui ?? true}
|
.checked=${this._config?.menu?.buttons?.download ??
|
||||||
.configValue=${'menu_buttons.fullscreen'}
|
defaults.menu.buttons.download}
|
||||||
|
.configValue=${'menu.buttons.download'}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
|
<br />
|
||||||
|
<ha-formfield
|
||||||
|
.label=${localize('editor.show_button') +
|
||||||
|
': ' +
|
||||||
|
localize('config.menu.buttons.frigate_ui')}
|
||||||
|
>
|
||||||
|
<ha-switch
|
||||||
|
.checked=${this._config?.menu?.buttons?.frigate_ui ??
|
||||||
|
defaults.menu.buttons.frigate_ui}
|
||||||
|
.configValue=${'menu.buttons.frigate_ui'}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
|
<br />
|
||||||
|
<ha-formfield
|
||||||
|
.label=${localize('editor.show_button') +
|
||||||
|
': ' +
|
||||||
|
localize('config.menu.buttons.fullscreen')}
|
||||||
|
>
|
||||||
|
<ha-switch
|
||||||
|
.checked=${this._config?.menu?.buttons?.fullscreen ??
|
||||||
|
defaults.menu.buttons.fullscreen}
|
||||||
|
.configValue=${'menu.buttons.fullscreen'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
@@ -458,17 +486,17 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
${options.advanced.show
|
${options.advanced.show
|
||||||
? html` <div class="values">
|
? html` <div class="values">
|
||||||
<paper-input
|
<paper-input
|
||||||
.label=${localize('editor.zone')}
|
.label=${localize('config.frigate.label')}
|
||||||
.value=${this._config?.zone || ''}
|
.value=${this._config?.frigate?.label || ''}
|
||||||
.configValue=${'zone'}
|
.configValue=${'frigate.label'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="values">
|
<div class="values">
|
||||||
<paper-input
|
<paper-input
|
||||||
.label=${localize('editor.label')}
|
.label=${localize('config.frigate.zone')}
|
||||||
.value=${this._config?.label || ''}
|
.value=${this._config?.frigate?.zone || ''}
|
||||||
.configValue=${'label'}
|
.configValue=${'frigate.zone'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
</div>`
|
</div>`
|
||||||
@@ -483,9 +511,9 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
${options.webrtc.show
|
${options.webrtc.show
|
||||||
? html` <div class="values">
|
? html` <div class="values">
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
.label=${localize('webrtc.entity')}
|
.label=${localize('config.live.webrtc.entity')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
.configValue=${'webrtc.entity'}
|
.configValue=${'live.webrtc.entity'}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<paper-listbox
|
||||||
slot="dropdown-content"
|
slot="dropdown-content"
|
||||||
@@ -497,9 +525,9 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('webrtc.url')}
|
label=${localize('config.live.webrtc.url')}
|
||||||
.value=${this._config?.webrtc?.url || ''}
|
.value=${this._config?.live?.webrtc?.url || ''}
|
||||||
.configValue=${'webrtc.url'}
|
.configValue=${'live.webrtc.url'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
</div>`
|
</div>`
|
||||||
@@ -549,13 +577,14 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
let objectTarget = newConfig;
|
let objectTarget = newConfig;
|
||||||
|
|
||||||
if (key.includes('.')) {
|
if (key.includes('.')) {
|
||||||
const parts = key.split('.', 2);
|
const parts = key.split('.');
|
||||||
const configName = parts[0];
|
objectTarget = parts.slice(0, -1).reduce((obj, key) => {
|
||||||
if (!(configName in newConfig)) {
|
if (!(key in obj)) {
|
||||||
newConfig[configName] = {};
|
obj[key] = {};
|
||||||
}
|
}
|
||||||
objectTarget = newConfig[configName];
|
return obj[key];
|
||||||
key = parts[1];
|
}, newConfig);
|
||||||
|
key = parts[parts.length-1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value !== undefined && objectTarget[key] === value) {
|
if (value !== undefined && objectTarget[key] === value) {
|
||||||
|
|||||||
@@ -1,15 +1,103 @@
|
|||||||
{
|
{
|
||||||
"common": {
|
"common": {
|
||||||
"version": "Version",
|
"version": "Version",
|
||||||
"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"
|
||||||
},
|
},
|
||||||
|
"config": {
|
||||||
|
"camera_entity": "Camera Entity",
|
||||||
|
"frigate": {
|
||||||
|
"camera_name": "Frigate camera name (Optional, autodetected from entity)",
|
||||||
|
"client_id": "Frigate client id (For >1 Frigate server)",
|
||||||
|
"label": "Label/object filter",
|
||||||
|
"url": "Frigate server URL",
|
||||||
|
"zone": "Zone"
|
||||||
|
},
|
||||||
|
"view": {
|
||||||
|
"default": "Default view",
|
||||||
|
"views": {
|
||||||
|
"live": "Live view",
|
||||||
|
"clip": "Most recent clip",
|
||||||
|
"clips": "Clips gallery",
|
||||||
|
"snapshot": "Most recent snapshot",
|
||||||
|
"snapshots": "Snapshots gallery",
|
||||||
|
"image": "Static image"
|
||||||
|
},
|
||||||
|
"timeout": "View timeout (seconds)"
|
||||||
|
},
|
||||||
|
"event_viewer": {
|
||||||
|
"autoplay_clip": "Autoplay most recent clip",
|
||||||
|
"lazy_load": "Lazily load event media",
|
||||||
|
"controls": {
|
||||||
|
"next_previous": {
|
||||||
|
"style": "Event Viewer Next & Previous Control style",
|
||||||
|
"size": "Event Viewer Next & Previous Control size",
|
||||||
|
"styles": {
|
||||||
|
"thumbnails": "Thumbnails",
|
||||||
|
"chevrons": "Chevrons",
|
||||||
|
"none": "None"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"live": {
|
||||||
|
"preload": "Preload live view",
|
||||||
|
"provider": "Live view provider",
|
||||||
|
"providers": {
|
||||||
|
"frigate": "Frigate",
|
||||||
|
"frigate-jsmpeg": "Frigate JSMpeg",
|
||||||
|
"webrtc": "WebRTC"
|
||||||
|
},
|
||||||
|
"webrtc": {
|
||||||
|
"entity": "WebRTC Camera Entity (Not a Frigate camera)",
|
||||||
|
"url": "WebRTC Camera URL"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"src": "Static image URL/data for image view"
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"buttons": {
|
||||||
|
"frigate": "Frigate menu / Default view",
|
||||||
|
"frigate_ui": "Frigate user Interface",
|
||||||
|
"fullscreen": "Fullscreen",
|
||||||
|
"download": "Download event media"
|
||||||
|
},
|
||||||
|
"mode": "Menu mode",
|
||||||
|
"modes": {
|
||||||
|
"none": "No menu",
|
||||||
|
"hidden-top": "Hidden top",
|
||||||
|
"hidden-left": "Hidden left",
|
||||||
|
"hidden-bottom": "Hidden bottom",
|
||||||
|
"hidden-right": "Hidden right",
|
||||||
|
"overlay-top": "Overlay top",
|
||||||
|
"overlay-left": "Overlay left",
|
||||||
|
"overlay-bottom": "Overlay bottom",
|
||||||
|
"overlay-right": "Overlay right",
|
||||||
|
"hover-top": "Hover top",
|
||||||
|
"hover-left": "Hover left",
|
||||||
|
"hover-bottom": "Hover bottom",
|
||||||
|
"hover-right": "Hover right",
|
||||||
|
"above": "Above",
|
||||||
|
"below": "Below"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dimensions": {
|
||||||
|
"aspect_ratio": "Default aspect ratio",
|
||||||
|
"aspect_ratio_mode": "Aspect ratio mode",
|
||||||
|
"aspect_ratio_modes": {
|
||||||
|
"unconstrained": "Unconstrained aspect ratio",
|
||||||
|
"dynamic": "Aspect ratio adjusts to media",
|
||||||
|
"static": "Static aspect ratio"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
"basic": "Basic",
|
"basic": "Basic",
|
||||||
"basic_secondary": "Basic options for most users",
|
"basic_secondary": "Basic options for most users",
|
||||||
@@ -21,74 +109,7 @@
|
|||||||
"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 (Optional)",
|
"show_button": "Show button"
|
||||||
"frigate_camera_name": "Frigate camera name (Optional, autodetected from entity)",
|
|
||||||
"default_view": "Default view (Optional)",
|
|
||||||
"frigate_client_id": "Frigate client id (Optional, for >1 Frigate server)",
|
|
||||||
"view_timeout": "View timeout (seconds)",
|
|
||||||
"frigate_url": "Frigate URL (Optional, for Frigate UI button)",
|
|
||||||
"autoplay_clip": "Autoplay latest clip",
|
|
||||||
"menu_mode": "Menu mode (Optional)",
|
|
||||||
"show_button": "Show button",
|
|
||||||
"zone": "Zone",
|
|
||||||
"label": "Frigate label/object filter (Optional)",
|
|
||||||
"live_provider": "Live view provider (Optional)",
|
|
||||||
"live_preload": "Preload live view",
|
|
||||||
"image": "Static image URL for image view (Optional)",
|
|
||||||
"lazy_load": "Lazily load event media"
|
|
||||||
},
|
|
||||||
"menu": {
|
|
||||||
"frigate": "Frigate Menu / Default View",
|
|
||||||
"live": "Live View",
|
|
||||||
"clips": "Clips Gallery",
|
|
||||||
"snapshots": "Snapshots Gallery",
|
|
||||||
"clip": "Latest Clip",
|
|
||||||
"snapshot": "Latest Snapshot",
|
|
||||||
"frigate_ui": "Frigate User Interface",
|
|
||||||
"fullscreen": "Fullscreen",
|
|
||||||
"image": "Static Image",
|
|
||||||
"download": "Download event media"
|
|
||||||
},
|
|
||||||
"control": {
|
|
||||||
"nextprev": "Media Next & Previous Controls",
|
|
||||||
"thumbnails": "Thumbnails",
|
|
||||||
"chevrons": "Chevrons",
|
|
||||||
"none": "None"
|
|
||||||
},
|
|
||||||
"dimensions": {
|
|
||||||
"aspect_ratio_mode": "Aspect Ratio Mode",
|
|
||||||
"aspect_ratio": "Default aspect ratio (Optional)"
|
|
||||||
},
|
|
||||||
"aspect_ratio_mode": {
|
|
||||||
"unconstrained": "Unconstrained aspect ratio",
|
|
||||||
"dynamic": "Aspect ratio adjusts to media",
|
|
||||||
"static": "Static aspect ratio"
|
|
||||||
},
|
|
||||||
"menu_mode": {
|
|
||||||
"none": "No menu",
|
|
||||||
"hidden-top": "Hidden Top",
|
|
||||||
"hidden-left": "Hidden Left",
|
|
||||||
"hidden-bottom": "Hidden Bottom",
|
|
||||||
"hidden-right": "Hidden Right",
|
|
||||||
"overlay-top": "Overlay Top",
|
|
||||||
"overlay-left": "Overlay Left",
|
|
||||||
"overlay-bottom": "Overlay Bottom",
|
|
||||||
"overlay-right": "Overlay Right",
|
|
||||||
"hover-top": "Hover Top",
|
|
||||||
"hover-left": "Hover Left",
|
|
||||||
"hover-bottom": "Hover Bottom",
|
|
||||||
"hover-right": "Hover Right",
|
|
||||||
"above": "Above",
|
|
||||||
"below": "Below"
|
|
||||||
},
|
|
||||||
"live_provider": {
|
|
||||||
"frigate": "Frigate",
|
|
||||||
"frigate-jsmpeg": "Frigate JSMpeg",
|
|
||||||
"webrtc": "WebRTC"
|
|
||||||
},
|
|
||||||
"webrtc": {
|
|
||||||
"entity": "Optional WebRTC Camera Entity (Not a Frigate camera)",
|
|
||||||
"url": "Optional WebRTC Camera URL"
|
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"empty_response": "Received empty response from Home Assistant for request",
|
"empty_response": "Received empty response from Home Assistant for request",
|
||||||
@@ -101,7 +122,7 @@
|
|||||||
"invalid_configuration": "Invalid configuration",
|
"invalid_configuration": "Invalid configuration",
|
||||||
"invalid_configuration_no_hint": "No location hint available (bad or missing type?)",
|
"invalid_configuration_no_hint": "No location hint available (bad or missing type?)",
|
||||||
"missing_webrtc": "WebRTC component not found",
|
"missing_webrtc": "WebRTC component not found",
|
||||||
"no_frigate_camera_name": "Cannot autodetect Frigate camera name, you need to either set camera_entity and / or frigate_camera_name",
|
"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",
|
||||||
"jsmpeg_no_sign": "Could not retrieve or sign JSMPEG websocket path",
|
"jsmpeg_no_sign": "Could not retrieve or sign JSMPEG websocket path",
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
|
:host {
|
||||||
|
--video-max-height: none;
|
||||||
|
}
|
||||||
|
|
||||||
ha-hls-player {
|
ha-hls-player {
|
||||||
transform-style: preserve-3d;
|
transform-style: preserve-3d;
|
||||||
}
|
}
|
||||||
|
|
||||||
img,video {
|
img,video {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
+172
-52
@@ -38,7 +38,7 @@ export type FrigateCardView =
|
|||||||
| typeof FRIGATE_CARD_VIEWS_USER_SPECIFIED[number]
|
| typeof FRIGATE_CARD_VIEWS_USER_SPECIFIED[number]
|
||||||
| typeof FRIGATE_CARD_VIEWS_INTERNAL[number];
|
| typeof FRIGATE_CARD_VIEWS_INTERNAL[number];
|
||||||
|
|
||||||
export const FRIGATE_MENU_MODES = [
|
const FRIGATE_MENU_MODES = [
|
||||||
'none',
|
'none',
|
||||||
'hidden-top',
|
'hidden-top',
|
||||||
'hidden-left',
|
'hidden-left',
|
||||||
@@ -55,13 +55,8 @@ export const FRIGATE_MENU_MODES = [
|
|||||||
'above',
|
'above',
|
||||||
'below',
|
'below',
|
||||||
] as const;
|
] as const;
|
||||||
export type FrigateMenuMode = typeof FRIGATE_MENU_MODES[number];
|
|
||||||
|
|
||||||
export const NEXT_PREVIOUS_CONTROL_STYLES = ['none', 'thumbnails', 'chevrons'] as const;
|
export const NEXT_PREVIOUS_CONTROL_STYLES = ['none', 'thumbnails', 'chevrons'] as const;
|
||||||
export type NextPreviousControlStyle = typeof NEXT_PREVIOUS_CONTROL_STYLES[number];
|
|
||||||
|
|
||||||
export const LIVE_PROVIDERS = ['frigate', 'frigate-jsmpeg', 'webrtc'] as const;
|
export const LIVE_PROVIDERS = ['frigate', 'frigate-jsmpeg', 'webrtc'] as const;
|
||||||
export type LiveProvider = typeof LIVE_PROVIDERS[number];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action Types (for "Picture Elements" / Menu)
|
* Action Types (for "Picture Elements" / Menu)
|
||||||
@@ -122,7 +117,7 @@ const elementsBaseSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Picture Element Types
|
* Picture Element Configuration.
|
||||||
*
|
*
|
||||||
* All picture element types are validated (not just the Frigate card custom
|
* All picture element types are validated (not just the Frigate card custom
|
||||||
* ones) as a convenience to present the user with a consistent error display
|
* ones) as a convenience to present the user with a consistent error display
|
||||||
@@ -214,7 +209,7 @@ const customSchema = z
|
|||||||
if (!val.match(/^custom:(?!frigate-card).+/)) {
|
if (!val.match(/^custom:(?!frigate-card).+/)) {
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
code: z.ZodIssueCode.custom,
|
code: z.ZodIssueCode.custom,
|
||||||
message: "Frigate-card custom elements must match specific schemas",
|
message: 'Frigate-card custom elements must match specific schemas',
|
||||||
fatal: true,
|
fatal: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -223,7 +218,7 @@ const customSchema = z
|
|||||||
.passthrough();
|
.passthrough();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom Element Types
|
* Custom Element Types.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const menuIconSchema = iconSchema.merge(
|
export const menuIconSchema = iconSchema.merge(
|
||||||
@@ -269,15 +264,37 @@ export type PictureElement = z.infer<typeof pictureElementSchema>;
|
|||||||
const pictureElementsSchema = pictureElementSchema.array().optional();
|
const pictureElementsSchema = pictureElementSchema.array().optional();
|
||||||
export type PictureElements = z.infer<typeof pictureElementsSchema>;
|
export type PictureElements = z.infer<typeof pictureElementsSchema>;
|
||||||
|
|
||||||
export const frigateCardConfigSchema = z.object({
|
/**
|
||||||
camera_entity: z.string().optional(),
|
* Frigate configuration section.
|
||||||
|
*/
|
||||||
|
const frigateConfigDefault = {
|
||||||
|
client_id: 'frigate' as const,
|
||||||
|
};
|
||||||
|
const frigateConfigDefaultSchema = z
|
||||||
|
.object({
|
||||||
// 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(),
|
url: z.string().optional(),
|
||||||
frigate_client_id: z.string().optional().default('frigate'),
|
client_id: z.string().optional().default(frigateConfigDefault.client_id),
|
||||||
frigate_camera_name: z.string().optional(),
|
camera_name: z.string().optional(),
|
||||||
view_default: z.enum(FRIGATE_CARD_VIEWS_USER_SPECIFIED).optional().default('live'),
|
label: z.string().optional(),
|
||||||
view_timeout: z
|
zone: z.string().optional(),
|
||||||
|
})
|
||||||
|
.default(frigateConfigDefault);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View configuration section.
|
||||||
|
*/
|
||||||
|
const viewConfigDefault = {
|
||||||
|
default: 'live' as const,
|
||||||
|
timeout: 180,
|
||||||
|
};
|
||||||
|
const viewConfigSchema = z
|
||||||
|
.object({
|
||||||
|
default: z
|
||||||
|
.enum(FRIGATE_CARD_VIEWS_USER_SPECIFIED)
|
||||||
|
.optional()
|
||||||
|
.default(viewConfigDefault.default),
|
||||||
|
timeout: z
|
||||||
.number()
|
.number()
|
||||||
.or(
|
.or(
|
||||||
z
|
z
|
||||||
@@ -286,52 +303,129 @@ export const frigateCardConfigSchema = z.object({
|
|||||||
.transform((val) => Number(val)),
|
.transform((val) => Number(val)),
|
||||||
)
|
)
|
||||||
.optional()
|
.optional()
|
||||||
.default(180),
|
.default(viewConfigDefault.timeout),
|
||||||
live_provider: z.enum(LIVE_PROVIDERS).default('frigate'),
|
})
|
||||||
live_preload: z.boolean().default(false),
|
.default(viewConfigDefault);
|
||||||
image: z.string().optional(),
|
|
||||||
webrtc: z
|
/**
|
||||||
|
* Image view configuration section.
|
||||||
|
*/
|
||||||
|
const imageConfigSchema = z
|
||||||
|
.object({
|
||||||
|
src: z.string().optional(),
|
||||||
|
})
|
||||||
|
.optional();
|
||||||
|
export type ImageViewConfig = z.infer<typeof imageConfigSchema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Live view configuration section.
|
||||||
|
*/
|
||||||
|
const liveConfigDefault = {
|
||||||
|
provider: 'frigate' as const,
|
||||||
|
preload: false,
|
||||||
|
};
|
||||||
|
const webrtcConfigSchema = z
|
||||||
.object({
|
.object({
|
||||||
entity: z.string().optional(),
|
entity: z.string().optional(),
|
||||||
url: z.string().optional(),
|
url: z.string().optional(),
|
||||||
})
|
})
|
||||||
.passthrough()
|
.passthrough()
|
||||||
.optional(),
|
.optional();
|
||||||
label: z.string().optional(),
|
export type WebRTCConfig = z.infer<typeof webrtcConfigSchema>;
|
||||||
zone: z.string().optional(),
|
|
||||||
autoplay_clip: z.boolean().default(false),
|
const liveConfigSchema = z
|
||||||
event_viewer: z
|
|
||||||
.object({
|
.object({
|
||||||
lazy_load: z.boolean().default(true),
|
provider: z.enum(LIVE_PROVIDERS).default(liveConfigDefault.provider),
|
||||||
|
preload: z.boolean().default(liveConfigDefault.preload),
|
||||||
|
webrtc: webrtcConfigSchema,
|
||||||
})
|
})
|
||||||
.optional(),
|
.default(liveConfigDefault);
|
||||||
menu_mode: z.enum(FRIGATE_MENU_MODES).optional().default('hidden-top'),
|
|
||||||
menu_buttons: z
|
/**
|
||||||
|
* Menu configuration section.
|
||||||
|
*/
|
||||||
|
const menuConfigDefault = {
|
||||||
|
mode: 'hidden-top' as const,
|
||||||
|
buttons: {
|
||||||
|
frigate: true,
|
||||||
|
live: true,
|
||||||
|
clips: true,
|
||||||
|
snapshots: true,
|
||||||
|
image: false,
|
||||||
|
download: true,
|
||||||
|
frigate_ui: true,
|
||||||
|
fullscreen: true,
|
||||||
|
},
|
||||||
|
button_size: '40px',
|
||||||
|
};
|
||||||
|
const menuConfigSchema = z
|
||||||
.object({
|
.object({
|
||||||
frigate: z.boolean().default(true),
|
mode: z.enum(FRIGATE_MENU_MODES).optional().default(menuConfigDefault.mode),
|
||||||
live: z.boolean().default(true),
|
buttons: z
|
||||||
clips: z.boolean().default(true),
|
.object({
|
||||||
snapshots: z.boolean().default(true),
|
frigate: z.boolean().default(menuConfigDefault.buttons.frigate),
|
||||||
image: z.boolean().default(false),
|
live: z.boolean().default(menuConfigDefault.buttons.live),
|
||||||
download: z.boolean().default(true),
|
clips: z.boolean().default(menuConfigDefault.buttons.clips),
|
||||||
frigate_ui: z.boolean().default(true),
|
snapshots: z.boolean().default(menuConfigDefault.buttons.snapshots),
|
||||||
fullscreen: z.boolean().default(true),
|
image: z.boolean().default(menuConfigDefault.buttons.image),
|
||||||
|
download: z.boolean().default(menuConfigDefault.buttons.download),
|
||||||
|
frigate_ui: z.boolean().default(menuConfigDefault.buttons.frigate_ui),
|
||||||
|
fullscreen: z.boolean().default(menuConfigDefault.buttons.fullscreen),
|
||||||
})
|
})
|
||||||
.optional(),
|
.default(menuConfigDefault.buttons),
|
||||||
menu_button_size: z.string().default('40px'),
|
button_size: z.string().default(menuConfigDefault.button_size),
|
||||||
update_entities: z.string().array().optional(),
|
})
|
||||||
elements: pictureElementsSchema,
|
.default(menuConfigDefault);
|
||||||
|
export type MenuConfig = z.infer<typeof menuConfigSchema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event viewer configuration section (clip, snapshot, clip-specific, snapshot-specific).
|
||||||
|
*/
|
||||||
|
const viewerConfigDefault = {
|
||||||
|
autoplay_clip: false,
|
||||||
|
lazy_load: true,
|
||||||
|
controls: {
|
||||||
|
next_previous: {
|
||||||
|
size: '48px',
|
||||||
|
style: 'thumbnails' as const,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const nextPreviousControlConfigSchema = z
|
||||||
|
.object({
|
||||||
|
style: z
|
||||||
|
.enum(NEXT_PREVIOUS_CONTROL_STYLES)
|
||||||
|
.default(viewerConfigDefault.controls.next_previous.style),
|
||||||
|
size: z.string().default(viewerConfigDefault.controls.next_previous.size),
|
||||||
|
})
|
||||||
|
.default(viewerConfigDefault.controls.next_previous);
|
||||||
|
export type NextPreviousControlConfig = z.infer<typeof nextPreviousControlConfigSchema>;
|
||||||
|
|
||||||
|
const viewerConfigSchema = z
|
||||||
|
.object({
|
||||||
|
autoplay_clip: z.boolean().default(viewerConfigDefault.autoplay_clip),
|
||||||
|
lazy_load: z.boolean().default(viewerConfigDefault.lazy_load),
|
||||||
controls: z
|
controls: z
|
||||||
.object({
|
.object({
|
||||||
nextprev: z.enum(NEXT_PREVIOUS_CONTROL_STYLES).default('thumbnails'),
|
next_previous: nextPreviousControlConfigSchema,
|
||||||
nextprev_size: z.string().default('48px'),
|
|
||||||
})
|
})
|
||||||
.optional(),
|
.default(viewerConfigDefault.controls),
|
||||||
dimensions: z
|
})
|
||||||
|
.default(viewerConfigDefault);
|
||||||
|
export type ViewerConfig = z.infer<typeof viewerConfigSchema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dimensions configuration section.
|
||||||
|
*/
|
||||||
|
const dimensionsConfigDefault = {
|
||||||
|
aspect_ratio_mode: 'dynamic' as const,
|
||||||
|
aspect_ratio: [16, 9],
|
||||||
|
};
|
||||||
|
const dimensionsConfigSchema = z
|
||||||
.object({
|
.object({
|
||||||
aspect_ratio_mode: z
|
aspect_ratio_mode: z
|
||||||
.enum(['dynamic', 'static', 'unconstrained'])
|
.enum(['dynamic', 'static', 'unconstrained'])
|
||||||
.default('dynamic'),
|
.default(dimensionsConfigDefault.aspect_ratio_mode),
|
||||||
aspect_ratio: z
|
aspect_ratio: z
|
||||||
.number()
|
.number()
|
||||||
.array()
|
.array()
|
||||||
@@ -342,9 +436,27 @@ export const frigateCardConfigSchema = z.object({
|
|||||||
.regex(/^\s*\d+\s*[:\/]\s*\d+\s*$/)
|
.regex(/^\s*\d+\s*[:\/]\s*\d+\s*$/)
|
||||||
.transform((input) => input.split(/[:\/]/).map((d) => Number(d))),
|
.transform((input) => input.split(/[:\/]/).map((d) => Number(d))),
|
||||||
)
|
)
|
||||||
.default([16, 9]),
|
.default(dimensionsConfigDefault.aspect_ratio),
|
||||||
})
|
})
|
||||||
.optional(),
|
.default(dimensionsConfigDefault);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main card config.
|
||||||
|
*/
|
||||||
|
export const frigateCardConfigSchema = z.object({
|
||||||
|
camera_entity: z.string().optional(),
|
||||||
|
|
||||||
|
frigate: frigateConfigDefaultSchema,
|
||||||
|
view: viewConfigSchema,
|
||||||
|
menu: menuConfigSchema,
|
||||||
|
live: liveConfigSchema,
|
||||||
|
event_viewer: viewerConfigSchema,
|
||||||
|
image: imageConfigSchema,
|
||||||
|
elements: pictureElementsSchema,
|
||||||
|
dimensions: dimensionsConfigSchema,
|
||||||
|
|
||||||
|
// Entities that should trigger a card update.
|
||||||
|
update_entities: z.string().array().optional(),
|
||||||
|
|
||||||
// Stock lovelace card config.
|
// Stock lovelace card config.
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
@@ -354,6 +466,14 @@ export const frigateCardConfigSchema = z.object({
|
|||||||
});
|
});
|
||||||
export type FrigateCardConfig = z.infer<typeof frigateCardConfigSchema>;
|
export type FrigateCardConfig = z.infer<typeof frigateCardConfigSchema>;
|
||||||
|
|
||||||
|
export const frigateCardConfigDefaults = {
|
||||||
|
frigate: frigateConfigDefault,
|
||||||
|
view: viewConfigDefault,
|
||||||
|
menu: menuConfigDefault,
|
||||||
|
live: liveConfigDefault,
|
||||||
|
event_viewer: viewerConfigDefault,
|
||||||
|
};
|
||||||
|
|
||||||
// Schema for card (non-user configured) menu icons.
|
// Schema for card (non-user configured) menu icons.
|
||||||
const internalMenuIconSchema = z.object({
|
const internalMenuIconSchema = z.object({
|
||||||
type: z.literal('internal-menu-icon'),
|
type: z.literal('internal-menu-icon'),
|
||||||
@@ -404,8 +524,8 @@ export interface Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface MenuInteraction {
|
export interface MenuInteraction {
|
||||||
interaction: "hold" | "tap" | "double_tap",
|
interaction: 'hold' | 'tap' | 'double_tap';
|
||||||
button: MenuButton,
|
button: MenuButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user