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
+16
View File
@@ -216,6 +216,22 @@ export class ActionsManager {
.getViewManager()
.setViewWithNewDisplayMode(frigateCardAction.display_mode);
break;
case 'ptz':
const cameraID = this._api.getViewManager().getView()?.camera;
if (cameraID) {
this._api
.getCameraManager()
.executePTZAction(cameraID, frigateCardAction.ptz_action, {
phase: frigateCardAction.ptz_phase,
preset: frigateCardAction.ptz_preset,
});
}
break;
case 'show_ptz':
this._api
.getViewManager()
.setViewWithNewContext({ live: { ptzVisible: frigateCardAction.show_ptz } });
break;
default:
console.warn(`Frigate card received unknown card action: ${action}`);
}
+10 -7
View File
@@ -13,7 +13,7 @@ export enum InitializationAspect {
export class InitializationManager {
protected _api: CardInitializerAPI;
protected _initializer;
protected _initializer: Initializer;
constructor(api: CardInitializerAPI, initializer?: Initializer) {
this._api = api;
@@ -21,11 +21,15 @@ export class InitializationManager {
}
public isInitializedMandatory(): boolean {
return this._initializer.isInitializedMultiple([
InitializationAspect.LANGUAGES,
InitializationAspect.SIDE_LOAD_ELEMENTS,
InitializationAspect.CAMERAS,
]);
return (
this._initializer.isInitializedMultiple([
InitializationAspect.LANGUAGES,
InitializationAspect.SIDE_LOAD_ELEMENTS,
InitializationAspect.CAMERAS,
]) &&
// If there's no view, re-initialize (e.g. config changes).
this._api.getViewManager().hasView()
);
}
/**
@@ -80,7 +84,6 @@ export class InitializationManager {
}
}
this._api.getCardElementManager().update();
return true;
}
+4 -2
View File
@@ -25,10 +25,10 @@ export class MediaPlayerManager {
return this._mediaPlayers.length > 0;
}
public async initialize(): Promise<void> {
public async initialize(): Promise<boolean> {
const hass = this._api.getHASSManager().getHASS();
if (!hass) {
return;
return false;
}
const isValidMediaPlayer = (entityID: string): boolean => {
@@ -66,6 +66,8 @@ export class MediaPlayerManager {
const entity = mediaPlayerEntities?.get(entityID);
return !entity || !entity.hidden_by;
});
return true;
}
public async stop(mediaPlayer: string): Promise<void> {
+6 -2
View File
@@ -16,7 +16,7 @@ export class MicrophoneManager {
this._api = api;
}
public async connect(): Promise<void> {
public async connect(): Promise<boolean> {
try {
this._stream = await navigator.mediaDevices.getUserMedia({
audio: true,
@@ -24,15 +24,19 @@ export class MicrophoneManager {
});
} catch (e: unknown) {
errorToConsole(e as Error);
this._stream = null;
this._api.getCardElementManager().update();
return false;
}
this._setMute();
return true;
}
public async disconnect(): Promise<void> {
this._stream?.getTracks().forEach((track) => track.stop());
this._stream = undefined;
this._stream = undefined;
this._api.getCardElementManager().update();
}
+3 -4
View File
@@ -1,5 +1,5 @@
import { FrigateCardCustomAction, FrigateCardViewAction } from '../config/types';
import { createFrigateCardCustomAction } from '../utils/action.js';
import { createFrigateCardCameraAction, createFrigateCardSimpleAction } from '../utils/action.js';
import { CardQueryStringAPI } from './types';
import { ViewManagerSetViewParameters } from './view-manager';
@@ -139,8 +139,7 @@ export class QueryStringManager {
case 'camera_select':
case 'live_substream_select':
if (value) {
customAction = createFrigateCardCustomAction(action, {
camera: value,
customAction = createFrigateCardCameraAction(action, value, {
cardID: cardID,
});
}
@@ -160,7 +159,7 @@ export class QueryStringManager {
case 'snapshot':
case 'snapshots':
case 'timeline':
customAction = createFrigateCardCustomAction(action, {
customAction = createFrigateCardSimpleAction(action, {
cardID: cardID,
});
break;
+8 -2
View File
@@ -28,8 +28,14 @@ export class TriggersManager {
const now = new Date();
let triggerChanges = false;
const cameras = this._api.getCameraManager().getStore().getVisibleCameras();
for (const [cameraID, config] of cameras?.entries()) {
const visibleCameraIDs = this._api
.getCameraManager()
.getStore()
.getVisibleCameraIDs();
for (const [cameraID, config] of this._api
.getCameraManager()
.getStore()
.getCameraConfigEntries(visibleCameraIDs)) {
const triggerEntities = config.triggers.entities;
const diffs = getHassDifferences(hass, oldHass, triggerEntities, {
stateOnly: true,
+5 -2
View File
@@ -1,7 +1,6 @@
import { ViewContext } from 'view';
import { FrigateCardConfig, FrigateCardView, ViewDisplayMode } from '../config/types';
import { View } from '../view/view';
import { getAllDependentCameras } from '../utils/camera';
import { log } from '../utils/debug';
import { executeMediaQueryForView } from '../utils/media-to-view';
import { CardViewAPI } from './types';
@@ -28,6 +27,10 @@ export class ViewManager {
return this._view;
}
public hasView(): boolean {
return !!this.getView();
}
public setView(view: View): void {
this._setView(view);
}
@@ -255,7 +258,7 @@ export class ViewManager {
protected _createViewWithNextStream(baseView: View): View {
const dependencies = [
...getAllDependentCameras(this._api.getCameraManager(), baseView.camera),
...this._api.getCameraManager().getStore().getAllDependentCameras(baseView.camera),
];
if (dependencies.length <= 1) {
return baseView.clone();