Make mini timelines editable.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// TODO: When a media viewer is first loaded the selected child won't work (because the underlying carousel has not yet rendered)
|
||||
// TODO: Make minitimeline configurable in the editor
|
||||
// TODO: time on recording != time on timeline in media viewer after scrolling around
|
||||
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
|
||||
@@ -84,6 +84,17 @@ export const CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL =
|
||||
`${CONF_MEDIA_VIEWER}.controls.thumbnails.show_timeline_control` as const;
|
||||
export const CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SIZE =
|
||||
`${CONF_MEDIA_VIEWER}.controls.thumbnails.size` as const;
|
||||
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD =
|
||||
`${CONF_MEDIA_VIEWER}.controls.timeline.clustering_threshold` as const;
|
||||
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MEDIA =
|
||||
`${CONF_MEDIA_VIEWER}.controls.timeline.media` as const;
|
||||
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MODE =
|
||||
`${CONF_MEDIA_VIEWER}.controls.timeline.mode` as const;
|
||||
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS =
|
||||
`${CONF_MEDIA_VIEWER}.controls.timeline.show_recordings` as const;
|
||||
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_WINDOW_SECONDS =
|
||||
`${CONF_MEDIA_VIEWER}.controls.timeline.window_seconds` as const;
|
||||
|
||||
export const CONF_MEDIA_VIEWER_CONTROLS_TITLE_MODE =
|
||||
`${CONF_MEDIA_VIEWER}.controls.title.mode` as const;
|
||||
export const CONF_MEDIA_VIEWER_CONTROLS_TITLE_DURATION_SECONDS =
|
||||
@@ -115,6 +126,16 @@ export const CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL =
|
||||
`${CONF_LIVE}.controls.thumbnails.show_favorite_control` as const;
|
||||
export const CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL =
|
||||
`${CONF_LIVE}.controls.thumbnails.show_timeline_control` as const;
|
||||
export const CONF_LIVE_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD =
|
||||
`${CONF_LIVE}.control s.timeline.clustering_threshold` as const;
|
||||
export const CONF_LIVE_CONTROLS_TIMELINE_MEDIA =
|
||||
`${CONF_LIVE}.controls.timeline.media` as const;
|
||||
export const CONF_LIVE_CONTROLS_TIMELINE_MODE =
|
||||
`${CONF_LIVE}.controls.timeline.mode` as const;
|
||||
export const CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS =
|
||||
`${CONF_LIVE}.controls.timeline.show_recordings` as const;
|
||||
export const CONF_LIVE_CONTROLS_TIMELINE_WINDOW_SECONDS =
|
||||
`${CONF_LIVE}.controls.timeline.window_seconds` as const;
|
||||
export const CONF_LIVE_CONTROLS_TITLE_MODE = `${CONF_LIVE}.controls.title.mode` as const;
|
||||
export const CONF_LIVE_CONTROLS_TITLE_DURATION_SECONDS =
|
||||
`${CONF_LIVE}.controls.title.duration_seconds` as const;
|
||||
|
||||
+103
-6
@@ -54,6 +54,11 @@ import {
|
||||
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
|
||||
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
|
||||
CONF_LIVE_CONTROLS_THUMBNAILS_SIZE,
|
||||
CONF_LIVE_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD,
|
||||
CONF_LIVE_CONTROLS_TIMELINE_MEDIA,
|
||||
CONF_LIVE_CONTROLS_TIMELINE_MODE,
|
||||
CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
||||
CONF_LIVE_CONTROLS_TIMELINE_WINDOW_SECONDS,
|
||||
CONF_LIVE_CONTROLS_TITLE_DURATION_SECONDS,
|
||||
CONF_LIVE_CONTROLS_TITLE_MODE,
|
||||
CONF_LIVE_DRAGGABLE,
|
||||
@@ -76,6 +81,11 @@ import {
|
||||
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
|
||||
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
|
||||
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SIZE,
|
||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD,
|
||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MEDIA,
|
||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MODE,
|
||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_WINDOW_SECONDS,
|
||||
CONF_MEDIA_VIEWER_CONTROLS_TITLE_DURATION_SECONDS,
|
||||
CONF_MEDIA_VIEWER_CONTROLS_TITLE_MODE,
|
||||
CONF_MEDIA_VIEWER_DRAGGABLE,
|
||||
@@ -136,8 +146,10 @@ const MENU_CAMERAS_WEBRTC = 'cameras.webrtc';
|
||||
const MENU_EVENT_GALLERY_CONTROLS = 'event_gallery.controls';
|
||||
const MENU_IMAGE_LAYOUT = 'image.layout';
|
||||
const MENU_LIVE_CONTROLS = 'live.controls';
|
||||
const MENU_LIVE_CONTROLS_TIMELINE = 'live.controls.timeline';
|
||||
const MENU_LIVE_LAYOUT = 'live.layout';
|
||||
const MENU_MEDIA_VIEWER_CONTROLS = 'media_viewer.controls';
|
||||
const MENU_MEDIA_VIEWER_CONTROLS_TIMELINE = 'media_viewer.controls.timeline';
|
||||
const MENU_MEDIA_VIEWER_LAYOUT = 'media_viewer.layout';
|
||||
const MENU_TIMELINE_CONTROLS = 'timeline.controls';
|
||||
const MENU_OPTIONS = 'options';
|
||||
@@ -421,6 +433,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
{ value: 'fill', label: localize('config.common.layout.fits.fill') },
|
||||
];
|
||||
|
||||
protected _miniTimelineModes: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{ value: 'none', label: localize('config.timeline.mini.modes.none') },
|
||||
{ value: 'above', label: localize('config.timeline.mini.modes.above') },
|
||||
{ value: 'below', label: localize('config.timeline.mini.modes.below') },
|
||||
];
|
||||
|
||||
public setConfig(config: RawFrigateCardConfig): void {
|
||||
// Note: This does not use Zod to parse the configuration, so it may be
|
||||
// partially or completely invalid. It's more useful to have a partially
|
||||
@@ -819,6 +838,71 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the core timeline controls (mini or full timeline),
|
||||
* @param configPathWindowSeconds Timeline window config path.
|
||||
* @param configPathClusteringThreshold Clustering threshold config path.
|
||||
* @param configPathTimelineMedia Timeline media config path.
|
||||
* @param configPathShowRecordings Show recordings config path.
|
||||
* @param defaultShowRecordings Default value of show_recordings.
|
||||
* @returns A rendered template.
|
||||
*/
|
||||
protected _renderTimelineCoreControls(
|
||||
configPathWindowSeconds: string,
|
||||
configPathClusteringThreshold: string,
|
||||
configPathTimelineMedia: string,
|
||||
configPathShowRecordings: string,
|
||||
defaultShowRecordings: boolean,
|
||||
): TemplateResult {
|
||||
return html` ${this._renderNumberInput(configPathWindowSeconds, {
|
||||
label: localize(`config.${CONF_TIMELINE_WINDOW_SECONDS}`),
|
||||
})}
|
||||
${this._renderNumberInput(configPathClusteringThreshold, {
|
||||
label: localize(`config.${CONF_TIMELINE_CLUSTERING_THRESHOLD}`),
|
||||
})}
|
||||
${this._renderOptionSelector(configPathTimelineMedia, this._timelineMediaTypes, {
|
||||
label: localize(`config.${CONF_TIMELINE_MEDIA}`),
|
||||
})}
|
||||
${this._renderSwitch(configPathShowRecordings, defaultShowRecordings, {
|
||||
label: localize(`config.${CONF_TIMELINE_SHOW_RECORDINGS}`),
|
||||
})}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the mini timeline controls.
|
||||
* @param domain The submenu domain.
|
||||
* @param configPathWindowSeconds Timeline window config path.
|
||||
* @param configPathClusteringThreshold Clustering threshold config path.
|
||||
* @param configPathTimelineMedia Timeline media config path.
|
||||
* @param configPathShowRecordings Show recordings config path.
|
||||
* @returns A rendered template.
|
||||
*/
|
||||
protected _renderMiniTimeline(
|
||||
domain: string,
|
||||
configPathMode: string,
|
||||
configPathWindowSeconds: string,
|
||||
configPathClusteringThreshold: string,
|
||||
configPathTimelineMedia: string,
|
||||
configPathShowRecordings: string,
|
||||
): TemplateResult | void {
|
||||
return this._putInSubmenu(
|
||||
domain,
|
||||
true,
|
||||
'config.timeline.mini.options',
|
||||
{ name: 'mdi:chart-gantt' },
|
||||
html` ${this._renderOptionSelector(configPathMode, this._miniTimelineModes, {
|
||||
label: localize('config.timeline.mini.mode'),
|
||||
})}
|
||||
${this._renderTimelineCoreControls(
|
||||
configPathWindowSeconds,
|
||||
configPathClusteringThreshold,
|
||||
configPathTimelineMedia,
|
||||
configPathShowRecordings,
|
||||
frigateCardConfigDefaults.mini_timeline.show_recordings,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a camera section.
|
||||
* @param cameras The full array of cameras.
|
||||
@@ -1312,6 +1396,14 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
max: 60,
|
||||
},
|
||||
)}
|
||||
${this._renderMiniTimeline(
|
||||
MENU_LIVE_CONTROLS_TIMELINE,
|
||||
CONF_LIVE_CONTROLS_TIMELINE_MODE,
|
||||
CONF_LIVE_CONTROLS_TIMELINE_WINDOW_SECONDS,
|
||||
CONF_LIVE_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD,
|
||||
CONF_LIVE_CONTROLS_TIMELINE_MEDIA,
|
||||
CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
||||
)}
|
||||
`,
|
||||
)}
|
||||
${this._renderMediaLayout(
|
||||
@@ -1429,6 +1521,14 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
CONF_MEDIA_VIEWER_CONTROLS_TITLE_DURATION_SECONDS,
|
||||
{ min: 0, max: 60 },
|
||||
)}
|
||||
${this._renderMiniTimeline(
|
||||
MENU_MEDIA_VIEWER_CONTROLS_TIMELINE,
|
||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MODE,
|
||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_WINDOW_SECONDS,
|
||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD,
|
||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MEDIA,
|
||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
||||
)}
|
||||
`,
|
||||
)}
|
||||
${this._renderMediaLayout(
|
||||
@@ -1458,13 +1558,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
${this._renderOptionSetHeader('timeline')}
|
||||
${this._expandedMenus[MENU_OPTIONS] === 'timeline'
|
||||
? html` <div class="values">
|
||||
${this._renderNumberInput(CONF_TIMELINE_WINDOW_SECONDS)}
|
||||
${this._renderNumberInput(CONF_TIMELINE_CLUSTERING_THRESHOLD)}
|
||||
${this._renderOptionSelector(
|
||||
${this._renderTimelineCoreControls(
|
||||
CONF_TIMELINE_WINDOW_SECONDS,
|
||||
CONF_TIMELINE_CLUSTERING_THRESHOLD,
|
||||
CONF_TIMELINE_MEDIA,
|
||||
this._timelineMediaTypes,
|
||||
)}
|
||||
${this._renderSwitch(
|
||||
CONF_TIMELINE_SHOW_RECORDINGS,
|
||||
defaults.timeline.show_recordings,
|
||||
)}
|
||||
|
||||
@@ -254,6 +254,15 @@
|
||||
"size": "Timeline thumbnails size in pixels"
|
||||
}
|
||||
},
|
||||
"mini": {
|
||||
"options": "Mini Timeline",
|
||||
"mode": "Mode",
|
||||
"modes": {
|
||||
"none": "None",
|
||||
"above": "Above",
|
||||
"below": "Below"
|
||||
}
|
||||
},
|
||||
"media": "The media the timeline displays",
|
||||
"medias": {
|
||||
"all": "All media types",
|
||||
|
||||
@@ -1240,6 +1240,7 @@ export const frigateCardConfigDefaults = {
|
||||
event_gallery: galleryConfigDefault,
|
||||
image: imageConfigDefault,
|
||||
timeline: timelineConfigDefault,
|
||||
mini_timeline: timelineCoreConfigDefault,
|
||||
};
|
||||
|
||||
const menuButtonSchema = z.discriminatedUnion('type', [
|
||||
|
||||
Reference in New Issue
Block a user