Allow live thumbnails to load recordings .
This commit is contained in:
@@ -564,7 +564,8 @@ live:
|
|||||||
| `show_download_control` | `true` | :white_check_mark: | Whether to show the download control on each thumbnail.|
|
| `show_download_control` | `true` | :white_check_mark: | Whether to show the download control on each thumbnail.|
|
||||||
| `show_favorite_control` | `true` | :white_check_mark: | Whether to show the favorite ('star') control on each thumbnail.|
|
| `show_favorite_control` | `true` | :white_check_mark: | Whether to show the favorite ('star') control on each thumbnail.|
|
||||||
| `show_timeline_control` | `true` | :white_check_mark: | Whether to show the timeline ('target') control on each thumbnail.|
|
| `show_timeline_control` | `true` | :white_check_mark: | Whether to show the timeline ('target') control on each thumbnail.|
|
||||||
| `media` | `all` | :white_check_mark: | Whether to show `clips`, `snapshots` or `all` in the thumbnail carousel in the `live` view.|
|
| `media_type` | `events` | :white_check_mark: | Whether to load `events` or `recordings` media.|
|
||||||
|
| `events_media_type` | `all` | :white_check_mark: | Whether to show `clips`, `snapshots` or `all` in the thumbnail carousel in the `live` view. This setting is only relevant when the `media_type` parameter (above) is set to `events`.|
|
||||||
|
|
||||||
#### Live Controls: Next / Previous
|
#### Live Controls: Next / Previous
|
||||||
|
|
||||||
@@ -597,7 +598,7 @@ live:
|
|||||||
| `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. |
|
||||||
| `media` | `all` | :white_check_mark: | Whether to show only events with `clips`, events with `snapshots` or `all` events. When `all` is used, `clips` are favored for events that have both a clip and a snapshot.|
|
| `events_media_type` | `all` | :white_check_mark: | Whether to show only events with `clips`, events with `snapshots` or `all` events. When `all` is used, `clips` are favored for events that have both a clip and a snapshot.|
|
||||||
| `show_recordings` | `true` | :white_check_mark: | Whether to show recordings on the timeline (specifically: which hours have any recorded content).|
|
| `show_recordings` | `true` | :white_check_mark: | Whether to show recordings on the timeline (specifically: which hours have any recorded content).|
|
||||||
|
|
||||||
**Caution**: 🚩 For optimal UX, keep the settings for the mini-timeline in the `live` and `media_viewer` identical. Dragging the timeline may cause the card to change between the `live` view and `media_viewer` based views as the user pans between the past and present -- if the settings are different the timeline must "reset".
|
**Caution**: 🚩 For optimal UX, keep the settings for the mini-timeline in the `live` and `media_viewer` identical. Dragging the timeline may cause the card to change between the `live` view and `media_viewer` based views as the user pans between the past and present -- if the settings are different the timeline must "reset".
|
||||||
@@ -781,7 +782,7 @@ media_viewer:
|
|||||||
| `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. |
|
||||||
| `media` | `all` | :heavy_multiplication_x: | Whether to show only events with `clips`, events with `snapshots` or `all` events. When `all` is used, `clips` are favored for events that have both a clip and a snapshot.|
|
| `events_media_type` | `all` | :heavy_multiplication_x: | Whether to show only events with `clips`, events with `snapshots` or `all` events. When `all` is used, `clips` are favored for events that have both a clip and a snapshot. |
|
||||||
| `show_recordings` | `true` | :heavy_multiplication_x: | Whether to show recordings on the timeline (specifically: which hours have any recorded content).|
|
| `show_recordings` | `true` | :heavy_multiplication_x: | Whether to show recordings on the timeline (specifically: which hours have any recorded content).|
|
||||||
|
|
||||||
**Caution**: 🚩 For optimal UX, keep the settings for the mini-timeline in the `live` and `media_viewer` identical. Dragging the timeline may cause the card to change between the `live` view and `media_viewer` based views as the user pans between the past and present -- if the settings are different the timeline must "reset".
|
**Caution**: 🚩 For optimal UX, keep the settings for the mini-timeline in the `live` and `media_viewer` identical. Dragging the timeline may cause the card to change between the `live` view and `media_viewer` based views as the user pans between the past and present -- if the settings are different the timeline must "reset".
|
||||||
@@ -911,7 +912,7 @@ See the [fully expanded timeline configuration example](#config-expanded-timelin
|
|||||||
| `style` | `stack` | :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` | `stack` | :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. |
|
||||||
| `media` | `all` | :heavy_multiplication_x: | Whether to show only events with `clips`, events with `snapshots` or `all` events. When `all` is used, `clips` are favored for events that have both a clip and a snapshot.|
|
| `events_media_type` | `all` | :heavy_multiplication_x: | Whether to show only events with `clips`, events with `snapshots` or `all` events. When `all` is used, `clips` are favored for events that have both a clip and a snapshot.|
|
||||||
| `show_recordings` | `true` | :heavy_multiplication_x: | Whether to show recordings on the timeline (specifically: which hours have any recorded content).|
|
| `show_recordings` | `true` | :heavy_multiplication_x: | Whether to show recordings on the timeline (specifically: which hours have any recorded content).|
|
||||||
| `controls` | | :heavy_multiplication_x: | Configuration for the timeline controls. See below.|
|
| `controls` | | :heavy_multiplication_x: | Configuration for the timeline controls. See below.|
|
||||||
|
|
||||||
@@ -2092,7 +2093,8 @@ live:
|
|||||||
style: chevrons
|
style: chevrons
|
||||||
size: 48
|
size: 48
|
||||||
thumbnails:
|
thumbnails:
|
||||||
media: clips
|
media_type: events
|
||||||
|
events_media_type: all
|
||||||
size: 100
|
size: 100
|
||||||
show_details: false
|
show_details: false
|
||||||
show_download_control: true
|
show_download_control: true
|
||||||
@@ -2103,7 +2105,7 @@ live:
|
|||||||
style: ribbon
|
style: ribbon
|
||||||
mode: none
|
mode: none
|
||||||
clustering_threshold: 3
|
clustering_threshold: 3
|
||||||
media: all
|
events_media_type: all
|
||||||
show_recordings: true
|
show_recordings: true
|
||||||
window_seconds: 3600
|
window_seconds: 3600
|
||||||
title:
|
title:
|
||||||
@@ -2201,7 +2203,7 @@ media_viewer:
|
|||||||
style: ribbon
|
style: ribbon
|
||||||
mode: none
|
mode: none
|
||||||
clustering_threshold: 3
|
clustering_threshold: 3
|
||||||
media: all
|
events_media_type: all
|
||||||
show_recordings: true
|
show_recordings: true
|
||||||
window_seconds: 3600
|
window_seconds: 3600
|
||||||
title:
|
title:
|
||||||
@@ -2663,7 +2665,7 @@ Reference: [Timeline Options](#timeline-options).
|
|||||||
timeline:
|
timeline:
|
||||||
style: stack
|
style: stack
|
||||||
clustering_threshold: 3
|
clustering_threshold: 3
|
||||||
media: all
|
events_media_type: all
|
||||||
show_recordings: true
|
show_recordings: true
|
||||||
window_seconds: 3600
|
window_seconds: 3600
|
||||||
controls:
|
controls:
|
||||||
@@ -2779,7 +2781,8 @@ overrides:
|
|||||||
style: chevrons
|
style: chevrons
|
||||||
size: 48
|
size: 48
|
||||||
thumbnails:
|
thumbnails:
|
||||||
media: clips
|
media_type: events
|
||||||
|
events_media_type: all
|
||||||
size: 100
|
size: 100
|
||||||
show_details: false
|
show_details: false
|
||||||
show_download_control: true
|
show_download_control: true
|
||||||
|
|||||||
@@ -47,18 +47,18 @@ export class TimelineDataSource {
|
|||||||
protected _eventRanges = new ExpiringMemoryRangeSet();
|
protected _eventRanges = new ExpiringMemoryRangeSet();
|
||||||
|
|
||||||
protected _cameraIDs: Set<string>;
|
protected _cameraIDs: Set<string>;
|
||||||
protected _mediaType: ClipsOrSnapshotsOrAll;
|
protected _eventsMediaType: ClipsOrSnapshotsOrAll;
|
||||||
protected _showRecordings: boolean;
|
protected _showRecordings: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
cameraManager: CameraManager,
|
cameraManager: CameraManager,
|
||||||
cameraIDs: Set<string>,
|
cameraIDs: Set<string>,
|
||||||
media: ClipsOrSnapshotsOrAll,
|
eventsMediaType: ClipsOrSnapshotsOrAll,
|
||||||
showRecordings: boolean,
|
showRecordings: boolean,
|
||||||
) {
|
) {
|
||||||
this._cameraManager = cameraManager;
|
this._cameraManager = cameraManager;
|
||||||
this._cameraIDs = cameraIDs;
|
this._cameraIDs = cameraIDs;
|
||||||
this._mediaType = media;
|
this._eventsMediaType = eventsMediaType;
|
||||||
this._showRecordings = showRecordings;
|
this._showRecordings = showRecordings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,8 +101,8 @@ export class TimelineDataSource {
|
|||||||
return this._cameraManager.generateDefaultEventQueries(this._cameraIDs, {
|
return this._cameraManager.generateDefaultEventQueries(this._cameraIDs, {
|
||||||
start: window.start,
|
start: window.start,
|
||||||
end: window.end,
|
end: window.end,
|
||||||
...(this._mediaType === 'clips' && { hasClip: true }),
|
...(this._eventsMediaType === 'clips' && { hasClip: true }),
|
||||||
...(this._mediaType === 'snapshots' && { hasSnapshot: true }),
|
...(this._eventsMediaType === 'snapshots' && { hasSnapshot: true }),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export class FrigateCardGallery extends LitElement {
|
|||||||
this.view,
|
this.view,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const mediaType = this.view.is('snapshots')
|
const eventsMediaType = this.view.is('snapshots')
|
||||||
? 'snapshots'
|
? 'snapshots'
|
||||||
: this.view.is('clips')
|
: this.view.is('clips')
|
||||||
? 'clips'
|
? 'clips'
|
||||||
@@ -98,7 +98,7 @@ export class FrigateCardGallery extends LitElement {
|
|||||||
this.cardWideConfig,
|
this.cardWideConfig,
|
||||||
this.view,
|
this.view,
|
||||||
{
|
{
|
||||||
...(mediaType && { mediaType: mediaType }),
|
...(eventsMediaType && { eventsMediaType: eventsMediaType }),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,16 @@ import {
|
|||||||
ThumbnailsControlConfig,
|
ThumbnailsControlConfig,
|
||||||
} from '../config/types.js';
|
} from '../config/types.js';
|
||||||
import basicBlockStyle from '../scss/basic-block.scss';
|
import basicBlockStyle from '../scss/basic-block.scss';
|
||||||
import { ClipsOrSnapshotsOrAll, ExtendedHomeAssistant } from '../types.js';
|
import {
|
||||||
|
ClipsOrSnapshotsOrAll,
|
||||||
|
EventsOrRecordings,
|
||||||
|
ExtendedHomeAssistant,
|
||||||
|
} from '../types.js';
|
||||||
import { contentsChanged, dispatchFrigateCardEvent } from '../utils/basic.js';
|
import { contentsChanged, dispatchFrigateCardEvent } from '../utils/basic.js';
|
||||||
import { changeViewToRecentEventsForCameraAndDependents } from '../utils/media-to-view';
|
import {
|
||||||
|
changeViewToRecentEventsForCameraAndDependents,
|
||||||
|
changeViewToRecentRecordingForCameraAndDependents,
|
||||||
|
} from '../utils/media-to-view';
|
||||||
import { View } from '../view/view.js';
|
import { View } from '../view/view.js';
|
||||||
import './surround-basic.js';
|
import './surround-basic.js';
|
||||||
import { ThumbnailCarouselTap } from './thumbnail-carousel.js';
|
import { ThumbnailCarouselTap } from './thumbnail-carousel.js';
|
||||||
@@ -48,7 +55,10 @@ export class FrigateCardSurround extends LitElement {
|
|||||||
|
|
||||||
// If fetchMedia is not specified, no fetching is done.
|
// If fetchMedia is not specified, no fetching is done.
|
||||||
@property({ attribute: false, hasChanged: contentsChanged })
|
@property({ attribute: false, hasChanged: contentsChanged })
|
||||||
public fetchMedia?: ClipsOrSnapshotsOrAll;
|
public fetchMediaType?: EventsOrRecordings;
|
||||||
|
|
||||||
|
@property({ attribute: false, hasChanged: contentsChanged })
|
||||||
|
public fetchEventsMediaType?: ClipsOrSnapshotsOrAll;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public cameraManager?: CameraManager;
|
public cameraManager?: CameraManager;
|
||||||
@@ -68,7 +78,8 @@ export class FrigateCardSurround extends LitElement {
|
|||||||
if (
|
if (
|
||||||
!this.cameraManager ||
|
!this.cameraManager ||
|
||||||
!this.cardWideConfig ||
|
!this.cardWideConfig ||
|
||||||
!this.fetchMedia ||
|
!this.fetchMediaType ||
|
||||||
|
!this.fetchEventsMediaType ||
|
||||||
!this.hass ||
|
!this.hass ||
|
||||||
!this.view ||
|
!this.view ||
|
||||||
this.view.query ||
|
this.view.query ||
|
||||||
@@ -78,6 +89,8 @@ export class FrigateCardSurround extends LitElement {
|
|||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.fetchMediaType === 'events') {
|
||||||
await changeViewToRecentEventsForCameraAndDependents(
|
await changeViewToRecentEventsForCameraAndDependents(
|
||||||
this,
|
this,
|
||||||
this.cameraManager,
|
this.cameraManager,
|
||||||
@@ -86,10 +99,23 @@ export class FrigateCardSurround extends LitElement {
|
|||||||
{
|
{
|
||||||
allCameras: this.view.isGrid(),
|
allCameras: this.view.isGrid(),
|
||||||
targetView: this.view.view,
|
targetView: this.view.view,
|
||||||
mediaType: this.fetchMedia,
|
eventsMediaType: this.fetchEventsMediaType,
|
||||||
select: 'latest',
|
select: 'latest',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
} else if (this.fetchMediaType === 'recordings') {
|
||||||
|
await changeViewToRecentRecordingForCameraAndDependents(
|
||||||
|
this,
|
||||||
|
this.cameraManager,
|
||||||
|
this.cardWideConfig,
|
||||||
|
this.view,
|
||||||
|
{
|
||||||
|
allCameras: this.view.isGrid(),
|
||||||
|
targetView: this.view.view,
|
||||||
|
select: 'latest',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1161,7 +1161,7 @@ export class FrigateCardTimelineCore extends LitElement {
|
|||||||
this._timelineSource = new TimelineDataSource(
|
this._timelineSource = new TimelineDataSource(
|
||||||
this.cameraManager,
|
this.cameraManager,
|
||||||
cameraIDs,
|
cameraIDs,
|
||||||
this.timelineConfig.media,
|
this.timelineConfig.events_media_type,
|
||||||
this.timelineConfig.show_recordings,
|
this.timelineConfig.show_recordings,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ export class FrigateCardViewer extends LitElement {
|
|||||||
{
|
{
|
||||||
allCameras: this.view.isGrid(),
|
allCameras: this.view.isGrid(),
|
||||||
targetView: 'media',
|
targetView: 'media',
|
||||||
mediaType: mediaType,
|
eventsMediaType: mediaType,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,8 +163,11 @@ export class FrigateCardViews extends LitElement {
|
|||||||
class="${classMap(overallClasses)}"
|
class="${classMap(overallClasses)}"
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.view=${this.view}
|
.view=${this.view}
|
||||||
.fetchMedia=${this.view?.is('live')
|
.fetchMediaType=${this.view?.is('live')
|
||||||
? this.overriddenConfig.live.controls.thumbnails.media
|
? this.overriddenConfig.live.controls.thumbnails.media_type
|
||||||
|
: undefined}
|
||||||
|
.fetchEventsMediaType=${this.view?.is('live')
|
||||||
|
? this.overriddenConfig.live.controls.thumbnails.events_media_type
|
||||||
: undefined}
|
: undefined}
|
||||||
.thumbnailConfig=${!this.hide ? thumbnailConfig : undefined}
|
.thumbnailConfig=${!this.hide ? thumbnailConfig : undefined}
|
||||||
.timelineConfig=${!this.hide ? miniTimelineConfig : undefined}
|
.timelineConfig=${!this.hide ? miniTimelineConfig : undefined}
|
||||||
|
|||||||
@@ -10,9 +10,13 @@ import {
|
|||||||
CONF_CAMERAS_GLOBAL_JSMPEG,
|
CONF_CAMERAS_GLOBAL_JSMPEG,
|
||||||
CONF_CAMERAS_GLOBAL_WEBRTC_CARD,
|
CONF_CAMERAS_GLOBAL_WEBRTC_CARD,
|
||||||
CONF_ELEMENTS,
|
CONF_ELEMENTS,
|
||||||
|
CONF_LIVE_CONTROLS_THUMBNAILS_EVENTS_MEDIA_TYPE,
|
||||||
|
CONF_LIVE_CONTROLS_TIMELINE_EVENTS_MEDIA_TYPE,
|
||||||
CONF_MEDIA_GALLERY,
|
CONF_MEDIA_GALLERY,
|
||||||
|
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_EVENTS_MEDIA_TYPE,
|
||||||
CONF_MENU_BUTTONS_CAMERA_UI,
|
CONF_MENU_BUTTONS_CAMERA_UI,
|
||||||
CONF_OVERRIDES,
|
CONF_OVERRIDES,
|
||||||
|
CONF_TIMELINE_EVENTS_MEDIA_TYPE,
|
||||||
CONF_VIEW_INTERACTION_SECONDS,
|
CONF_VIEW_INTERACTION_SECONDS,
|
||||||
CONF_VIEW_SCAN_ACTIONS_UNTRIGGER,
|
CONF_VIEW_SCAN_ACTIONS_UNTRIGGER,
|
||||||
} from './const';
|
} from './const';
|
||||||
@@ -516,4 +520,20 @@ const UPGRADES = [
|
|||||||
upgradeWithOverrides('media_viewer.auto_unmute', (data) =>
|
upgradeWithOverrides('media_viewer.auto_unmute', (data) =>
|
||||||
data === 'all' ? ['selected', 'visible'] : data === 'never' ? null : arrayify(data),
|
data === 'all' ? ['selected', 'visible'] : data === 'never' ? null : arrayify(data),
|
||||||
),
|
),
|
||||||
|
upgradeMoveToWithOverrides(
|
||||||
|
'live.controls.thumbnails.media',
|
||||||
|
CONF_LIVE_CONTROLS_THUMBNAILS_EVENTS_MEDIA_TYPE,
|
||||||
|
),
|
||||||
|
upgradeMoveToWithOverrides(
|
||||||
|
'timeline.media',
|
||||||
|
CONF_TIMELINE_EVENTS_MEDIA_TYPE,
|
||||||
|
),
|
||||||
|
upgradeMoveToWithOverrides(
|
||||||
|
'live.controls.timeline.media',
|
||||||
|
CONF_LIVE_CONTROLS_TIMELINE_EVENTS_MEDIA_TYPE,
|
||||||
|
),
|
||||||
|
upgradeMoveToWithOverrides(
|
||||||
|
'media_viewer.controls.timeline.media',
|
||||||
|
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_EVENTS_MEDIA_TYPE,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|||||||
+17
-8
@@ -626,26 +626,31 @@ const thumbnailsControlSchema = thumbnailsControlBaseSchema.extend({
|
|||||||
});
|
});
|
||||||
export type ThumbnailsControlConfig = z.infer<typeof thumbnailsControlSchema>;
|
export type ThumbnailsControlConfig = z.infer<typeof thumbnailsControlSchema>;
|
||||||
|
|
||||||
|
// *************************************************************************
|
||||||
|
// Event Media Type Configuration
|
||||||
|
// *************************************************************************
|
||||||
|
const eventsMediaTypeSchema = z.enum(['all', 'clips', 'snapshots']);
|
||||||
|
|
||||||
// *************************************************************************
|
// *************************************************************************
|
||||||
// Timeline Configuration
|
// Timeline Configuration
|
||||||
// *************************************************************************
|
// *************************************************************************
|
||||||
|
|
||||||
const timelineCoreConfigDefault = {
|
const timelineCoreConfigDefault = {
|
||||||
clustering_threshold: 3,
|
clustering_threshold: 3,
|
||||||
media: 'all' as const,
|
events_media_type: 'all' as const,
|
||||||
window_seconds: 60 * 60,
|
window_seconds: 60 * 60,
|
||||||
show_recordings: true,
|
show_recordings: true,
|
||||||
style: 'stack' as const,
|
style: 'stack' as const,
|
||||||
};
|
};
|
||||||
|
|
||||||
const timelineMediaSchema = z.enum(['all', 'clips', 'snapshots']);
|
|
||||||
|
|
||||||
const timelineCoreConfigSchema = z.object({
|
const timelineCoreConfigSchema = z.object({
|
||||||
clustering_threshold: z
|
clustering_threshold: z
|
||||||
.number()
|
.number()
|
||||||
.optional()
|
.optional()
|
||||||
.default(timelineCoreConfigDefault.clustering_threshold),
|
.default(timelineCoreConfigDefault.clustering_threshold),
|
||||||
media: timelineMediaSchema.optional().default(timelineCoreConfigDefault.media),
|
events_media_type: eventsMediaTypeSchema
|
||||||
|
.optional()
|
||||||
|
.default(timelineCoreConfigDefault.events_media_type),
|
||||||
window_seconds: z
|
window_seconds: z
|
||||||
.number()
|
.number()
|
||||||
.min(1 * 60)
|
.min(1 * 60)
|
||||||
@@ -867,7 +872,8 @@ export type FrigateCardPTZConfig = z.infer<typeof frigateCardPTZSchema>;
|
|||||||
|
|
||||||
const liveThumbnailControlsDefaults = {
|
const liveThumbnailControlsDefaults = {
|
||||||
...thumbnailControlsDefaults,
|
...thumbnailControlsDefaults,
|
||||||
media: 'all' as const,
|
media_type: 'events' as const,
|
||||||
|
events_media_type: 'all' as const,
|
||||||
};
|
};
|
||||||
|
|
||||||
const liveConfigDefault = {
|
const liveConfigDefault = {
|
||||||
@@ -899,9 +905,12 @@ const liveConfigDefault = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const livethumbnailsControlSchema = thumbnailsControlSchema.extend({
|
const livethumbnailsControlSchema = thumbnailsControlSchema.extend({
|
||||||
media: z
|
media_type: z
|
||||||
.enum(['all', 'clips', 'snapshots'])
|
.enum(['events', 'recordings'])
|
||||||
.default(liveConfigDefault.controls.thumbnails.media),
|
.default(liveConfigDefault.controls.thumbnails.media_type),
|
||||||
|
events_media_type: eventsMediaTypeSchema.default(
|
||||||
|
liveConfigDefault.controls.thumbnails.events_media_type,
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
const liveOverridableConfigSchema = z
|
const liveOverridableConfigSchema = z
|
||||||
|
|||||||
+9
-7
@@ -146,8 +146,8 @@ export const CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SIZE =
|
|||||||
`${CONF_MEDIA_VIEWER}.controls.thumbnails.size` as const;
|
`${CONF_MEDIA_VIEWER}.controls.thumbnails.size` as const;
|
||||||
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD =
|
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD =
|
||||||
`${CONF_MEDIA_VIEWER}.controls.timeline.clustering_threshold` as const;
|
`${CONF_MEDIA_VIEWER}.controls.timeline.clustering_threshold` as const;
|
||||||
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MEDIA =
|
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_EVENTS_MEDIA_TYPE =
|
||||||
`${CONF_MEDIA_VIEWER}.controls.timeline.media` 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_SHOW_RECORDINGS =
|
export const CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS =
|
||||||
@@ -189,8 +189,10 @@ export const CONF_LIVE_CONTROLS_PTZ_ORIENTATION =
|
|||||||
`${CONF_LIVE}.controls.ptz.orientation` as const;
|
`${CONF_LIVE}.controls.ptz.orientation` as const;
|
||||||
export const CONF_LIVE_CONTROLS_PTZ_POSITION =
|
export const CONF_LIVE_CONTROLS_PTZ_POSITION =
|
||||||
`${CONF_LIVE}.controls.ptz.position` as const;
|
`${CONF_LIVE}.controls.ptz.position` as const;
|
||||||
export const CONF_LIVE_CONTROLS_THUMBNAILS_MEDIA =
|
export const CONF_LIVE_CONTROLS_THUMBNAILS_MEDIA_TYPE =
|
||||||
`${CONF_LIVE}.controls.thumbnails.media` as const;
|
`${CONF_LIVE}.controls.thumbnails.media_type` as const;
|
||||||
|
export const CONF_LIVE_CONTROLS_THUMBNAILS_EVENTS_MEDIA_TYPE =
|
||||||
|
`${CONF_LIVE}.controls.thumbnails.events_media_type` as const;
|
||||||
export const CONF_LIVE_CONTROLS_THUMBNAILS_MODE =
|
export const CONF_LIVE_CONTROLS_THUMBNAILS_MODE =
|
||||||
`${CONF_LIVE}.controls.thumbnails.mode` as const;
|
`${CONF_LIVE}.controls.thumbnails.mode` as const;
|
||||||
export const CONF_LIVE_CONTROLS_THUMBNAILS_SIZE =
|
export const CONF_LIVE_CONTROLS_THUMBNAILS_SIZE =
|
||||||
@@ -205,8 +207,8 @@ export const CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL =
|
|||||||
`${CONF_LIVE}.controls.thumbnails.show_timeline_control` as const;
|
`${CONF_LIVE}.controls.thumbnails.show_timeline_control` as const;
|
||||||
export const CONF_LIVE_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD =
|
export const CONF_LIVE_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD =
|
||||||
`${CONF_LIVE}.controls.timeline.clustering_threshold` as const;
|
`${CONF_LIVE}.controls.timeline.clustering_threshold` as const;
|
||||||
export const CONF_LIVE_CONTROLS_TIMELINE_MEDIA =
|
export const CONF_LIVE_CONTROLS_TIMELINE_EVENTS_MEDIA_TYPE =
|
||||||
`${CONF_LIVE}.controls.timeline.media` 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_SHOW_RECORDINGS =
|
export const CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS =
|
||||||
@@ -256,7 +258,7 @@ const CONF_TIMELINE = 'timeline' as const;
|
|||||||
export const CONF_TIMELINE_WINDOW_SECONDS = `${CONF_TIMELINE}.window_seconds` as const;
|
export const CONF_TIMELINE_WINDOW_SECONDS = `${CONF_TIMELINE}.window_seconds` as const;
|
||||||
export const CONF_TIMELINE_CLUSTERING_THRESHOLD =
|
export const CONF_TIMELINE_CLUSTERING_THRESHOLD =
|
||||||
`${CONF_TIMELINE}.clustering_threshold` as const;
|
`${CONF_TIMELINE}.clustering_threshold` as const;
|
||||||
export const CONF_TIMELINE_MEDIA = `${CONF_TIMELINE}.media` as const;
|
export const CONF_TIMELINE_EVENTS_MEDIA_TYPE = `${CONF_TIMELINE}.events_media_type` as const;
|
||||||
export const CONF_TIMELINE_SHOW_RECORDINGS = `${CONF_TIMELINE}.show_recordings` as const;
|
export const CONF_TIMELINE_SHOW_RECORDINGS = `${CONF_TIMELINE}.show_recordings` as const;
|
||||||
export const CONF_TIMELINE_STYLE = `${CONF_TIMELINE}.style` as const;
|
export const CONF_TIMELINE_STYLE = `${CONF_TIMELINE}.style` as const;
|
||||||
export const CONF_TIMELINE_CONTROLS_THUMBNAILS_MODE =
|
export const CONF_TIMELINE_CONTROLS_THUMBNAILS_MODE =
|
||||||
|
|||||||
+51
-27
@@ -86,7 +86,8 @@ import {
|
|||||||
CONF_LIVE_CONTROLS_PTZ_MODE,
|
CONF_LIVE_CONTROLS_PTZ_MODE,
|
||||||
CONF_LIVE_CONTROLS_PTZ_ORIENTATION,
|
CONF_LIVE_CONTROLS_PTZ_ORIENTATION,
|
||||||
CONF_LIVE_CONTROLS_PTZ_POSITION,
|
CONF_LIVE_CONTROLS_PTZ_POSITION,
|
||||||
CONF_LIVE_CONTROLS_THUMBNAILS_MEDIA,
|
CONF_LIVE_CONTROLS_THUMBNAILS_EVENTS_MEDIA_TYPE,
|
||||||
|
CONF_LIVE_CONTROLS_THUMBNAILS_MEDIA_TYPE,
|
||||||
CONF_LIVE_CONTROLS_THUMBNAILS_MODE,
|
CONF_LIVE_CONTROLS_THUMBNAILS_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,
|
||||||
@@ -94,7 +95,7 @@ import {
|
|||||||
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
|
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
|
||||||
CONF_LIVE_CONTROLS_THUMBNAILS_SIZE,
|
CONF_LIVE_CONTROLS_THUMBNAILS_SIZE,
|
||||||
CONF_LIVE_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD,
|
CONF_LIVE_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD,
|
||||||
CONF_LIVE_CONTROLS_TIMELINE_MEDIA,
|
CONF_LIVE_CONTROLS_TIMELINE_EVENTS_MEDIA_TYPE,
|
||||||
CONF_LIVE_CONTROLS_TIMELINE_MODE,
|
CONF_LIVE_CONTROLS_TIMELINE_MODE,
|
||||||
CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
CONF_LIVE_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
||||||
CONF_LIVE_CONTROLS_TIMELINE_STYLE,
|
CONF_LIVE_CONTROLS_TIMELINE_STYLE,
|
||||||
@@ -138,7 +139,7 @@ import {
|
|||||||
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
|
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SIZE,
|
CONF_MEDIA_VIEWER_CONTROLS_THUMBNAILS_SIZE,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD,
|
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MEDIA,
|
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_SHOW_RECORDINGS,
|
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_SHOW_RECORDINGS,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_STYLE,
|
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_STYLE,
|
||||||
@@ -174,7 +175,7 @@ import {
|
|||||||
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
|
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_FAVORITE_CONTROL,
|
||||||
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
|
CONF_TIMELINE_CONTROLS_THUMBNAILS_SHOW_TIMELINE_CONTROL,
|
||||||
CONF_TIMELINE_CONTROLS_THUMBNAILS_SIZE,
|
CONF_TIMELINE_CONTROLS_THUMBNAILS_SIZE,
|
||||||
CONF_TIMELINE_MEDIA,
|
CONF_TIMELINE_EVENTS_MEDIA_TYPE,
|
||||||
CONF_TIMELINE_SHOW_RECORDINGS,
|
CONF_TIMELINE_SHOW_RECORDINGS,
|
||||||
CONF_TIMELINE_STYLE,
|
CONF_TIMELINE_STYLE,
|
||||||
CONF_TIMELINE_WINDOW_SECONDS,
|
CONF_TIMELINE_WINDOW_SECONDS,
|
||||||
@@ -452,15 +453,27 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
protected _thumbnailMedias: EditorSelectOption[] = [
|
protected _thumbnailMediaTypes: EditorSelectOption[] = [
|
||||||
|
{ value: '', label: '' },
|
||||||
|
{
|
||||||
|
value: 'events',
|
||||||
|
label: localize('config.common.controls.thumbnails.media_types.events'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'recordings',
|
||||||
|
label: localize('config.common.controls.thumbnails.media_types.recordings'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
protected _thumbnailEventsMediaTypes: EditorSelectOption[] = [
|
||||||
{ value: '', label: '' },
|
{ value: '', label: '' },
|
||||||
{
|
{
|
||||||
value: 'clips',
|
value: 'clips',
|
||||||
label: localize('config.common.controls.thumbnails.medias.clips'),
|
label: localize('config.common.controls.thumbnails.events_media_types.clips'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'snapshots',
|
value: 'snapshots',
|
||||||
label: localize('config.common.controls.thumbnails.medias.snapshots'),
|
label: localize('config.common.controls.thumbnails.events_media_types.snapshots'),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -498,11 +511,11 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
{ value: 'url', label: localize('config.image.modes.url') },
|
{ value: 'url', label: localize('config.image.modes.url') },
|
||||||
];
|
];
|
||||||
|
|
||||||
protected _timelineMediaTypes: EditorSelectOption[] = [
|
protected _timelineEventsMediaTypes: EditorSelectOption[] = [
|
||||||
{ value: '', label: '' },
|
{ value: '', label: '' },
|
||||||
{ value: 'all', label: localize('config.common.timeline.medias.all') },
|
{ value: 'all', label: localize('config.common.timeline.events_media_types.all') },
|
||||||
{ value: 'clips', label: localize('config.common.timeline.medias.clips') },
|
{ value: 'clips', label: localize('config.common.timeline.events_media_types.clips') },
|
||||||
{ value: 'snapshots', label: localize('config.common.timeline.medias.snapshots') },
|
{ value: 'snapshots', label: localize('config.common.timeline.events_media_types.snapshots') },
|
||||||
];
|
];
|
||||||
|
|
||||||
protected _timelineStyleTypes: EditorSelectOption[] = [
|
protected _timelineStyleTypes: EditorSelectOption[] = [
|
||||||
@@ -1149,7 +1162,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
* @param configPathStyle Timeline style config path.
|
* @param configPathStyle Timeline style config path.
|
||||||
* @param configPathWindowSeconds Timeline window config path.
|
* @param configPathWindowSeconds Timeline window config path.
|
||||||
* @param configPathClusteringThreshold Clustering threshold config path.
|
* @param configPathClusteringThreshold Clustering threshold config path.
|
||||||
* @param configPathTimelineMedia Timeline media config path.
|
* @param configPathTimelineEventsMediaType Timeline media config path.
|
||||||
* @param configPathShowRecordings Show recordings config path.
|
* @param configPathShowRecordings Show recordings config path.
|
||||||
* @param defaultShowRecordings Default value of show_recordings.
|
* @param defaultShowRecordings Default value of show_recordings.
|
||||||
* @returns A rendered template.
|
* @returns A rendered template.
|
||||||
@@ -1158,7 +1171,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
configPathStyle: string,
|
configPathStyle: string,
|
||||||
configPathWindowSeconds: string,
|
configPathWindowSeconds: string,
|
||||||
configPathClusteringThreshold: string,
|
configPathClusteringThreshold: string,
|
||||||
configPathTimelineMedia: string,
|
configPathTimelineEventsMediaType: string,
|
||||||
configPathShowRecordings: string,
|
configPathShowRecordings: string,
|
||||||
defaultShowRecordings: boolean,
|
defaultShowRecordings: boolean,
|
||||||
): TemplateResult {
|
): TemplateResult {
|
||||||
@@ -1175,8 +1188,8 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
${this._renderNumberInput(configPathClusteringThreshold, {
|
${this._renderNumberInput(configPathClusteringThreshold, {
|
||||||
label: localize(`config.common.${CONF_TIMELINE_CLUSTERING_THRESHOLD}`),
|
label: localize(`config.common.${CONF_TIMELINE_CLUSTERING_THRESHOLD}`),
|
||||||
})}
|
})}
|
||||||
${this._renderOptionSelector(configPathTimelineMedia, this._timelineMediaTypes, {
|
${this._renderOptionSelector(configPathTimelineEventsMediaType, this._timelineEventsMediaTypes, {
|
||||||
label: localize(`config.common.${CONF_TIMELINE_MEDIA}`),
|
label: localize(`config.common.${CONF_TIMELINE_EVENTS_MEDIA_TYPE}`),
|
||||||
})}
|
})}
|
||||||
${this._renderSwitch(configPathShowRecordings, defaultShowRecordings, {
|
${this._renderSwitch(configPathShowRecordings, defaultShowRecordings, {
|
||||||
label: localize(`config.common.${CONF_TIMELINE_SHOW_RECORDINGS}`),
|
label: localize(`config.common.${CONF_TIMELINE_SHOW_RECORDINGS}`),
|
||||||
@@ -1188,7 +1201,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
* @param domain The submenu domain.
|
* @param domain The submenu domain.
|
||||||
* @param configPathWindowSeconds Timeline window config path.
|
* @param configPathWindowSeconds Timeline window config path.
|
||||||
* @param configPathClusteringThreshold Clustering threshold config path.
|
* @param configPathClusteringThreshold Clustering threshold config path.
|
||||||
* @param configPathTimelineMedia Timeline media config path.
|
* @param configPathTimelineEventsMediaType Timeline media config path.
|
||||||
* @param configPathShowRecordings Show recordings config path.
|
* @param configPathShowRecordings Show recordings config path.
|
||||||
* @returns A rendered template.
|
* @returns A rendered template.
|
||||||
*/
|
*/
|
||||||
@@ -1198,7 +1211,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
configPathStyle: string,
|
configPathStyle: string,
|
||||||
configPathWindowSeconds: string,
|
configPathWindowSeconds: string,
|
||||||
configPathClusteringThreshold: string,
|
configPathClusteringThreshold: string,
|
||||||
configPathTimelineMedia: string,
|
configPathTimelineEventsMediaType: string,
|
||||||
configPathShowRecordings: string,
|
configPathShowRecordings: string,
|
||||||
showRecordingsDefault: boolean,
|
showRecordingsDefault: boolean,
|
||||||
): TemplateResult | void {
|
): TemplateResult | void {
|
||||||
@@ -1214,7 +1227,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
configPathStyle,
|
configPathStyle,
|
||||||
configPathWindowSeconds,
|
configPathWindowSeconds,
|
||||||
configPathClusteringThreshold,
|
configPathClusteringThreshold,
|
||||||
configPathTimelineMedia,
|
configPathTimelineEventsMediaType,
|
||||||
configPathShowRecordings,
|
configPathShowRecordings,
|
||||||
showRecordingsDefault,
|
showRecordingsDefault,
|
||||||
)}`,
|
)}`,
|
||||||
@@ -1330,7 +1343,8 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
show_download_control: boolean;
|
show_download_control: boolean;
|
||||||
},
|
},
|
||||||
options?: {
|
options?: {
|
||||||
configPathMedia?: string;
|
configPathMediaType?: string;
|
||||||
|
configPathEventsMediaType?: string;
|
||||||
configPathMode?: string;
|
configPathMode?: string;
|
||||||
},
|
},
|
||||||
): TemplateResult | void {
|
): TemplateResult | void {
|
||||||
@@ -1349,12 +1363,21 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
},
|
},
|
||||||
)}`
|
)}`
|
||||||
: html``}
|
: html``}
|
||||||
${options?.configPathMedia
|
${options?.configPathMediaType
|
||||||
? html`${this._renderOptionSelector(
|
? html`${this._renderOptionSelector(
|
||||||
options.configPathMedia,
|
options.configPathMediaType,
|
||||||
this._thumbnailMedias,
|
this._thumbnailMediaTypes,
|
||||||
{
|
{
|
||||||
label: localize('config.common.controls.thumbnails.media'),
|
label: localize('config.common.controls.thumbnails.media_type'),
|
||||||
|
},
|
||||||
|
)}`
|
||||||
|
: html``}
|
||||||
|
${options?.configPathEventsMediaType
|
||||||
|
? html`${this._renderOptionSelector(
|
||||||
|
options.configPathEventsMediaType,
|
||||||
|
this._thumbnailEventsMediaTypes,
|
||||||
|
{
|
||||||
|
label: localize('config.common.controls.thumbnails.events_media_type'),
|
||||||
},
|
},
|
||||||
)}`
|
)}`
|
||||||
: html``}
|
: html``}
|
||||||
@@ -2101,7 +2124,8 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DOWNLOAD_CONTROL,
|
CONF_LIVE_CONTROLS_THUMBNAILS_SHOW_DOWNLOAD_CONTROL,
|
||||||
this._defaults.live.controls.thumbnails,
|
this._defaults.live.controls.thumbnails,
|
||||||
{
|
{
|
||||||
configPathMedia: CONF_LIVE_CONTROLS_THUMBNAILS_MEDIA,
|
configPathMediaType: CONF_LIVE_CONTROLS_THUMBNAILS_MEDIA_TYPE,
|
||||||
|
configPathEventsMediaType: CONF_LIVE_CONTROLS_THUMBNAILS_EVENTS_MEDIA_TYPE,
|
||||||
configPathMode: CONF_LIVE_CONTROLS_THUMBNAILS_MODE,
|
configPathMode: CONF_LIVE_CONTROLS_THUMBNAILS_MODE,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
@@ -2116,7 +2140,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
CONF_LIVE_CONTROLS_TIMELINE_STYLE,
|
CONF_LIVE_CONTROLS_TIMELINE_STYLE,
|
||||||
CONF_LIVE_CONTROLS_TIMELINE_WINDOW_SECONDS,
|
CONF_LIVE_CONTROLS_TIMELINE_WINDOW_SECONDS,
|
||||||
CONF_LIVE_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD,
|
CONF_LIVE_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD,
|
||||||
CONF_LIVE_CONTROLS_TIMELINE_MEDIA,
|
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,
|
||||||
)}
|
)}
|
||||||
@@ -2309,7 +2333,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
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,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD,
|
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_CLUSTERING_THRESHOLD,
|
||||||
CONF_MEDIA_VIEWER_CONTROLS_TIMELINE_MEDIA,
|
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,
|
||||||
)}
|
)}
|
||||||
@@ -2347,7 +2371,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
CONF_TIMELINE_STYLE,
|
CONF_TIMELINE_STYLE,
|
||||||
CONF_TIMELINE_WINDOW_SECONDS,
|
CONF_TIMELINE_WINDOW_SECONDS,
|
||||||
CONF_TIMELINE_CLUSTERING_THRESHOLD,
|
CONF_TIMELINE_CLUSTERING_THRESHOLD,
|
||||||
CONF_TIMELINE_MEDIA,
|
CONF_TIMELINE_EVENTS_MEDIA_TYPE,
|
||||||
CONF_TIMELINE_SHOW_RECORDINGS,
|
CONF_TIMELINE_SHOW_RECORDINGS,
|
||||||
this._defaults.timeline.show_recordings,
|
this._defaults.timeline.show_recordings,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -119,8 +119,13 @@
|
|||||||
},
|
},
|
||||||
"thumbnails": {
|
"thumbnails": {
|
||||||
"editor_label": "Thumbnails",
|
"editor_label": "Thumbnails",
|
||||||
"media": "Whether to show thumbnails of clips or snapshots",
|
"media_type": "Whether to show thumnails of events or recordings",
|
||||||
"medias": {
|
"media_types": {
|
||||||
|
"events": "Events",
|
||||||
|
"recordings": "Recordings"
|
||||||
|
},
|
||||||
|
"events_media_type": "Whether to show event thumbnails of clips or snapshots",
|
||||||
|
"events_media_types": {
|
||||||
"clips": "Clip thumbnails",
|
"clips": "Clip thumbnails",
|
||||||
"snapshots": "Snapshot thumbnails"
|
"snapshots": "Snapshot thumbnails"
|
||||||
},
|
},
|
||||||
@@ -191,9 +196,9 @@
|
|||||||
},
|
},
|
||||||
"timeline": {
|
"timeline": {
|
||||||
"clustering_threshold": "The count of events at which they are clustered (0=no clustering)",
|
"clustering_threshold": "The count of events at which they are clustered (0=no clustering)",
|
||||||
"media": "The media the timeline displays",
|
"events_media_type": "The events media the timeline displays",
|
||||||
"medias": {
|
"events_media_types": {
|
||||||
"all": "All media types",
|
"all": "All event media types",
|
||||||
"clips": "Clips",
|
"clips": "Clips",
|
||||||
"snapshots": "Snapshots"
|
"snapshots": "Snapshots"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { z } from 'zod';
|
|||||||
|
|
||||||
export type ClipsOrSnapshots = 'clips' | 'snapshots';
|
export type ClipsOrSnapshots = 'clips' | 'snapshots';
|
||||||
export type ClipsOrSnapshotsOrAll = 'clips' | 'snapshots' | 'all';
|
export type ClipsOrSnapshotsOrAll = 'clips' | 'snapshots' | 'all';
|
||||||
|
export type EventsOrRecordings = 'events' | 'recordings';
|
||||||
|
|
||||||
export class FrigateCardError extends Error {
|
export class FrigateCardError extends Error {
|
||||||
context?: unknown;
|
context?: unknown;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export const changeViewToRecentEventsForCameraAndDependents = async (
|
|||||||
view: View,
|
view: View,
|
||||||
options?: {
|
options?: {
|
||||||
allCameras?: boolean;
|
allCameras?: boolean;
|
||||||
mediaType?: ClipsOrSnapshotsOrAll;
|
eventsMediaType?: ClipsOrSnapshotsOrAll;
|
||||||
targetView?: FrigateCardView;
|
targetView?: FrigateCardView;
|
||||||
select?: ResultSelectType;
|
select?: ResultSelectType;
|
||||||
},
|
},
|
||||||
@@ -37,7 +37,7 @@ export const changeViewToRecentEventsForCameraAndDependents = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const queries = createQueriesForEventsView(cameraManager, cardWideConfig, cameraIDs, {
|
const queries = createQueriesForEventsView(cameraManager, cardWideConfig, cameraIDs, {
|
||||||
mediaType: options?.mediaType,
|
eventsMediaType: options?.eventsMediaType,
|
||||||
});
|
});
|
||||||
if (!queries) {
|
if (!queries) {
|
||||||
return;
|
return;
|
||||||
@@ -62,15 +62,15 @@ const createQueriesForEventsView = (
|
|||||||
cardWideConfig: CardWideConfig,
|
cardWideConfig: CardWideConfig,
|
||||||
cameraIDs: Set<string>,
|
cameraIDs: Set<string>,
|
||||||
options?: {
|
options?: {
|
||||||
mediaType?: ClipsOrSnapshotsOrAll;
|
eventsMediaType?: ClipsOrSnapshotsOrAll;
|
||||||
},
|
},
|
||||||
): EventMediaQueries | null => {
|
): EventMediaQueries | null => {
|
||||||
const limit =
|
const limit =
|
||||||
cardWideConfig.performance?.features.media_chunk_size ?? MEDIA_CHUNK_SIZE_DEFAULT;
|
cardWideConfig.performance?.features.media_chunk_size ?? MEDIA_CHUNK_SIZE_DEFAULT;
|
||||||
const eventQueries = cameraManager.generateDefaultEventQueries(cameraIDs, {
|
const eventQueries = cameraManager.generateDefaultEventQueries(cameraIDs, {
|
||||||
limit: limit,
|
limit: limit,
|
||||||
...(options?.mediaType === 'clips' && { hasClip: true }),
|
...(options?.eventsMediaType === 'clips' && { hasClip: true }),
|
||||||
...(options?.mediaType === 'snapshots' && { hasSnapshot: true }),
|
...(options?.eventsMediaType === 'snapshots' && { hasSnapshot: true }),
|
||||||
});
|
});
|
||||||
return eventQueries ? new EventMediaQueries(eventQueries) : null;
|
return eventQueries ? new EventMediaQueries(eventQueries) : null;
|
||||||
};
|
};
|
||||||
@@ -91,7 +91,7 @@ export const changeViewToRecentRecordingForCameraAndDependents = async (
|
|||||||
view: View,
|
view: View,
|
||||||
options?: {
|
options?: {
|
||||||
allCameras?: boolean;
|
allCameras?: boolean;
|
||||||
targetView?: 'recording' | 'recordings';
|
targetView?: FrigateCardView;
|
||||||
select?: ResultSelectType;
|
select?: ResultSelectType;
|
||||||
},
|
},
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
|
|||||||
@@ -1590,5 +1590,105 @@ describe('should handle version specific upgrades', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('should rename thumbnails.media to thumbnails.events_media_type', () => {
|
||||||
|
it.each([['all' as const], ['clips' as const], ['snapshots' as const]])(
|
||||||
|
'%s',
|
||||||
|
(mediaEventType: string) => {
|
||||||
|
const config = {
|
||||||
|
live: {
|
||||||
|
controls: {
|
||||||
|
thumbnails: {
|
||||||
|
media: mediaEventType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
live: {
|
||||||
|
controls: {
|
||||||
|
thumbnails: {
|
||||||
|
events_media_type: mediaEventType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should rename timeline.media to timeline.events_media_type', () => {
|
||||||
|
it.each([['all' as const], ['clips' as const], ['snapshots' as const]])(
|
||||||
|
'%s',
|
||||||
|
(mediaEventType: string) => {
|
||||||
|
const config = {
|
||||||
|
timeline: {
|
||||||
|
media: mediaEventType,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
timeline: {
|
||||||
|
events_media_type: mediaEventType,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should rename live.controls.timeline.media to live.controls.timeline.events_media_type', () => {
|
||||||
|
it.each([['all' as const], ['clips' as const], ['snapshots' as const]])(
|
||||||
|
'%s',
|
||||||
|
(mediaEventType: string) => {
|
||||||
|
const config = {
|
||||||
|
live: {
|
||||||
|
controls: {
|
||||||
|
timeline: {
|
||||||
|
media: mediaEventType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
live: {
|
||||||
|
controls: {
|
||||||
|
timeline: {
|
||||||
|
events_media_type: mediaEventType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should rename media_viewer.controls.timeline.media to media_viewer.controls.timeline.events_media_type', () => {
|
||||||
|
it.each([['all' as const], ['clips' as const], ['snapshots' as const]])(
|
||||||
|
'%s',
|
||||||
|
(mediaEventType: string) => {
|
||||||
|
const config = {
|
||||||
|
media_viewer: {
|
||||||
|
controls: {
|
||||||
|
timeline: {
|
||||||
|
media: mediaEventType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(upgradeConfig(config)).toBeTruthy();
|
||||||
|
expect(config).toEqual({
|
||||||
|
media_viewer: {
|
||||||
|
controls: {
|
||||||
|
timeline: {
|
||||||
|
events_media_type: mediaEventType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -77,7 +77,8 @@ describe('config defaults', () => {
|
|||||||
position: 'bottom-right',
|
position: 'bottom-right',
|
||||||
},
|
},
|
||||||
thumbnails: {
|
thumbnails: {
|
||||||
media: 'all',
|
media_type: 'events',
|
||||||
|
events_media_type: 'all',
|
||||||
mode: 'right',
|
mode: 'right',
|
||||||
show_details: true,
|
show_details: true,
|
||||||
show_download_control: true,
|
show_download_control: true,
|
||||||
@@ -87,7 +88,7 @@ describe('config defaults', () => {
|
|||||||
},
|
},
|
||||||
timeline: {
|
timeline: {
|
||||||
clustering_threshold: 3,
|
clustering_threshold: 3,
|
||||||
media: 'all',
|
events_media_type: 'all',
|
||||||
mode: 'none',
|
mode: 'none',
|
||||||
show_recordings: true,
|
show_recordings: true,
|
||||||
style: 'ribbon',
|
style: 'ribbon',
|
||||||
@@ -143,7 +144,7 @@ describe('config defaults', () => {
|
|||||||
},
|
},
|
||||||
timeline: {
|
timeline: {
|
||||||
clustering_threshold: 3,
|
clustering_threshold: 3,
|
||||||
media: 'all',
|
events_media_type: 'all',
|
||||||
mode: 'none',
|
mode: 'none',
|
||||||
show_recordings: true,
|
show_recordings: true,
|
||||||
style: 'ribbon',
|
style: 'ribbon',
|
||||||
@@ -268,7 +269,7 @@ describe('config defaults', () => {
|
|||||||
size: 100,
|
size: 100,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
media: 'all',
|
events_media_type: 'all',
|
||||||
show_recordings: true,
|
show_recordings: true,
|
||||||
style: 'stack',
|
style: 'stack',
|
||||||
window_seconds: 3600,
|
window_seconds: 3600,
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ describe('changeViewToRecentEventsForCameraAndDependents', () => {
|
|||||||
{},
|
{},
|
||||||
createView(),
|
createView(),
|
||||||
{
|
{
|
||||||
mediaType: 'clips',
|
eventsMediaType: 'clips',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
expect(elementHandler.viewHandler).not.toBeCalled();
|
expect(elementHandler.viewHandler).not.toBeCalled();
|
||||||
@@ -222,7 +222,7 @@ describe('changeViewToRecentEventsForCameraAndDependents', () => {
|
|||||||
{},
|
{},
|
||||||
createView(),
|
createView(),
|
||||||
{
|
{
|
||||||
mediaType: mediaType,
|
eventsMediaType: mediaType,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -10,10 +10,10 @@ export default defineConfig({
|
|||||||
// Thresholds will automatically be updated as coverage improves to avoid
|
// Thresholds will automatically be updated as coverage improves to avoid
|
||||||
// back-sliding.
|
// back-sliding.
|
||||||
thresholdAutoUpdate: true,
|
thresholdAutoUpdate: true,
|
||||||
statements: 72.72,
|
statements: 72.73,
|
||||||
branches: 61.97,
|
branches: 61.93,
|
||||||
functions: 74.01,
|
functions: 74.01,
|
||||||
lines: 72.62,
|
lines: 72.63,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user