Add configuration for new live view.

This commit is contained in:
Dermot Duffy
2022-01-14 21:31:16 -08:00
parent 38cddd0f18
commit af3b9092fd
5 changed files with 96 additions and 42 deletions
+24 -24
View File
@@ -1,6 +1,3 @@
// TODO title on hover over camera
// TODO controls
// TODO lazy loading configuration
// TODO editor // TODO editor
// TODO readme // TODO readme
// TODO search for TODOs // TODO search for TODOs
@@ -214,7 +211,7 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
return { return {
startIndex: startIndex < 0 ? undefined : startIndex, startIndex: startIndex < 0 ? undefined : startIndex,
// TODO: draggable: this.viewerConfig.draggable, draggable: this.liveConfig?.draggable,
}; };
} }
@@ -227,7 +224,7 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
*/ */
protected _getLazyLoadCount(): number | null { protected _getLazyLoadCount(): number | null {
// Defaults to fully-lazy loading. // Defaults to fully-lazy loading.
return 0; return this.liveConfig?.lazy_load === false ? null : 0;
} }
/** /**
@@ -239,11 +236,14 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
return []; return [];
} }
return Array.from(this.cameras.values()).map((cameraConfig, index) => { return Array.from(this.cameras.values()).map((cameraConfig, index) => {
let refreshedConfig = {...cameraConfig}; let refreshedConfig = { ...cameraConfig };
if (this.hass) { if (this.hass) {
refreshedConfig = refreshCameraConfigDynamicParameters(this.hass, refreshedConfig); refreshedConfig = refreshCameraConfigDynamicParameters(
this.hass,
refreshedConfig,
);
} }
return this._renderLive(refreshedConfig, index) return this._renderLive(refreshedConfig, index);
}); });
} }
@@ -303,13 +303,13 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
if (currentIndex > 0) { if (currentIndex > 0) {
prev = this.cameras.get(keys[currentIndex - 1]) ?? null; prev = this.cameras.get(keys[currentIndex - 1]) ?? null;
if (prev) { if (prev) {
prev = refreshCameraConfigDynamicParameters(this.hass, {...prev}); prev = refreshCameraConfigDynamicParameters(this.hass, { ...prev });
} }
} }
if (currentIndex + 1 < this.cameras.size) { if (currentIndex + 1 < this.cameras.size) {
next = this.cameras.get(keys[currentIndex + 1]) ?? null; next = this.cameras.get(keys[currentIndex + 1]) ?? null;
if (next) { if (next) {
next = refreshCameraConfigDynamicParameters(this.hass, {...next}); next = refreshCameraConfigDynamicParameters(this.hass, { ...next });
} }
} }
return [prev, next]; return [prev, next];
@@ -318,14 +318,18 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
/** /**
* Handle updating of the next/previous controls when the carousel is moved. * Handle updating of the next/previous controls when the carousel is moved.
*/ */
protected _selectSlideNextPreviousHandler(): void { protected _selectSlideNextPreviousHandler(): void {
const updateNextPreviousControl = (control: FrigateCardNextPreviousControl, direction: 'previous' | 'next'): void => { const updateNextPreviousControl = (
control: FrigateCardNextPreviousControl,
direction: 'previous' | 'next',
): void => {
const [prev, next] = this._getCameraNeighbors(); const [prev, next] = this._getCameraNeighbors();
const target = direction == 'previous' ? prev : next; const target = direction == 'previous' ? prev : next;
control.disabled = (target == null) control.disabled = target == null;
control.title = (target && target.title ? target.title : '') control.title = target && target.title ? target.title : '';
} control.icon = target && target.icon ? target.icon : undefined;
};
if (this._previousControlRef.value) { if (this._previousControlRef.value) {
updateNextPreviousControl(this._previousControlRef.value, 'previous'); updateNextPreviousControl(this._previousControlRef.value, 'previous');
@@ -352,11 +356,9 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
<frigate-card-next-previous-control <frigate-card-next-previous-control
${ref(this._previousControlRef)} ${ref(this._previousControlRef)}
.direction=${'previous'} .direction=${'previous'}
.controlConfig=${{ .controlConfig=${this.liveConfig?.controls.next_previous}
style: 'chevrons',
size: '40px',
}}
.title=${prev && prev.title ? prev.title : ''} .title=${prev && prev.title ? prev.title : ''}
.icon=${prev && prev.icon ? prev.icon : undefined}
?disabled=${prev == null} ?disabled=${prev == null}
@click=${() => { @click=${() => {
this._nextPreviousHandler('previous'); this._nextPreviousHandler('previous');
@@ -369,11 +371,9 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
<frigate-card-next-previous-control <frigate-card-next-previous-control
${ref(this._nextControlRef)} ${ref(this._nextControlRef)}
.direction=${'next'} .direction=${'next'}
.controlConfig=${{ .controlConfig=${this.liveConfig?.controls.next_previous}
style: 'chevrons',
size: '40px',
}}
.title=${next && next.title ? next.title : ''} .title=${next && next.title ? next.title : ''}
.icon=${next && next.icon ? next.icon : undefined}
?disabled=${next == null} ?disabled=${next == null}
@click=${() => { @click=${() => {
this._nextPreviousHandler('next'); this._nextPreviousHandler('next');
+15 -4
View File
@@ -23,6 +23,9 @@ export class FrigateCardNextPreviousControl extends LitElement {
@property({ attribute: false }) @property({ attribute: false })
public thumbnail?: string; public thumbnail?: string;
@property({ attribute: false })
public icon?: string;
@property({ attribute: true, type: Boolean }) @property({ attribute: true, type: Boolean })
public disabled = false; public disabled = false;
@@ -36,12 +39,20 @@ export class FrigateCardNextPreviousControl extends LitElement {
previous: this.direction == 'previous', previous: this.direction == 'previous',
next: this.direction == 'next', next: this.direction == 'next',
thumbnails: this._controlConfig.style == 'thumbnails', thumbnails: this._controlConfig.style == 'thumbnails',
chevrons: this._controlConfig.style == 'chevrons', icons: ['chevrons', 'icons'].includes(this._controlConfig.style),
button: this._controlConfig.style == 'chevrons', button: ['chevrons', 'icons'].includes(this._controlConfig.style),
}; };
if (this._controlConfig.style == 'chevrons') { if (['chevrons', 'icons'].includes(this._controlConfig.style)) {
const icon = this.direction == 'previous' ? 'mdi:chevron-left' : 'mdi:chevron-right'; let icon: string;
if (this._controlConfig.style === 'chevrons') {
icon = this.direction == 'previous' ? 'mdi:chevron-left' : 'mdi:chevron-right';
} else {
if (!this.icon) {
return html``;
}
icon = this.icon
}
// 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
// attributes can be removed from the <ha-icon-button>. // attributes can be removed from the <ha-icon-button>.
+16 -3
View File
@@ -255,6 +255,18 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
}; };
} }
/**
* Returns the number of slides to lazily load. 0 means all slides are lazy
* loaded, 1 means that 1 slide on each side of the currently selected slide
* should lazy load, etc. `null` means lazy loading is disabled and everything
* should load simultaneously.
* @returns
*/
protected _getLazyLoadCount(): number | null {
// Defaults to fully-lazy loading.
return this.viewerConfig?.lazy_load === false ? null : 0;
}
/** /**
* Get the previous and next true media items from the current view. * Get the previous and next true media items from the current view.
* @returns A BrowseMediaNeighbors with indices and objects of true media * @returns A BrowseMediaNeighbors with indices and objects of true media
@@ -558,7 +570,7 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
autoplay = this.viewerConfig.autoplay_clip; autoplay = this.viewerConfig.autoplay_clip;
} }
const lazyLoad = this.viewerConfig.lazy_load; const lazyLoad = (this._getLazyLoadCount() !== null);
return html` return html`
<div class="embla__slide"> <div class="embla__slide">
@@ -616,8 +628,9 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
@load="${(e: Event) => { @load="${(e: Event) => {
if ( if (
this.viewerConfig && this.viewerConfig &&
(!this.viewerConfig.lazy_load || // This handler will be called on the empty image, only call
this._slideHasBeenLazyLoaded[slideIndex]) // the below when it's the 'real image'.
(!lazyLoad || this._slideHasBeenLazyLoaded[slideIndex])
) { ) {
this._mediaLoadedHandler(slideIndex, createMediaShowInfo(e)); this._mediaLoadedHandler(slideIndex, createMediaShowInfo(e));
} }
+1 -1
View File
@@ -21,7 +21,7 @@
right: var(--frigate-card-next-position); right: var(--frigate-card-next-position);
} }
.controls.chevrons { .controls.icons {
top: calc(50% - (40px / 2)); top: calc(50% - (40px / 2));
} }
+40 -10
View File
@@ -57,8 +57,7 @@ const FRIGATE_MENU_MODES = [
'above', 'above',
'below', 'below',
] as const; ] as const;
export const NEXT_PREVIOUS_CONTROL_STYLES = ['none', 'thumbnails', 'chevrons'] as const; const LIVE_PROVIDERS = ['frigate', 'frigate-jsmpeg', 'webrtc'] as const;
export const LIVE_PROVIDERS = ['frigate', 'frigate-jsmpeg', 'webrtc'] as const;
/** /**
* Action Types (for "Picture Elements" / Menu) * Action Types (for "Picture Elements" / Menu)
@@ -430,13 +429,29 @@ const thumbnailsControlSchema = z.object({
}); });
export type ThumbnailsControlConfig = z.infer<typeof thumbnailsControlSchema>; export type ThumbnailsControlConfig = z.infer<typeof thumbnailsControlSchema>;
/**
* Next/Previous Control configuration section.
*/
const nextPreviousControlConfigSchema = z.object({
style: z.enum(['none', 'chevrons', 'icons', 'thumbnails']),
size: z.string(),
});
export type NextPreviousControlConfig = z.infer<typeof nextPreviousControlConfigSchema>;
/** /**
* Live view configuration section. * Live view configuration section.
*/ */
const liveConfigDefault = { const liveConfigDefault = {
provider: 'frigate' as const, provider: 'frigate' as const,
preload: false, preload: false,
lazy_load: true,
draggable: true,
controls: { controls: {
next_previous: {
size: '48px',
style: 'chevrons' as const,
},
thumbnails: { thumbnails: {
media: 'clips' as const, media: 'clips' as const,
}, },
@@ -474,14 +489,28 @@ const jsmpegConfigSchema = z
.optional(); .optional();
export type JSMPEGConfig = z.infer<typeof jsmpegConfigSchema>; export type JSMPEGConfig = z.infer<typeof jsmpegConfigSchema>;
const liveNextPreviousControlConfigSchema = nextPreviousControlConfigSchema.merge(
z.object({
style: z
.enum(['none', 'chevrons', 'icons'])
.default(liveConfigDefault.controls.next_previous.style),
size: z.string().default(liveConfigDefault.controls.next_previous.size),
}));
export type LiveNextPreviousControlConfig = z.infer<typeof liveNextPreviousControlConfigSchema>;
const liveConfigSchema = z const liveConfigSchema = z
.object({ .object({
provider: z.enum(LIVE_PROVIDERS).default(liveConfigDefault.provider), provider: z.enum(LIVE_PROVIDERS).default(liveConfigDefault.provider),
preload: z.boolean().default(liveConfigDefault.preload), preload: z.boolean().default(liveConfigDefault.preload),
webrtc: webrtcConfigSchema, webrtc: webrtcConfigSchema,
jsmpeg: jsmpegConfigSchema, jsmpeg: jsmpegConfigSchema,
lazy_load: z.boolean().default(liveConfigDefault.lazy_load),
draggable: z.boolean().default(liveConfigDefault.draggable),
controls: z controls: z
.object({ .object({
next_previous: liveNextPreviousControlConfigSchema.default(
liveConfigDefault.controls.next_previous,
),
thumbnails: thumbnailsControlSchema thumbnails: thumbnailsControlSchema
.merge( .merge(
z.object({ z.object({
@@ -555,13 +584,14 @@ const viewerConfigDefault = {
}, },
}, },
}; };
const nextPreviousControlConfigSchema = z.object({ const viewerNextPreviousControlConfigSchema = nextPreviousControlConfigSchema.merge(
style: z z.object({
.enum(NEXT_PREVIOUS_CONTROL_STYLES) style: z
.default(viewerConfigDefault.controls.next_previous.style), .enum(['none', 'thumbnails', 'chevrons'])
size: z.string().default(viewerConfigDefault.controls.next_previous.size), .default(viewerConfigDefault.controls.next_previous.style),
}); size: z.string().default(viewerConfigDefault.controls.next_previous.size),
export type NextPreviousControlConfig = z.infer<typeof nextPreviousControlConfigSchema>; }));
export type ViewerNextPreviousControlConfig = z.infer<typeof viewerNextPreviousControlConfigSchema>;
const viewerConfigSchema = z const viewerConfigSchema = z
.object({ .object({
@@ -570,7 +600,7 @@ const viewerConfigSchema = z
draggable: z.boolean().default(viewerConfigDefault.draggable), draggable: z.boolean().default(viewerConfigDefault.draggable),
controls: z controls: z
.object({ .object({
next_previous: nextPreviousControlConfigSchema.default( next_previous: viewerNextPreviousControlConfigSchema.default(
viewerConfigDefault.controls.next_previous, viewerConfigDefault.controls.next_previous,
), ),
thumbnails: thumbnailsControlSchema.default( thumbnails: thumbnailsControlSchema.default(