Add initial keyboard shortcut support.
This commit is contained in:
@@ -31,6 +31,7 @@ import {
|
||||
import { BrowseMediaCamera } from './camera';
|
||||
import { BrowseMediaViewMediaFactory } from './media';
|
||||
import { BrowseMediaMetadata } from './types';
|
||||
import { getPTZCapabilitiesFromCameraConfig } from '../utils/ptz';
|
||||
|
||||
/**
|
||||
* A utility method to determine if a browse media object matches against a
|
||||
@@ -154,6 +155,7 @@ export class BrowseMediaCameraManagerEngine
|
||||
seek: false,
|
||||
snapshots: true,
|
||||
substream: true,
|
||||
ptz: getPTZCapabilitiesFromCameraConfig(cameraConfig) ?? undefined,
|
||||
},
|
||||
{
|
||||
disable: cameraConfig.capabilities?.disable,
|
||||
|
||||
@@ -8,10 +8,10 @@ import {
|
||||
subscribeToTrigger,
|
||||
} from '../utils/ha';
|
||||
import { EntityRegistryManager } from '../utils/ha/entity-registry';
|
||||
import { Capabilities } from './capabilities';
|
||||
import { CameraManagerEngine } from './engine';
|
||||
import { CameraNoIDError } from './error';
|
||||
import { CameraEventCallback } from './types';
|
||||
import { Capabilities } from './capabilities';
|
||||
|
||||
type DestroyCallback = () => Promise<void>;
|
||||
|
||||
|
||||
@@ -54,6 +54,18 @@ export class Capabilities {
|
||||
return this._capabilities.ptz ?? null;
|
||||
}
|
||||
|
||||
public hasPTZCapability(): boolean {
|
||||
return !!(
|
||||
this._capabilities.ptz?.down?.length ||
|
||||
this._capabilities.ptz?.up?.length ||
|
||||
this._capabilities.ptz?.left?.length ||
|
||||
this._capabilities.ptz?.right?.length ||
|
||||
this._capabilities.ptz?.zoomIn?.length ||
|
||||
this._capabilities.ptz?.zoomOut?.length ||
|
||||
this._capabilities.ptz?.presets?.length
|
||||
);
|
||||
}
|
||||
|
||||
public getRawCapabilities(): CapabilitiesRaw {
|
||||
return this._capabilities;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||
import { CameraConfig, PTZAction, PTZPhase } from '../config/types';
|
||||
import { CameraConfig, ActionPhase } from '../config/types';
|
||||
import { ExtendedHomeAssistant } from '../types';
|
||||
import { EntityRegistryManager } from '../utils/ha/entity-registry';
|
||||
import { ViewMedia } from '../view/media';
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
RecordingSegmentsQuery,
|
||||
RecordingSegmentsQueryResultsMap,
|
||||
} from './types';
|
||||
import { PTZAction } from '../config/ptz';
|
||||
|
||||
export const CAMERA_MANAGER_ENGINE_EVENT_LIMIT_DEFAULT = 10000;
|
||||
|
||||
@@ -139,7 +140,7 @@ export interface CameraManagerEngine {
|
||||
cameraConfig: CameraConfig,
|
||||
action: PTZAction,
|
||||
options?: {
|
||||
phase?: PTZPhase;
|
||||
phase?: ActionPhase;
|
||||
preset?: string;
|
||||
},
|
||||
): Promise<void>;
|
||||
|
||||
@@ -3,7 +3,10 @@ import uniq from 'lodash-es/uniq';
|
||||
import { CameraConfig } from '../../config/types';
|
||||
import { localize } from '../../localize/localize';
|
||||
import { PTZCapabilities, PTZMovementType } from '../../types';
|
||||
import { errorToConsole } from '../../utils/basic';
|
||||
import {
|
||||
errorToConsole,
|
||||
recursivelyMergeObjectsConcatenatingArraysUniquely,
|
||||
} from '../../utils/basic';
|
||||
import { subscribeToTrigger } from '../../utils/ha';
|
||||
import { EntityRegistryManager } from '../../utils/ha/entity-registry';
|
||||
import { Entity } from '../../utils/ha/entity-registry/types';
|
||||
@@ -13,6 +16,7 @@ import { CameraInitializationError } from '../error';
|
||||
import { getCameraEntityFromConfig } from '../utils/camera-entity-from-config';
|
||||
import { getPTZInfo } from './requests';
|
||||
import { PTZInfo, frigateEventChangeTriggerResponseSchema } from './types';
|
||||
import { getPTZCapabilitiesFromCameraConfig } from '../utils/ptz';
|
||||
|
||||
const CAMERA_BIRDSEYE = 'birdseye' as const;
|
||||
|
||||
@@ -97,7 +101,18 @@ export class FrigateCamera extends Camera {
|
||||
|
||||
protected async _initializeCapabilities(hass: HomeAssistant): Promise<void> {
|
||||
const config = this.getConfig();
|
||||
const ptz = await this._getPTZCapabilities(hass, config);
|
||||
|
||||
const configPTZCapabilities = getPTZCapabilitiesFromCameraConfig(this.getConfig());
|
||||
const frigatePTZCapabilities = await this._getPTZCapabilities(hass, config);
|
||||
const combinedPTZCapabilities =
|
||||
configPTZCapabilities || frigatePTZCapabilities
|
||||
? recursivelyMergeObjectsConcatenatingArraysUniquely(
|
||||
{},
|
||||
configPTZCapabilities,
|
||||
frigatePTZCapabilities,
|
||||
)
|
||||
: null;
|
||||
|
||||
const birdseye = isBirdseye(config);
|
||||
this._capabilities = new Capabilities(
|
||||
{
|
||||
@@ -110,7 +125,7 @@ export class FrigateCamera extends Camera {
|
||||
live: true,
|
||||
menu: true,
|
||||
substream: true,
|
||||
...(ptz && { ptz: ptz }),
|
||||
...(combinedPTZCapabilities && { ptz: combinedPTZCapabilities }),
|
||||
},
|
||||
{
|
||||
disable: config.capabilities?.disable,
|
||||
@@ -153,20 +168,25 @@ export class FrigateCamera extends Camera {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Note: The Frigate integration only supports continuous PTZ movements
|
||||
// (regardless of the actual underlying camera capability).
|
||||
const panTilt: PTZMovementType[] = [
|
||||
...(ptzInfo.features?.includes('pt') ? ['continuous' as const] : []),
|
||||
...(ptzInfo.features?.includes('pt-r') ? ['relative' as const] : []),
|
||||
];
|
||||
const zoom: PTZMovementType[] = [
|
||||
...(ptzInfo.features?.includes('zoom') ? ['continuous' as const] : []),
|
||||
...(ptzInfo.features?.includes('zoom-r') ? ['relative' as const] : []),
|
||||
];
|
||||
const presets = ptzInfo.presets;
|
||||
|
||||
if (panTilt.length || zoom.length || presets?.length) {
|
||||
return {
|
||||
...(panTilt && { panTilt: panTilt }),
|
||||
...(zoom && { zoom: zoom }),
|
||||
...(panTilt && {
|
||||
left: panTilt,
|
||||
right: panTilt,
|
||||
up: panTilt,
|
||||
down: panTilt,
|
||||
}),
|
||||
...(zoom && { zoomIn: zoom, zoomOut: zoom }),
|
||||
...(presets && { presets: presets }),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ import isEqual from 'lodash-es/isEqual';
|
||||
import orderBy from 'lodash-es/orderBy';
|
||||
import throttle from 'lodash-es/throttle';
|
||||
import uniqWith from 'lodash-es/uniqWith';
|
||||
import { CameraConfig, PTZAction, PTZPhase } from '../../config/types';
|
||||
import { PTZAction } from '../../config/ptz';
|
||||
import { ActionPhase, CameraConfig } from '../../config/types';
|
||||
import { ExtendedHomeAssistant } from '../../types';
|
||||
import {
|
||||
allPromises,
|
||||
@@ -19,8 +20,8 @@ import { ViewMediaClassifier } from '../../view/media-classifier';
|
||||
import { RecordingSegmentsCache, RequestCache } from '../cache';
|
||||
import { Camera } from '../camera';
|
||||
import {
|
||||
CAMERA_MANAGER_ENGINE_EVENT_LIMIT_DEFAULT,
|
||||
CameraManagerEngine,
|
||||
CAMERA_MANAGER_ENGINE_EVENT_LIMIT_DEFAULT,
|
||||
} from '../engine';
|
||||
import { GenericCameraManagerEngine } from '../generic/engine-generic';
|
||||
import { DateRange } from '../range';
|
||||
@@ -61,12 +62,12 @@ import { FrigateCamera, isBirdseye } from './camera';
|
||||
import { FrigateViewMediaFactory } from './media';
|
||||
import { FrigateViewMediaClassifier } from './media-classifier';
|
||||
import {
|
||||
NativeFrigateEventQuery,
|
||||
NativeFrigateRecordingSegmentsQuery,
|
||||
getEventSummary,
|
||||
getEvents,
|
||||
getEventSummary,
|
||||
getRecordingSegments,
|
||||
getRecordingsSummary,
|
||||
NativeFrigateEventQuery,
|
||||
NativeFrigateRecordingSegmentsQuery,
|
||||
retainEvent,
|
||||
} from './requests';
|
||||
import {
|
||||
@@ -1015,7 +1016,7 @@ export class FrigateCameraManagerEngine
|
||||
cameraConfig: CameraConfig,
|
||||
action: PTZAction,
|
||||
options?: {
|
||||
phase?: PTZPhase;
|
||||
phase?: ActionPhase;
|
||||
preset?: string;
|
||||
},
|
||||
): Promise<void> {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
|
||||
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||
import { CameraConfig, PTZAction, PTZPhase } from '../../config/types';
|
||||
import { ExtendedHomeAssistant } from '../../types';
|
||||
import { PTZAction, PTZ_PAN_TILT_ACTIONS, PTZ_ZOOM_ACTIONS } from '../../config/ptz';
|
||||
import { ActionPhase, CameraConfig } from '../../config/types';
|
||||
import { ExtendedHomeAssistant, PTZCapabilities, PTZMovementType } from '../../types';
|
||||
import { getEntityIcon, getEntityTitle } from '../../utils/ha';
|
||||
import { EntityRegistryManager } from '../../utils/ha/entity-registry';
|
||||
import { ViewMedia } from '../../view/media';
|
||||
@@ -35,6 +36,7 @@ import {
|
||||
} from '../types';
|
||||
import { getCameraEntityFromConfig } from '../utils/camera-entity-from-config';
|
||||
import { getDefaultGo2RTCEndpoint } from '../utils/go2rtc-endpoint';
|
||||
import { getPTZCapabilitiesFromCameraConfig } from '../utils/ptz';
|
||||
|
||||
export class GenericCameraManagerEngine implements CameraManagerEngine {
|
||||
protected _eventCallback?: CameraEventCallback;
|
||||
@@ -64,6 +66,7 @@ export class GenericCameraManagerEngine implements CameraManagerEngine {
|
||||
seek: false,
|
||||
snapshots: false,
|
||||
substream: true,
|
||||
ptz: getPTZCapabilitiesFromCameraConfig(cameraConfig) ?? undefined,
|
||||
},
|
||||
{
|
||||
disable: cameraConfig.capabilities?.disable,
|
||||
@@ -222,7 +225,7 @@ export class GenericCameraManagerEngine implements CameraManagerEngine {
|
||||
_cameraConfig: CameraConfig,
|
||||
_action: PTZAction,
|
||||
_options?: {
|
||||
phase?: PTZPhase;
|
||||
phase?: ActionPhase;
|
||||
preset?: string;
|
||||
},
|
||||
): Promise<void> {
|
||||
|
||||
@@ -3,7 +3,8 @@ import cloneDeep from 'lodash-es/cloneDeep';
|
||||
import sum from 'lodash-es/sum';
|
||||
import PQueue from 'p-queue';
|
||||
import { CardCameraAPI } from '../card-controller/types.js';
|
||||
import { CameraConfig, CamerasConfig, PTZAction, PTZPhase } from '../config/types.js';
|
||||
import { PTZAction } from '../config/ptz.js';
|
||||
import { ActionPhase, CameraConfig, CamerasConfig } from '../config/types.js';
|
||||
import { MEDIA_CHUNK_SIZE_DEFAULT } from '../const.js';
|
||||
import { localize } from '../localize/localize.js';
|
||||
import {
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
} from '../utils/basic.js';
|
||||
import { getCameraID } from '../utils/camera.js';
|
||||
import { log } from '../utils/debug.js';
|
||||
import { getConfiguredPTZAction } from './utils/ptz.js';
|
||||
import { ViewMedia } from '../view/media.js';
|
||||
import { Capabilities } from './capabilities.js';
|
||||
import { CameraManagerEngineFactory } from './engine-factory.js';
|
||||
@@ -771,18 +773,26 @@ export class CameraManager {
|
||||
public async executePTZAction(
|
||||
cameraID: string,
|
||||
action: PTZAction,
|
||||
options: {
|
||||
phase?: PTZPhase;
|
||||
options?: {
|
||||
phase?: ActionPhase;
|
||||
preset?: string;
|
||||
},
|
||||
): Promise<void> {
|
||||
const hass = this._api.getHASSManager().getHASS();
|
||||
const engine = this._store.getEngineForCameraID(cameraID);
|
||||
const cameraConfig = this._store.getCameraConfig(cameraID);
|
||||
|
||||
if (!engine || !cameraConfig || !hass) {
|
||||
if (!cameraConfig) {
|
||||
return;
|
||||
}
|
||||
return engine.executePTZAction(hass, cameraConfig, action, options);
|
||||
const configuredAction = getConfiguredPTZAction(cameraConfig, action, options);
|
||||
if (configuredAction) {
|
||||
return await this._api.getActionsManager().executeActions(configuredAction);
|
||||
}
|
||||
|
||||
const hass = this._api.getHASSManager().getHASS();
|
||||
const engine = this._store.getEngineForCameraID(cameraID);
|
||||
|
||||
if (!engine || !hass) {
|
||||
return;
|
||||
}
|
||||
return await engine.executePTZAction(hass, cameraConfig, action, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { PTZAction, PTZBaseAction } from '../../config/ptz';
|
||||
import { ActionPhase, ActionType, CameraConfig } from '../../config/types';
|
||||
import { PTZCapabilities, PTZMovementType } from '../../types';
|
||||
|
||||
export const getConfiguredPTZAction = (
|
||||
cameraConfig: CameraConfig,
|
||||
action: PTZAction,
|
||||
options?: {
|
||||
phase?: ActionPhase;
|
||||
preset?: string;
|
||||
},
|
||||
): ActionType | ActionType[] | null => {
|
||||
if (action === 'preset') {
|
||||
return (options?.preset ? cameraConfig.ptz.presets?.[options.preset] : null) ?? null;
|
||||
}
|
||||
|
||||
if (options?.phase) {
|
||||
return cameraConfig.ptz[`actions_${action}_${options.phase}`] ?? null;
|
||||
}
|
||||
|
||||
return cameraConfig.ptz[`actions_${action}`] ?? null;
|
||||
};
|
||||
|
||||
const hasConfiguredPTZAction = (
|
||||
cameraConfig: CameraConfig,
|
||||
action: PTZBaseAction,
|
||||
options?: {
|
||||
phase?: ActionPhase;
|
||||
preset?: string;
|
||||
},
|
||||
): boolean => {
|
||||
return !!getConfiguredPTZAction(cameraConfig, action, options);
|
||||
};
|
||||
|
||||
export const getConfiguredPTZMovementType = (
|
||||
cameraConfig: CameraConfig,
|
||||
action: PTZBaseAction,
|
||||
): PTZMovementType[] | null => {
|
||||
const continuous =
|
||||
hasConfiguredPTZAction(cameraConfig, action, { phase: 'start' }) &&
|
||||
hasConfiguredPTZAction(cameraConfig, action, { phase: 'stop' });
|
||||
const relative = hasConfiguredPTZAction(cameraConfig, action);
|
||||
|
||||
return continuous || relative
|
||||
? [
|
||||
...(continuous ? ['continuous' as const] : []),
|
||||
...(relative ? ['relative' as const] : []),
|
||||
]
|
||||
: null;
|
||||
};
|
||||
|
||||
export const getPTZCapabilitiesFromCameraConfig = (
|
||||
cameraConfig: CameraConfig,
|
||||
): PTZCapabilities | null => {
|
||||
const left = getConfiguredPTZMovementType(cameraConfig, 'left');
|
||||
const right = getConfiguredPTZMovementType(cameraConfig, 'right');
|
||||
const up = getConfiguredPTZMovementType(cameraConfig, 'up');
|
||||
const down = getConfiguredPTZMovementType(cameraConfig, 'down');
|
||||
const zoomIn = getConfiguredPTZMovementType(cameraConfig, 'zoom_in');
|
||||
const zoomOut = getConfiguredPTZMovementType(cameraConfig, 'zoom_out');
|
||||
const presets = cameraConfig.ptz.presets
|
||||
? Object.keys(cameraConfig.ptz.presets)
|
||||
: undefined;
|
||||
|
||||
return left?.length ||
|
||||
right?.length ||
|
||||
up?.length ||
|
||||
down?.length ||
|
||||
zoomIn?.length ||
|
||||
zoomOut?.length ||
|
||||
presets?.length
|
||||
? {
|
||||
left: left ?? undefined,
|
||||
right: right ?? undefined,
|
||||
up: up ?? undefined,
|
||||
down: down ?? undefined,
|
||||
zoomIn: zoomIn ?? undefined,
|
||||
zoomOut: zoomOut ?? undefined,
|
||||
presets: presets,
|
||||
}
|
||||
: null;
|
||||
};
|
||||
Reference in New Issue
Block a user