feat: Add support for Frigate reviews / detections [initial PR] (#2315)
- Add support for Frigate reviews / detections. - Add support for GenAI metadata. - Significant internal refactor to more flexible "UnifiedQuery" to allow mixing cameras with simple metadata and review metadata (e.g. a timeline view of a Frigate camera with reviews, and a Reolink camera with simple metadata). - Add support for folder media as camera media. There are a few more PRs to commit prior to this going live, but commiting this for now due to the scale of the change. BREAKING CHANGE: `media_type` and `events_type` are retired under `live`, `viewer` and `timeline` configuration sections, instead media type is associated (once) with the camera under `media`.
This commit is contained in:
@@ -7,22 +7,18 @@ import {
|
||||
unsafeCSS,
|
||||
} from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { CameraManager } from '../camera-manager/manager.js';
|
||||
import { FoldersManager } from '../card-controller/folders/manager.js';
|
||||
import { ViewItemManager } from '../card-controller/view/item-manager.js';
|
||||
import { ViewManagerEpoch } from '../card-controller/view/types.js';
|
||||
import { TimelineKeys } from '../components-lib/timeline/types.js';
|
||||
import { ConditionStateManagerReadonlyInterface } from '../conditions/types.js';
|
||||
import { ThumbnailsControlConfig } from '../config/schema/common/controls/thumbnails.js';
|
||||
import { MiniTimelineControlConfig } from '../config/schema/common/controls/timeline.js';
|
||||
import { FolderConfig } from '../config/schema/folders.js';
|
||||
import { CardWideConfig } from '../config/schema/types.js';
|
||||
import { HomeAssistant } from '../ha/types.js';
|
||||
import basicBlockStyle from '../scss/basic-block.scss';
|
||||
import { contentsChanged } from '../utils/basic.js';
|
||||
import { fireAdvancedCameraCardEvent } from '../utils/fire-advanced-camera-card-event.js';
|
||||
import { QueryClassifier } from '../view/query-classifier.js';
|
||||
import './surround-basic.js';
|
||||
import './thumbnail-carousel';
|
||||
|
||||
@@ -55,11 +51,8 @@ export class AdvancedCameraCardSurround extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public cardWideConfig?: CardWideConfig;
|
||||
|
||||
protected _timelineKeys?: TimelineKeys | null = null;
|
||||
|
||||
/**
|
||||
* Determine if a drawer is being used.
|
||||
* @returns `true` if a drawer is used, `false` otherwise.
|
||||
*/
|
||||
protected _hasDrawer(): boolean {
|
||||
return (
|
||||
@@ -67,80 +60,11 @@ export class AdvancedCameraCardSurround extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
protected willUpdate(changedProperties: PropertyValues): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
protected willUpdate(_changedProperties: PropertyValues): void {
|
||||
if (this.timelineConfig?.mode && this.timelineConfig.mode !== 'none') {
|
||||
import('./timeline-core.js');
|
||||
}
|
||||
|
||||
// Only reset the timeline cameraIDs when the media or display mode
|
||||
// materially changes (and not on every view change, since the view will
|
||||
// change frequently when the user is scrubbing video).
|
||||
const view = this.viewManagerEpoch?.manager.getView();
|
||||
if (
|
||||
changedProperties.has('viewManagerEpoch') &&
|
||||
(this.viewManagerEpoch?.manager.hasMajorMediaChange(
|
||||
this.viewManagerEpoch?.oldView,
|
||||
) ||
|
||||
this.viewManagerEpoch?.oldView?.displayMode !== view?.displayMode)
|
||||
) {
|
||||
const newKeys = this._getTimelineKeys();
|
||||
// Update only if changed, to avoid unnecessary timeline destructions.
|
||||
if (!isEqual(newKeys, this._timelineKeys)) {
|
||||
this._timelineKeys = newKeys ?? undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected _getTimelineKeys(): TimelineKeys | null {
|
||||
const cameraIDsToKeys = (cameraIDs: Set<string> | null): TimelineKeys | null => {
|
||||
return cameraIDs?.size
|
||||
? {
|
||||
type: 'camera',
|
||||
cameraIDs,
|
||||
}
|
||||
: null;
|
||||
};
|
||||
|
||||
const folderToKeys = (folderConfig: FolderConfig): TimelineKeys => {
|
||||
return {
|
||||
type: 'folder',
|
||||
folder: folderConfig,
|
||||
};
|
||||
};
|
||||
|
||||
const view = this.viewManagerEpoch?.manager.getView();
|
||||
if (!view || !this.cameraManager) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (view.is('live')) {
|
||||
const capabilitySearch = {
|
||||
anyCapabilities: ['clips' as const, 'snapshots' as const, 'recordings' as const],
|
||||
};
|
||||
if (view.supportsMultipleDisplayModes() && view.isGrid()) {
|
||||
return cameraIDsToKeys(
|
||||
this.cameraManager.getStore().getCameraIDsWithCapability(capabilitySearch),
|
||||
);
|
||||
} else {
|
||||
return cameraIDsToKeys(
|
||||
this.cameraManager
|
||||
.getStore()
|
||||
.getAllDependentCameras(view.camera, capabilitySearch),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const queries = view.query;
|
||||
if (view.isViewerView()) {
|
||||
if (QueryClassifier.isMediaQuery(queries)) {
|
||||
return cameraIDsToKeys(queries.getQueryCameraIDs());
|
||||
} else if (QueryClassifier.isFolderQuery(queries)) {
|
||||
const folderConfig = queries.getQuery()?.folder;
|
||||
return folderConfig ? folderToKeys(folderConfig) : null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
@@ -175,7 +99,9 @@ export class AdvancedCameraCardSurround extends LitElement {
|
||||
.viewItemManager=${this.viewItemManager}
|
||||
.fadeThumbnails=${view.isViewerView()}
|
||||
.viewManagerEpoch=${this.viewManagerEpoch}
|
||||
.foldersManager=${this.foldersManager}
|
||||
.selected=${view.queryResults?.getSelectedIndex() ?? undefined}
|
||||
.cardWideConfig=${this.cardWideConfig}
|
||||
>
|
||||
</advanced-camera-card-thumbnail-carousel>`
|
||||
: ''}
|
||||
@@ -189,7 +115,6 @@ export class AdvancedCameraCardSurround extends LitElement {
|
||||
this.thumbnailConfig?.mode === 'none'
|
||||
? 'play'
|
||||
: 'select'}
|
||||
.keys=${this._timelineKeys}
|
||||
.mini=${true}
|
||||
.timelineConfig=${this.timelineConfig}
|
||||
.thumbnailConfig=${this.thumbnailConfig}
|
||||
|
||||
Reference in New Issue
Block a user