Add out-of-the-box Frigate PTZ support.

This commit is contained in:
Dermot Duffy
2023-10-29 14:10:41 -07:00
parent fa43d2e439
commit e12912fa85
74 changed files with 3981 additions and 1514 deletions
+82 -38
View File
@@ -1,7 +1,8 @@
import { CameraConfig } from '../config/types';
import { ViewMedia } from '../view/media';
import { Camera } from './camera';
import { CameraManagerEngine } from './engine';
import { CameraConfigs, Engine } from './types';
import { Engine } from './types';
type CameraManagerEngineCameraIDMap = Map<CameraManagerEngine, Set<string>>;
@@ -10,74 +11,82 @@ export interface CameraManagerReadOnlyConfigStore {
getCameraConfigForMedia(media: ViewMedia): CameraConfig | null;
hasCameraID(cameraID: string): boolean;
hasVisibleCameraID(cameraID: string): boolean;
getCamera(cameraID: string): Camera | null;
getCameraCount(): number;
getVisibleCameraCount(): number;
getCameras(): CameraConfigs;
getVisibleCameras(): CameraConfigs;
getCameraConfigs(cameraIDs?: Iterable<string>): IterableIterator<CameraConfig>;
getCameraConfigEntries(
cameraIDs?: Iterable<string>,
): IterableIterator<[string, CameraConfig]>;
getCameraIDs(): Set<string>;
getVisibleCameraIDs(): Set<string>;
getAllDependentCameras(cameraID: string): Set<string>;
}
export class CameraManagerStore implements CameraManagerReadOnlyConfigStore {
protected _allConfigs: Map<string, CameraConfig> = new Map();
protected _visibleConfigs: Map<string, CameraConfig> = new Map();
protected _enginesByCamera: Map<string, CameraManagerEngine> = new Map();
protected _cameras: Map<string, Camera> = new Map();
protected _enginesByType: Map<Engine, CameraManagerEngine> = new Map();
public addCamera(
cameraID: string,
cameraConfig: CameraConfig,
engine: CameraManagerEngine,
): void {
if (!cameraConfig.hide) {
this._visibleConfigs.set(cameraID, cameraConfig);
}
this._allConfigs.set(cameraID, cameraConfig);
this._enginesByCamera.set(cameraID, engine);
this._enginesByType.set(engine.getEngineType(), engine);
public addCamera(camera: Camera): void {
this._cameras.set(camera.getID(), camera);
this._enginesByType.set(camera.getEngine().getEngineType(), camera.getEngine());
}
public reset(): void {
this._allConfigs.clear();
this._visibleConfigs.clear();
this._enginesByCamera.clear();
this._cameras.clear();
this._enginesByType.clear();
}
public getCamera(cameraID: string): Camera | null {
return this._cameras.get(cameraID) ?? null;
}
public getCameraConfig(cameraID: string): CameraConfig | null {
return this._allConfigs.get(cameraID) ?? null;
return this._cameras.get(cameraID)?.getConfig() ?? null;
}
public hasCameraID(cameraID: string): boolean {
return this._allConfigs.has(cameraID);
}
public hasVisibleCameraID(cameraID: string): boolean {
return this._visibleConfigs.has(cameraID);
return this._cameras.has(cameraID);
}
public getCameraCount(): number {
return this._allConfigs.size;
return this._cameras.size;
}
public getVisibleCameraCount(): number {
return this._visibleConfigs.size;
return this.getVisibleCameraIDs().size;
}
public getCameras(): CameraConfigs {
return this._allConfigs;
public getCameras(): Map<string, Camera> {
return this._cameras;
}
public getVisibleCameras(): CameraConfigs {
return this._visibleConfigs;
public *getCameraConfigs(
cameraIDs?: Iterable<string>,
): IterableIterator<CameraConfig> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for (const [_cameraID, config] of this.getCameraConfigEntries(cameraIDs)) {
yield config;
}
}
public *getCameraConfigEntries(
cameraIDs?: Iterable<string>,
): IterableIterator<[string, CameraConfig]> {
for (const cameraID of cameraIDs ?? this._cameras.keys()) {
const config = this.getCameraConfig(cameraID);
if (config) {
yield [cameraID, config];
}
}
}
public getCameraIDs(): Set<string> {
return new Set(this._allConfigs.keys());
return new Set(this._cameras.keys());
}
public getVisibleCameraIDs(): Set<string> {
return new Set(this._visibleConfigs.keys());
return this._getMatchingCameraIDs((camera) => !camera.getConfig().hide);
}
public getCameraConfigForMedia(media: ViewMedia): CameraConfig | null {
@@ -89,7 +98,7 @@ export class CameraManagerStore implements CameraManagerReadOnlyConfigStore {
}
public getEngineForCameraID(cameraID: string): CameraManagerEngine | null {
return this._enginesByCamera.get(cameraID) ?? null;
return this._cameras.get(cameraID)?.getEngine() ?? null;
}
public getEnginesForCameraIDs(
@@ -114,7 +123,42 @@ export class CameraManagerStore implements CameraManagerReadOnlyConfigStore {
return this.getEngineForCameraID(media.getCameraID());
}
public getAllEngines(): CameraManagerEngine[] {
return [...this._enginesByType.values()];
/**
* Get all cameras that depend on a given camera.
* @param cameraManager The camera manager.
* @param cameraID ID of the target camera.
* @returns A set of dependent cameraIDs or null (since JS sets guarantee order,
* the first item in the set is guaranteed to be the cameraID itself).
*/
public getAllDependentCameras(cameraID: string): Set<string> {
const cameraIDs: Set<string> = new Set();
const getDependentCameras = (cameraID: string): void => {
const cameraConfig = this.getCameraConfig(cameraID);
if (cameraConfig) {
cameraIDs.add(cameraID);
const dependentCameras: Set<string> = new Set();
cameraConfig.dependencies.cameras.forEach((item) => dependentCameras.add(item));
if (cameraConfig.dependencies.all_cameras) {
this.getCameraIDs().forEach((cameraID) => dependentCameras.add(cameraID));
}
for (const eventCameraID of dependentCameras) {
if (!cameraIDs.has(eventCameraID)) {
getDependentCameras(eventCameraID);
}
}
}
};
getDependentCameras(cameraID);
return cameraIDs;
}
protected _getMatchingCameraIDs(func: (camera: Camera) => boolean): Set<string> {
const output = new Set<string>();
for (const [cameraID, camera] of this._cameras.entries()) {
if (func(camera)) {
output.add(cameraID);
}
}
return output;
}
}