Add configuration for new live view.
This commit is contained in:
+24
-24
@@ -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');
|
||||||
|
|||||||
@@ -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>.
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user