Files
advanced-camera-card/src/camera-manager/browse-media/engine-browse-media.ts
T

171 lines
5.0 KiB
TypeScript

import { HomeAssistant } from 'custom-card-helpers';
import { CameraConfig, ExtendedHomeAssistant } from '../../types';
import { ViewMedia } from '../../view/media';
import {
CameraManagerMediaCapabilities,
DataQuery,
EventQuery,
PartialEventQuery,
CameraConfigs,
CameraManagerCameraCapabilities,
QueryType,
CameraEndpoint,
} from '../types';
import { EntityRegistryManager } from '../../utils/ha/entity-registry';
import { CameraManagerEngine } from '../engine';
import { GenericCameraManagerEngine } from '../generic/engine-generic';
import { CameraInitializationError } from '../error';
import { localize } from '../../localize/localize';
import { Entity } from '../../utils/ha/entity-registry/types';
import { BrowseMediaManager } from '../../utils/ha/browse-media/browse-media-manager';
import {
BROWSE_MEDIA_CACHE_SECONDS,
RichBrowseMedia,
} from '../../utils/ha/browse-media/types';
import { BrowseMediaMetadata } from './types';
import { rangesOverlap } from '../range';
import { ResolvedMediaCache, resolveMedia } from '../../utils/ha/resolved-media';
import { canonicalizeHAURL } from '../../utils/ha';
import { RequestCache } from '../cache';
/**
* A base class for cameras that read events from HA BrowseMedia interface.
*/
export class BrowseMediaCameraManagerEngine
extends GenericCameraManagerEngine
implements CameraManagerEngine
{
protected _cameraEntities: Map<string, Entity> = new Map();
protected _browseMediaManager: BrowseMediaManager<BrowseMediaMetadata>;
protected _resolvedMediaCache: ResolvedMediaCache;
protected _requestCache: RequestCache;
public constructor(
browseMediaManager: BrowseMediaManager<BrowseMediaMetadata>,
resolvedMediaCache: ResolvedMediaCache,
requestCache: RequestCache,
) {
super();
this._browseMediaManager = browseMediaManager;
this._resolvedMediaCache = resolvedMediaCache;
this._requestCache = requestCache;
}
public async initializeCamera(
hass: HomeAssistant,
entityRegistryManager: EntityRegistryManager,
cameraConfig: CameraConfig,
): Promise<CameraConfig> {
const entity = cameraConfig.camera_entity
? await entityRegistryManager.getEntity(hass, cameraConfig.camera_entity)
: null;
if (!entity || !cameraConfig.camera_entity) {
throw new CameraInitializationError(
localize('error.no_camera_entity'),
cameraConfig,
);
}
this._cameraEntities.set(cameraConfig.camera_entity, entity);
return cameraConfig;
}
public generateDefaultEventQuery(
_cameras: CameraConfigs,
cameraIDs: Set<string>,
query: PartialEventQuery,
): EventQuery[] | null {
return [
{
type: QueryType.Event,
cameraIDs: cameraIDs,
...query,
},
];
}
/**
* A utility method to determine if a browse media object matches against a
* start and end date.
* @param media The browse media object (with rich metadata).
* @param start The optional start date.
* @param end The optional end date.
* @returns `true` if the media falls within the provided dates.
*/
protected _mediaIsWithinDates = (
media: RichBrowseMedia<BrowseMediaMetadata>,
start?: Date,
end?: Date,
): boolean => {
// If no date is specified at all, everything matches.
const dateReference = start ?? end;
if (!dateReference) {
return true;
}
// If there's no metadata, nothing matches.
if (!media._metadata) {
return false;
}
// Determine if:
// - The media starts within the query timeframe.
// - The media ends within the query timeframe.
// - The media entirely encompasses the query timeframe.
return rangesOverlap(
{
start: media._metadata.startDate,
end: media._metadata.endDate,
},
{
start: start ?? dateReference,
end: end ?? dateReference,
},
);
};
public async getMediaDownloadPath(
hass: ExtendedHomeAssistant,
_cameraConfig: CameraConfig,
media: ViewMedia,
): Promise<CameraEndpoint | null> {
const contentID = media.getContentID();
if (!contentID) {
return null;
}
const resolvedMedia = await resolveMedia(hass, contentID, this._resolvedMediaCache);
return resolvedMedia
? { endpoint: canonicalizeHAURL(hass, resolvedMedia.url) }
: null;
}
public getQueryResultMaxAge(query: DataQuery): number | null {
if (query.type === QueryType.Event) {
return BROWSE_MEDIA_CACHE_SECONDS;
}
return null;
}
public getCameraCapabilities(
cameraConfig: CameraConfig,
): CameraManagerCameraCapabilities | null {
const parentCapabilities = super.getCameraCapabilities(cameraConfig);
if (!parentCapabilities) {
return null;
}
return {
...parentCapabilities,
supportsClips: true,
supportsSnapshots: true,
supportsTimeline: true,
};
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getMediaCapabilities(_media: ViewMedia): CameraManagerMediaCapabilities {
return {
canFavorite: false,
canDownload: true,
};
}
}