Add ability to set timeline pan mode via config.

This commit is contained in:
Dermot Duffy
2024-03-10 20:40:18 -07:00
parent b96872fa9e
commit 463f4bf32a
11 changed files with 144 additions and 85 deletions
+6 -1
View File
@@ -632,6 +632,7 @@ live:
| Option | Default | Overridable | Description | | Option | Default | Overridable | Description |
| - | - | - | - | | - | - | - | - |
| `mode` | `none` | :white_check_mark: | Whether to show the thumbnail carousel `below` the media, `above` the media, in a drawer to the `left` or `right` of the media or to hide it entirely (`none`).| | `mode` | `none` | :white_check_mark: | Whether to show the thumbnail carousel `below` the media, `above` the media, in a drawer to the `left` or `right` of the media or to hide it entirely (`none`).|
| `pan_mode` | `pan`| :white_check_mark: | If set to `pan` then dragging the timeline will have no effect on the media. If set to `seek` will open media for that time and seek to that position . If set to `seek-in-media` will seek within the opened media only. If set to `seek-in-camera` will seek within the selected camera only. |
| `style` | `ribbon` | :white_check_mark: | Whether the timeline should show events as a single flat `ribbon` or a `stack` of events that are clustered using the `clustering_threshold` (below). | | `style` | `ribbon` | :white_check_mark: | Whether the timeline should show events as a single flat `ribbon` or a `stack` of events that are clustered using the `clustering_threshold` (below). |
| `window_seconds` | `3600` | :white_check_mark: | The length of the default timeline in seconds. By default, 1 hour (`3600` seconds) is shown in the timeline. | | `window_seconds` | `3600` | :white_check_mark: | The length of the default timeline in seconds. By default, 1 hour (`3600` seconds) is shown in the timeline. |
| `clustering_threshold` | `3` | :white_check_mark: | The minimum number of overlapping events to allow prior to clustering/grouping them. Higher numbers cause clustering to happen less frequently. Depending on the timescale/zoom of the timeline, the underlying timeline library may still allow overlaps for low values of this parameter -- for a fully "flat" timeline use the `ribbon` style. `0` disables clustering entirely. Only used in the `stack` style of timeline. | | `clustering_threshold` | `3` | :white_check_mark: | The minimum number of overlapping events to allow prior to clustering/grouping them. Higher numbers cause clustering to happen less frequently. Depending on the timescale/zoom of the timeline, the underlying timeline library may still allow overlaps for low values of this parameter -- for a fully "flat" timeline use the `ribbon` style. `0` disables clustering entirely. Only used in the `stack` style of timeline. |
@@ -814,7 +815,9 @@ media_viewer:
| Option | Default | Overridable | Description | | Option | Default | Overridable | Description |
| - | - | - | - | | - | - | - | - |
| `mode` | `none` | :heavy_multiplication_x: | Whether to show the thumbnail carousel `below` the media, `above` the media, in a drawer to the `left` or `right` of the media or to hide it entirely (`none`).| | `mode` | `none` | :heavy_multiplication_x: | Whether to show the thumbnail carousel `below` the media, `above` the media, in a drawer to the
`left` or `right` of the media or to hide it entirely (`none`).|
| `pan_mode` | `pan`| :white_check_mark: | If set to `pan` then dragging the timeline will have no effect on the media. If set to `seek` will open media for that time and seek to that position . If set to `seek-in-media` will seek within the opened media only. If set to `seek-in-camera` will seek within the selected camera only. |
| `style` | `ribbon` | :heavy_multiplication_x: | Whether the timeline should show events as a single flat `ribbon` or a `stack` of events that are clustered using the `clustering_threshold` (below). | | `style` | `ribbon` | :heavy_multiplication_x: | Whether the timeline should show events as a single flat `ribbon` or a `stack` of events that are clustered using the `clustering_threshold` (below). |
| `window_seconds` | `3600` | :heavy_multiplication_x: | The length of the default timeline in seconds. By default, 1 hour (`3600` seconds) is shown in the timeline. | | `window_seconds` | `3600` | :heavy_multiplication_x: | The length of the default timeline in seconds. By default, 1 hour (`3600` seconds) is shown in the timeline. |
| `clustering_threshold` | `3` | :heavy_multiplication_x: | The minimum number of overlapping events to allow prior to clustering/grouping them. Higher numbers cause clustering to happen less frequently. Depending on the timescale/zoom of the timeline, the underlying timeline library may still allow overlaps for low values of this parameter -- for a fully "flat" timeline use the `ribbon` style. `0` disables clustering entirely. Only used in the `stack` style of timeline. | | `clustering_threshold` | `3` | :heavy_multiplication_x: | The minimum number of overlapping events to allow prior to clustering/grouping them. Higher numbers cause clustering to happen less frequently. Depending on the timescale/zoom of the timeline, the underlying timeline library may still allow overlaps for low values of this parameter -- for a fully "flat" timeline use the `ribbon` style. `0` disables clustering entirely. Only used in the `stack` style of timeline. |
@@ -2139,6 +2142,7 @@ live:
timeline: timeline:
style: ribbon style: ribbon
mode: none mode: none
pan_mode: pan
clustering_threshold: 3 clustering_threshold: 3
events_media_type: all events_media_type: all
show_recordings: true show_recordings: true
@@ -2232,6 +2236,7 @@ media_viewer:
timeline: timeline:
style: ribbon style: ribbon
mode: none mode: none
pan_mode: pan
clustering_threshold: 3 clustering_threshold: 3
events_media_type: all events_media_type: all
show_recordings: true show_recordings: true
+31 -30
View File
@@ -39,6 +39,7 @@ import {
FrigateCardView, FrigateCardView,
ThumbnailsControlBaseConfig, ThumbnailsControlBaseConfig,
TimelineCoreConfig, TimelineCoreConfig,
TimelinePanMode,
} from '../config/types'; } from '../config/types';
import { localize } from '../localize/localize'; import { localize } from '../localize/localize';
import timelineCoreStyle from '../scss/timeline-core.scss'; import timelineCoreStyle from '../scss/timeline-core.scss';
@@ -86,11 +87,9 @@ interface TimelineRangeChange extends TimelineWindow {
interface TimelineViewContext { interface TimelineViewContext {
window?: TimelineWindow; window?: TimelineWindow;
panBehavior?: TimelinePanBehavior;
} }
type TimelineItemClickAction = 'play' | 'select'; type TimelineItemClickAction = 'play' | 'select';
type TimelinePanBehavior = 'pan' | 'seek' | 'seek-in-media' | 'seek-in-camera';
declare module 'view' { declare module 'view' {
interface ViewContext { interface ViewContext {
@@ -214,7 +213,7 @@ export class FrigateCardTimelineCore extends LitElement {
public itemClickAction?: TimelineItemClickAction; public itemClickAction?: TimelineItemClickAction;
@state() @state()
protected _panBehavior: TimelinePanBehavior = 'seek'; protected _panMode: TimelinePanMode | null = null;
protected _targetBarVisible = false; protected _targetBarVisible = false;
@@ -288,20 +287,22 @@ export class FrigateCardTimelineCore extends LitElement {
} }
const capabilities = this.cameraManager?.getAggregateCameraCapabilities(cameraIDs); const capabilities = this.cameraManager?.getAggregateCameraCapabilities(cameraIDs);
const panMode = this._getEffectivePanMode();
const panTitle = const panTitle =
this._panBehavior === 'pan' panMode === 'pan'
? localize('timeline.pan_behavior.pan') ? localize('config.common.controls.timeline.pan_modes.pan')
: this._panBehavior === 'seek' : panMode === 'seek'
? localize('timeline.pan_behavior.seek') ? localize('config.common.controls.timeline.pan_modes.seek')
: this._panBehavior === 'seek-in-media' : panMode === 'seek-in-media'
? localize('timeline.pan_behavior.seek-in-media') ? localize('config.common.controls.timeline.pan_modes.seek-in-media')
: localize('timeline.pan_behavior.seek-in-camera'); : localize('config.common.controls.timeline.pan_modes.seek-in-camera');
const panIcon = const panIcon =
this._panBehavior === 'pan' panMode === 'pan'
? 'mdi:pan-horizontal' ? 'mdi:pan-horizontal'
: this._panBehavior === 'seek' : panMode === 'seek'
? 'mdi:filmstrip-box-multiple' ? 'mdi:filmstrip-box-multiple'
: this._panBehavior === 'seek-in-media' : panMode === 'seek-in-media'
? 'mdi:play-box-lock' ? 'mdi:play-box-lock'
: 'mdi:camera-lock'; : 'mdi:camera-lock';
@@ -318,12 +319,12 @@ export class FrigateCardTimelineCore extends LitElement {
? html` <ha-icon ? html` <ha-icon
.icon=${panIcon} .icon=${panIcon}
@click=${() => { @click=${() => {
this._panBehavior = this._panMode =
this._panBehavior === 'pan' panMode === 'pan'
? 'seek' ? 'seek'
: this._panBehavior === 'seek' : panMode === 'seek'
? 'seek-in-media' ? 'seek-in-media'
: this._panBehavior === 'seek-in-media' : panMode === 'seek-in-media'
? 'seek-in-camera' ? 'seek-in-camera'
: 'pan'; : 'pan';
}} }}
@@ -404,15 +405,15 @@ export class FrigateCardTimelineCore extends LitElement {
return; return;
} }
const panMode = this._getEffectivePanMode();
const targetBarOn = const targetBarOn =
this._shouldSupportSeeking() && this._shouldSupportSeeking() &&
(this._panBehavior === 'seek' || (panMode === 'seek' ||
this._panBehavior === 'seek-in-camera' || ((panMode === 'seek-in-camera' || panMode === 'seek-in-media') &&
(this._panBehavior === 'seek-in-media' &&
this._timeline.getSelection().some((id) => { this._timeline.getSelection().some((id) => {
const item = this._timelineSource?.dataset?.get(id); const item = this._timelineSource?.dataset?.get(id);
return ( return (
this._panBehavior !== 'seek-in-camera' || panMode !== 'seek-in-camera' ||
item?.media?.getCameraID() === this.view?.camera, item?.media?.getCameraID() === this.view?.camera,
item && item &&
item.start && item.start &&
@@ -473,6 +474,7 @@ export class FrigateCardTimelineCore extends LitElement {
): Promise<void> { ): Promise<void> {
const results = this.view?.queryResults; const results = this.view?.queryResults;
const media = results?.getResults(); const media = results?.getResults();
const panMode = this._getEffectivePanMode();
if ( if (
!media || !media ||
!results || !results ||
@@ -480,7 +482,7 @@ export class FrigateCardTimelineCore extends LitElement {
!this.view || !this.view ||
!this.hass || !this.hass ||
!this.cameraManager || !this.cameraManager ||
this._panBehavior === 'pan' panMode === 'pan'
) { ) {
return; return;
} }
@@ -488,7 +490,7 @@ export class FrigateCardTimelineCore extends LitElement {
const canSeek = this._shouldSupportSeeking(); const canSeek = this._shouldSupportSeeking();
let newResults: MediaQueriesResults | null = null; let newResults: MediaQueriesResults | null = null;
if (this._panBehavior === 'seek') { if (panMode === 'seek') {
newResults = results newResults = results
.clone() .clone()
.resetSelectedResult() .resetSelectedResult()
@@ -499,7 +501,7 @@ export class FrigateCardTimelineCore extends LitElement {
main: true, main: true,
}, },
); );
} else if (this._panBehavior === 'seek-in-camera') { } else if (panMode === 'seek-in-camera') {
newResults = results newResults = results
.clone() .clone()
.resetSelectedResult() .resetSelectedResult()
@@ -507,7 +509,7 @@ export class FrigateCardTimelineCore extends LitElement {
cameraID: this.view.camera, cameraID: this.view.camera,
}) })
.promoteCameraSelectionToMainSelection(this.view.camera); .promoteCameraSelectionToMainSelection(this.view.camera);
} else if (this._panBehavior === 'seek-in-media') { } else if (panMode === 'seek-in-media') {
newResults = results; newResults = results;
} }
@@ -531,6 +533,10 @@ export class FrigateCardTimelineCore extends LitElement {
.dispatchChangeEvent(this); .dispatchChangeEvent(this);
} }
protected _getEffectivePanMode(): TimelinePanMode {
return this._panMode ?? this.timelineConfig?.pan_mode ?? 'pan';
}
/** /**
* Called whenever the timeline is clicked. * Called whenever the timeline is clicked.
* @param properties The properties of the timeline click event. * @param properties The properties of the timeline click event.
@@ -988,10 +994,6 @@ export class FrigateCardTimelineCore extends LitElement {
: null; : null;
const context = this.view.context?.timeline; const context = this.view.context?.timeline;
if (context && context.panBehavior) {
this._panBehavior = context.panBehavior;
}
if (context && context.window) { if (context && context.window) {
desiredWindow = context.window; desiredWindow = context.window;
} else if (mediaWindow && !rangesOverlap(mediaWindow, timelineWindow)) { } else if (mediaWindow && !rangesOverlap(mediaWindow, timelineWindow)) {
@@ -1107,7 +1109,6 @@ export class FrigateCardTimelineCore extends LitElement {
return { return {
timeline: { timeline: {
...this.view?.context?.timeline, ...this.view?.context?.timeline,
panBehavior: this._panBehavior,
...(newWindow && { window: newWindow }), ...(newWindow && { window: newWindow }),
}, },
}; };
+12
View File
@@ -727,8 +727,17 @@ const timelineCoreConfigDefault = {
window_seconds: 60 * 60, window_seconds: 60 * 60,
show_recordings: true, show_recordings: true,
style: 'stack' as const, style: 'stack' as const,
pan_mode: 'pan' as const,
}; };
export const timelinePanModeSchema = z.enum([
'pan',
'seek',
'seek-in-media',
'seek-in-camera',
]);
export type TimelinePanMode = z.infer<typeof timelinePanModeSchema>;
const timelineCoreConfigSchema = z.object({ const timelineCoreConfigSchema = z.object({
clustering_threshold: z clustering_threshold: z
.number() .number()
@@ -748,6 +757,9 @@ const timelineCoreConfigSchema = z.object({
.optional() .optional()
.default(timelineCoreConfigDefault.show_recordings), .default(timelineCoreConfigDefault.show_recordings),
style: z.enum(['stack', 'ribbon']).optional().default(timelineCoreConfigDefault.style), style: z.enum(['stack', 'ribbon']).optional().default(timelineCoreConfigDefault.style),
pan_mode: timelinePanModeSchema
.optional()
.default(timelineCoreConfigDefault.pan_mode),
}); });
export type TimelineCoreConfig = z.infer<typeof timelineCoreConfigSchema>; export type TimelineCoreConfig = z.infer<typeof timelineCoreConfigSchema>;
+4
View File
@@ -163,6 +163,8 @@ export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_EVENTS_MEDIA_TYPE =
`${CONF_MEDIA_VIEWER}.controls.timeline.events_media_type` as const; `${CONF_MEDIA_VIEWER}.controls.timeline.events_media_type` as const;
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MODE = export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MODE =
`${CONF_MEDIA_VIEWER}.controls.timeline.mode` as const; `${CONF_MEDIA_VIEWER}.controls.timeline.mode` as const;
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_PAN_MODE =
`${CONF_MEDIA_VIEWER}.controls.timeline.pan_mode` as const;
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS = export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS =
`${CONF_MEDIA_VIEWER}.controls.timeline.show_recordings` as const; `${CONF_MEDIA_VIEWER}.controls.timeline.show_recordings` as const;
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_STYLE = export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_STYLE =
@@ -219,6 +221,8 @@ export const CONF_LIVE_CONTROLS_TIMELINE_EVENTS_MEDIA_TYPE =
`${CONF_LIVE}.controls.timeline.events_media_type` as const; `${CONF_LIVE}.controls.timeline.events_media_type` as const;
export const CONF_LIVE_CONTROLS_TIMELINE_MODE = export const CONF_LIVE_CONTROLS_TIMELINE_MODE =
`${CONF_LIVE}.controls.timeline.mode` as const; `${CONF_LIVE}.controls.timeline.mode` as const;
export const CONF_LIVE_CONTROLS_TIMELINE_PAN_MODE =
`${CONF_LIVE}.controls.timeline.pan_mode` as const;
export const CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS = export const CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS =
`${CONF_LIVE}.controls.timeline.show_recordings` as const; `${CONF_LIVE}.controls.timeline.show_recordings` as const;
export const CONF_LIVE_CONTROLS_TIMELINE_STYLE = export const CONF_LIVE_CONTROLS_TIMELINE_STYLE =
+52 -22
View File
@@ -91,6 +91,7 @@ import {
CONF_LIVE_CONTROLS_THUMBNAILS_EVENTS_MEDIA_TYPE, CONF_LIVE_CONTROLS_THUMBNAILS_EVENTS_MEDIA_TYPE,
CONF_LIVE_CONTROLS_THUMBNAILS_MEDIA_TYPE, CONF_LIVE_CONTROLS_THUMBNAILS_MEDIA_TYPE,
CONF_LIVE_CONTROLS_THUMBNAILS_MODE, CONF_LIVE_CONTROLS_THUMBNAILS_MODE,
CONF_LIVE_CONTROLS_TIMELINE_PAN_MODE,
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DETAILS, CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DETAILS,
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DOWNLOAD_CONTROL, CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DOWNLOAD_CONTROL,
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL, CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
@@ -140,6 +141,7 @@ import {
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD, CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD,
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_EVENTS_MEDIA_TYPE, CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_EVENTS_MEDIA_TYPE,
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MODE, CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MODE,
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_PAN_MODE,
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS, CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS,
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_STYLE, CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_STYLE,
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_WINDOW_SECONDS, CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_WINDOW_SECONDS,
@@ -719,6 +721,26 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
}, },
]; ];
protected _timelinePanModes: EditorSelectOption[] = [
{ value: '', label: '' },
{
value: 'pan',
label: localize('config.common.controls.timeline.pan_modes.pan'),
},
{
value: 'seek',
label: localize('config.common.controls.timeline.pan_modes.seek'),
},
{
value: 'seek-in-media',
label: localize('config.common.controls.timeline.pan_modes.seek-in-media'),
},
{
value: 'seek-in-camera',
label: localize('config.common.controls.timeline.pan_modes.seek-in-camera'),
},
];
public setConfig(config: RawFrigateCardConfig): void { public setConfig(config: RawFrigateCardConfig): void {
// Note: This does not use Zod to parse the configuration, so it may be // 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 // partially or completely invalid. It's more useful to have a partially
@@ -1197,30 +1219,34 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
configPathTimelineEventsMediaType: string, configPathTimelineEventsMediaType: string,
configPathShowRecordings: string, configPathShowRecordings: string,
defaultShowRecordings: boolean, defaultShowRecordings: boolean,
configPathPanMode?: string,
): TemplateResult { ): TemplateResult {
return html` ${this._renderOptionSelector( return html`
configPathStyle, ${this._renderOptionSelector(configPathStyle, this._timelineStyleTypes, {
this._timelineStyleTypes,
{
label: localize(`config.common.${CONF_TIMELINE_STYLE}`), label: localize(`config.common.${CONF_TIMELINE_STYLE}`),
}, })}
)} ${configPathPanMode
${this._renderNumberInput(configPathWindowSeconds, { ? this._renderOptionSelector(configPathPanMode, this._timelinePanModes, {
label: localize(`config.common.${CONF_TIMELINE_WINDOW_SECONDS}`), label: localize(`config.common.controls.timeline.pan_mode`),
})} })
${this._renderNumberInput(configPathClusteringThreshold, { : ``}
label: localize(`config.common.${CONF_TIMELINE_CLUSTERING_THRESHOLD}`), ${this._renderNumberInput(configPathWindowSeconds, {
})} label: localize(`config.common.${CONF_TIMELINE_WINDOW_SECONDS}`),
${this._renderOptionSelector( })}
configPathTimelineEventsMediaType, ${this._renderNumberInput(configPathClusteringThreshold, {
this._timelineEventsMediaTypes, label: localize(`config.common.${CONF_TIMELINE_CLUSTERING_THRESHOLD}`),
{ })}
label: localize(`config.common.${CONF_TIMELINE_EVENTS_MEDIA_TYPE}`), ${this._renderOptionSelector(
}, configPathTimelineEventsMediaType,
)} this._timelineEventsMediaTypes,
${this._renderSwitch(configPathShowRecordings, defaultShowRecordings, { {
label: localize(`config.common.${CONF_TIMELINE_SHOW_RECORDINGS}`), label: localize(`config.common.${CONF_TIMELINE_EVENTS_MEDIA_TYPE}`),
})}`; },
)}
${this._renderSwitch(configPathShowRecordings, defaultShowRecordings, {
label: localize(`config.common.${CONF_TIMELINE_SHOW_RECORDINGS}`),
})}
`;
} }
/** /**
@@ -1241,6 +1267,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
configPathTimelineEventsMediaType: string, configPathTimelineEventsMediaType: string,
configPathShowRecordings: string, configPathShowRecordings: string,
showRecordingsDefault: boolean, showRecordingsDefault: boolean,
configPathPanMode: string,
): TemplateResult | void { ): TemplateResult | void {
return this._putInSubmenu( return this._putInSubmenu(
domain, domain,
@@ -1257,6 +1284,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
configPathTimelineEventsMediaType, configPathTimelineEventsMediaType,
configPathShowRecordings, configPathShowRecordings,
showRecordingsDefault, showRecordingsDefault,
configPathPanMode,
)}`, )}`,
); );
} }
@@ -2208,6 +2236,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
CONF_LIVE_CONTROLS_TIMELINE_EVENTS_MEDIA_TYPE, CONF_LIVE_CONTROLS_TIMELINE_EVENTS_MEDIA_TYPE,
CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS, CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS,
this._defaults.live.controls.timeline.show_recordings, this._defaults.live.controls.timeline.show_recordings,
CONF_LIVE_CONTROLS_TIMELINE_PAN_MODE,
)} )}
${this._putInSubmenu( ${this._putInSubmenu(
MENU_LIVE_CONTROLS_PTZ, MENU_LIVE_CONTROLS_PTZ,
@@ -2394,6 +2423,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_EVENTS_MEDIA_TYPE, CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_EVENTS_MEDIA_TYPE,
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS, CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS,
this._defaults.media_viewer.controls.timeline.show_recordings, this._defaults.media_viewer.controls.timeline.show_recordings,
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_PAN_MODE,
)} )}
`, `,
)} )}
+7 -6
View File
@@ -173,6 +173,13 @@
"above": "Above", "above": "Above",
"below": "Below", "below": "Below",
"none": "None" "none": "None"
},
"pan_mode": "Default pan mode",
"pan_modes": {
"pan": "Pan",
"seek": "Pan seeks across all media",
"seek-in-camera": "Pan seeks within selected camera only",
"seek-in-media": "Pan seeks within selected media item only"
} }
}, },
"title": { "title": {
@@ -589,12 +596,6 @@
"timeline": "See media in timeline" "timeline": "See media in timeline"
}, },
"timeline": { "timeline": {
"pan_behavior": {
"pan": "Pan",
"seek": "Pan seeks across all media",
"seek-in-camera": "Pan seeks within selected camera only",
"seek-in-media": "Pan seeks within selected media item only"
},
"select_date": "Choose date" "select_date": "Choose date"
} }
} }
+8 -7
View File
@@ -28,8 +28,8 @@
"editor_label": "Opzioni di dipendenza" "editor_label": "Opzioni di dipendenza"
}, },
"dimensions": { "dimensions": {
"editor_label": "",
"aspect_ratio": "", "aspect_ratio": "",
"editor_label": "",
"layout": { "layout": {
"fit": "Adatta al layout", "fit": "Adatta al layout",
"fits": { "fits": {
@@ -172,6 +172,13 @@
"above": "sopra", "above": "sopra",
"below": "sotto", "below": "sotto",
"none": "sessuna" "none": "sessuna"
},
"pan_mode": "",
"pan_modes": {
"pan": "",
"seek": "",
"seek-in-camera": "",
"seek-in-media": ""
} }
}, },
"title": { "title": {
@@ -577,12 +584,6 @@
"timeline": "Vedi evento nella timeline" "timeline": "Vedi evento nella timeline"
}, },
"timeline": { "timeline": {
"pan_behavior": {
"pan": "",
"seek": "",
"seek-in-camera": "",
"seek-in-media": ""
},
"select_date": "Scegli la data" "select_date": "Scegli la data"
} }
} }
+8 -7
View File
@@ -28,8 +28,8 @@
"editor_label": "Opções de dependência" "editor_label": "Opções de dependência"
}, },
"dimensions": { "dimensions": {
"editor_label": "",
"aspect_ratio": "", "aspect_ratio": "",
"editor_label": "",
"layout": { "layout": {
"fit": "Ajuste de layout", "fit": "Ajuste de layout",
"fits": { "fits": {
@@ -172,6 +172,13 @@
"above": "Acima", "above": "Acima",
"below": "Abaixo", "below": "Abaixo",
"none": "Nenhum" "none": "Nenhum"
},
"pan_mode": "",
"pan_modes": {
"pan": "",
"seek": "",
"seek-in-camera": "",
"seek-in-media": ""
} }
}, },
"title": { "title": {
@@ -587,12 +594,6 @@
"timeline": "Ver evento na linha do tempo" "timeline": "Ver evento na linha do tempo"
}, },
"timeline": { "timeline": {
"pan_behavior": {
"pan": "",
"seek": "",
"seek-in-camera": "",
"seek-in-media": ""
},
"select_date": "Escolha a data" "select_date": "Escolha a data"
} }
} }
+9 -8
View File
@@ -28,8 +28,8 @@
"editor_label": "Opções de dependência" "editor_label": "Opções de dependência"
}, },
"dimensions": { "dimensions": {
"editor_label": "",
"aspect_ratio": "", "aspect_ratio": "",
"editor_label": "",
"layout": { "layout": {
"fit": "Fit", "fit": "Fit",
"fits": { "fits": {
@@ -102,9 +102,9 @@
"editor_label": "Opções de activação", "editor_label": "Opções de activação",
"entities": "Activar a partir de outras entidades", "entities": "Activar a partir de outras entidades",
"events": { "events": {
"clips": "",
"editor_label": "", "editor_label": "",
"events": "", "events": "",
"clips": "",
"snapshots": "" "snapshots": ""
}, },
"motion": "Activar detectando automaticamente o sensor de movimento", "motion": "Activar detectando automaticamente o sensor de movimento",
@@ -172,6 +172,13 @@
"above": "Por cima", "above": "Por cima",
"below": "Abaixo", "below": "Abaixo",
"none": "Nenhum" "none": "Nenhum"
},
"pan_mode": "",
"pan_modes": {
"pan": "",
"seek": "",
"seek-in-camera": "",
"seek-in-media": ""
} }
}, },
"title": { "title": {
@@ -569,12 +576,6 @@
"timeline": "Ver evento na linha do tempo" "timeline": "Ver evento na linha do tempo"
}, },
"timeline": { "timeline": {
"pan_behavior": {
"pan": "",
"seek": "",
"seek-in-camera": "",
"seek-in-media": ""
},
"select_date": "Selecionar a data" "select_date": "Selecionar a data"
} }
} }
+3
View File
@@ -91,6 +91,7 @@ describe('config defaults', () => {
clustering_threshold: 3, clustering_threshold: 3,
events_media_type: 'all', events_media_type: 'all',
mode: 'none', mode: 'none',
pan_mode: 'pan',
show_recordings: true, show_recordings: true,
style: 'ribbon', style: 'ribbon',
window_seconds: 3600, window_seconds: 3600,
@@ -147,6 +148,7 @@ describe('config defaults', () => {
clustering_threshold: 3, clustering_threshold: 3,
events_media_type: 'all', events_media_type: 'all',
mode: 'none', mode: 'none',
pan_mode: 'pan',
show_recordings: true, show_recordings: true,
style: 'ribbon', style: 'ribbon',
window_seconds: 3600, window_seconds: 3600,
@@ -271,6 +273,7 @@ describe('config defaults', () => {
}, },
}, },
events_media_type: 'all', events_media_type: 'all',
pan_mode: 'pan',
show_recordings: true, show_recordings: true,
style: 'stack', style: 'stack',
window_seconds: 3600, window_seconds: 3600,
+2 -2
View File
@@ -80,10 +80,10 @@ export default defineConfig({
// Expected thresholds for anything that does not have 100% coverage // Expected thresholds for anything that does not have 100% coverage
// yet. // yet.
statements: 10.67, statements: 10.69,
branches: 4.9, branches: 4.9,
functions: 7.81, functions: 7.81,
lines: 10.92, lines: 10.94,
...calculateFullCoverageThresholds(), ...calculateFullCoverageThresholds(),
}, },