Initial version of substream support.
This commit is contained in:
@@ -6,7 +6,6 @@ import {
|
||||
DataQuery,
|
||||
EventQuery,
|
||||
EventQueryResultsMap,
|
||||
MediaMetadata,
|
||||
PartialEventQuery,
|
||||
PartialRecordingQuery,
|
||||
PartialRecordingSegmentsQuery,
|
||||
|
||||
@@ -19,7 +19,6 @@ import {
|
||||
EventQuery,
|
||||
EventQueryResults,
|
||||
EventQueryResultsMap,
|
||||
MediaMetadata,
|
||||
PartialEventQuery,
|
||||
PartialRecordingQuery,
|
||||
PartialRecordingSegmentsQuery,
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { HomeAssistant } from 'custom-card-helpers';
|
||||
import { CameraConfig, CamerasConfig, CardWideConfig } from '../types.js';
|
||||
import {
|
||||
CameraConfig,
|
||||
CamerasConfig,
|
||||
CardWideConfig,
|
||||
} from '../types.js';
|
||||
import { allPromises, arrayify, setify } from '../utils/basic.js';
|
||||
import {
|
||||
CameraManagerCameraCapabilities,
|
||||
@@ -46,7 +50,7 @@ import { EntityRegistryManager } from '../utils/ha/entity-registry/index.js';
|
||||
import { getCameraID } from '../utils/camera.js';
|
||||
import { localize } from '../localize/localize.js';
|
||||
import { CameraInitializationError } from './error.js';
|
||||
import { CameraManagerStore } from './store.js';
|
||||
import { CameraManagerReadOnlyConfigStore, CameraManagerStore } from './store.js';
|
||||
import cloneDeep from 'lodash-es/cloneDeep';
|
||||
import { MEDIA_CHUNK_SIZE_DEFAULT } from '../const.js';
|
||||
|
||||
@@ -126,11 +130,15 @@ export class CameraManager {
|
||||
const output: Map<CameraConfig, CameraManagerEngine> = new Map();
|
||||
const engines: Map<Engine, CameraManagerEngine> = new Map();
|
||||
|
||||
for (const cameraConfig of camerasConfig) {
|
||||
const engineType = await this._engineFactory.getEngineForCamera(
|
||||
hass,
|
||||
cameraConfig,
|
||||
const getEngineTypes = async (configs: CameraConfig[]) => {
|
||||
return await allPromises(configs, (config) =>
|
||||
this._engineFactory.getEngineForCamera(hass, config),
|
||||
);
|
||||
};
|
||||
|
||||
const engineTypes = await getEngineTypes(camerasConfig);
|
||||
for (const [index, cameraConfig] of camerasConfig.entries()) {
|
||||
const engineType = engineTypes[index];
|
||||
const engine = engineType
|
||||
? engines.get(engineType) ?? this._engineFactory.createEngine(engineType)
|
||||
: null;
|
||||
@@ -218,8 +226,8 @@ export class CameraManager {
|
||||
this._store.addCamera(id, result.initializedConfig, result.engine);
|
||||
});
|
||||
|
||||
if (!this._store.getCameraCount()) {
|
||||
throw new CameraInitializationError(localize('error.no_cameras'));
|
||||
if (!this._store.getVisibleCameraCount()) {
|
||||
throw new CameraInitializationError(localize('error.no_visible_cameras'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,22 +235,8 @@ export class CameraManager {
|
||||
return this._store.getCameraCount() > 0;
|
||||
}
|
||||
|
||||
public getCameras(): Map<string, CameraConfig> | null {
|
||||
return this._store.getCameras();
|
||||
}
|
||||
|
||||
public getCameraConfig(cameraID: string): CameraConfig | null {
|
||||
return this._store.getCameraConfig(cameraID);
|
||||
}
|
||||
|
||||
public getCameraIDs(): Set<string> | null {
|
||||
return this._store.getCameraCount()
|
||||
? new Set(this._store.getCameras().keys())
|
||||
: null;
|
||||
}
|
||||
|
||||
public hasCameraID(cameraID: string): boolean {
|
||||
return this._store.hasCameraID(cameraID);
|
||||
public getStore(): CameraManagerReadOnlyConfigStore {
|
||||
return this._store;
|
||||
}
|
||||
|
||||
public generateDefaultEventQueries(
|
||||
@@ -287,7 +281,7 @@ export class CameraManager {
|
||||
|
||||
const results = await this._handleQuery(hass, query);
|
||||
|
||||
for (const [query, result] of results?.entries() ?? []) {
|
||||
for (const result of results?.values() ?? []) {
|
||||
if (result.metadata.what) {
|
||||
result.metadata.what.forEach(what.add, what);
|
||||
}
|
||||
@@ -325,19 +319,19 @@ export class CameraManager {
|
||||
let queries: DataQuery[] | null = null;
|
||||
if (QueryClassifier.isEventQuery(partialQuery)) {
|
||||
queries = engine.generateDefaultEventQuery(
|
||||
this._store.getCameras(),
|
||||
this._store.getVisibleCameras(),
|
||||
cameraIDs,
|
||||
partialQuery,
|
||||
);
|
||||
} else if (QueryClassifier.isRecordingQuery(partialQuery)) {
|
||||
queries = engine.generateDefaultRecordingQuery(
|
||||
this._store.getCameras(),
|
||||
this._store.getVisibleCameras(),
|
||||
cameraIDs,
|
||||
partialQuery,
|
||||
);
|
||||
} else if (QueryClassifier.isRecordingSegmentsQuery(partialQuery)) {
|
||||
queries = engine.generateDefaultRecordingSegmentsQuery(
|
||||
this._store.getCameras(),
|
||||
this._store.getVisibleCameras(),
|
||||
cameraIDs,
|
||||
partialQuery,
|
||||
);
|
||||
|
||||
+43
-10
@@ -5,8 +5,26 @@ import { CameraConfigs, Engine } from './types';
|
||||
|
||||
type CameraManagerEngineCameraIDMap = Map<CameraManagerEngine, Set<string>>;
|
||||
|
||||
export class CameraManagerStore {
|
||||
protected _configs: Map<string, CameraConfig> = new Map();
|
||||
export interface CameraManagerReadOnlyConfigStore {
|
||||
getCameraConfig(cameraID: string): CameraConfig | null;
|
||||
getCameraConfigForMedia(media: ViewMedia): CameraConfig | null;
|
||||
|
||||
hasCameraID(cameraID: string): boolean;
|
||||
hasVisibleCameraID(cameraID: string): boolean;
|
||||
|
||||
getCameraCount(): number;
|
||||
getVisibleCameraCount(): number;
|
||||
|
||||
getCameras(): CameraConfigs;
|
||||
getVisibleCameras(): CameraConfigs;
|
||||
|
||||
getCameraIDs(): Set<string>;
|
||||
getVisibleCameraIDs(): 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 _enginesByType: Map<Engine, CameraManagerEngine> = new Map();
|
||||
|
||||
@@ -15,29 +33,44 @@ export class CameraManagerStore {
|
||||
cameraConfig: CameraConfig,
|
||||
engine: CameraManagerEngine,
|
||||
): void {
|
||||
this._configs.set(cameraID, cameraConfig);
|
||||
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 getCameraCount(): number {
|
||||
return this._configs.size;
|
||||
public getCameraConfig(cameraID: string): CameraConfig | null {
|
||||
return this._allConfigs.get(cameraID) ?? null;
|
||||
}
|
||||
|
||||
public hasCameraID(cameraID: string): boolean {
|
||||
return this._configs.has(cameraID);
|
||||
return this._allConfigs.has(cameraID);
|
||||
}
|
||||
public hasVisibleCameraID(cameraID: string): boolean {
|
||||
return this._visibleConfigs.has(cameraID);
|
||||
}
|
||||
|
||||
public getCameraConfig(cameraID: string): CameraConfig | null {
|
||||
return this._configs.get(cameraID) ?? null;
|
||||
public getCameraCount(): number {
|
||||
return this._allConfigs.size;
|
||||
}
|
||||
public getVisibleCameraCount(): number {
|
||||
return this._visibleConfigs.size;
|
||||
}
|
||||
|
||||
public getCameras(): CameraConfigs {
|
||||
return this._configs;
|
||||
return this._allConfigs;
|
||||
}
|
||||
public getVisibleCameras(): CameraConfigs {
|
||||
return this._visibleConfigs;
|
||||
}
|
||||
|
||||
public getCameraIDs(): Set<string> {
|
||||
return new Set(this._configs.keys());
|
||||
return new Set(this._allConfigs.keys());
|
||||
}
|
||||
public getVisibleCameraIDs(): Set<string> {
|
||||
return new Set(this._visibleConfigs.keys());
|
||||
}
|
||||
|
||||
public getCameraConfigForMedia(media: ViewMedia): CameraConfig | null {
|
||||
|
||||
+87
-18
@@ -384,13 +384,14 @@ class FrigateCard extends LitElement {
|
||||
protected _getMenuButtons(): MenuButton[] {
|
||||
const buttons: MenuButton[] = [];
|
||||
|
||||
const cameras = this._cameraManager?.getCameras();
|
||||
const visibleCameras = this._cameraManager?.getStore().getVisibleCameras();
|
||||
const selectedCameraID = this._view?.camera;
|
||||
const selectedCameraConfig = this._getSelectedCameraConfig();
|
||||
const allSelectedCameraIDs =
|
||||
cameras && selectedCameraID
|
||||
? getAllDependentCameras(cameras, selectedCameraID)
|
||||
: null;
|
||||
const allSelectedCameraIDs = getAllDependentCameras(
|
||||
this._cameraManager,
|
||||
selectedCameraID,
|
||||
);
|
||||
|
||||
const cameraCapabilities = allSelectedCameraIDs
|
||||
? this._cameraManager?.getAggregateCameraCapabilities(allSelectedCameraIDs)
|
||||
: null;
|
||||
@@ -410,8 +411,8 @@ class FrigateCard extends LitElement {
|
||||
) as FrigateCardCustomAction,
|
||||
});
|
||||
|
||||
if (cameras) {
|
||||
const menuItems = Array.from(cameras, ([cameraID, config]) => {
|
||||
if (visibleCameras) {
|
||||
const menuItems = Array.from(visibleCameras, ([cameraID, config]) => {
|
||||
const action = createFrigateCardCustomAction('camera_select', {
|
||||
camera: cameraID,
|
||||
});
|
||||
@@ -439,6 +440,61 @@ class FrigateCard extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
if (selectedCameraID && allSelectedCameraIDs && this._view?.is('live')) {
|
||||
const dependencies = [...allSelectedCameraIDs];
|
||||
const override = this._view?.context?.live?.overrides?.get(selectedCameraID);
|
||||
|
||||
if (dependencies.length === 2) {
|
||||
// If there are only two dependencies (the main camera, and 1 other)
|
||||
// then use a button not a menu to toggle.
|
||||
buttons.push({
|
||||
icon: 'mdi:video-input-component',
|
||||
style:
|
||||
override && override !== selectedCameraID ? this._getEmphasizedStyle() : {},
|
||||
title: localize('config.menu.buttons.substreams'),
|
||||
...this._getConfig().menu.buttons.substreams,
|
||||
type: 'custom:frigate-card-menu-icon',
|
||||
tap_action: createFrigateCardCustomAction('live_substream_select', {
|
||||
camera:
|
||||
override === undefined || override === dependencies[0]
|
||||
? dependencies[1]
|
||||
: dependencies[0],
|
||||
}) as FrigateCardCustomAction,
|
||||
});
|
||||
} else if (dependencies.length > 2) {
|
||||
const menuItems = Array.from(dependencies, (cameraID) => {
|
||||
const action = createFrigateCardCustomAction('live_substream_select', {
|
||||
camera: cameraID,
|
||||
});
|
||||
const metadata = this._hass
|
||||
? this._cameraManager?.getCameraMetadata(this._hass, cameraID) ?? undefined
|
||||
: undefined;
|
||||
const cameraConfig = this._cameraManager?.getStore().getCameraConfig(cameraID);
|
||||
return {
|
||||
enabled: true,
|
||||
icon: metadata?.icon,
|
||||
entity: cameraConfig?.camera_entity,
|
||||
state_color: true,
|
||||
title: metadata?.title,
|
||||
selected:
|
||||
(this._view?.context?.live?.overrides?.get(selectedCameraID) ??
|
||||
selectedCameraID) === cameraID,
|
||||
...(action && { tap_action: action }),
|
||||
};
|
||||
});
|
||||
|
||||
buttons.push({
|
||||
icon: 'mdi:video-input-component',
|
||||
title: localize('config.menu.buttons.substreams'),
|
||||
style:
|
||||
override && override !== selectedCameraID ? this._getEmphasizedStyle() : {},
|
||||
...this._getConfig().menu.buttons.substreams,
|
||||
type: 'custom:frigate-card-menu-submenu',
|
||||
items: menuItems,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
buttons.push({
|
||||
icon: 'mdi:cctv',
|
||||
...this._getConfig().menu.buttons.live,
|
||||
@@ -634,7 +690,7 @@ class FrigateCard extends LitElement {
|
||||
if (!this._view || !this._cameraManager) {
|
||||
return null;
|
||||
}
|
||||
return this._cameraManager.getCameraConfig(this._view.camera);
|
||||
return this._cameraManager.getStore().getCameraConfig(this._view.camera);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -767,7 +823,7 @@ class FrigateCard extends LitElement {
|
||||
protected _changeView(args?: { view?: View; resetMessage?: boolean }): void {
|
||||
log(this._cardWideConfig, `Frigate Card view change: `, args?.view ?? '[default]');
|
||||
const changeView = (view: View): void => {
|
||||
if (View.isMediaChange(this._view, view)) {
|
||||
if (View.isMajorMediaChange(this._view, view)) {
|
||||
this._currentMediaLoadedInfo = null;
|
||||
}
|
||||
if (this._view?.view !== view.view) {
|
||||
@@ -785,7 +841,7 @@ class FrigateCard extends LitElement {
|
||||
// Load the default view.
|
||||
let cameraID: string | null = null;
|
||||
if (this._cameraManager) {
|
||||
const cameras = this._cameraManager.getCameras();
|
||||
const cameras = this._cameraManager.getStore().getVisibleCameras();
|
||||
if (cameras) {
|
||||
if (this._view?.camera && this._getConfig().view.update_cycle_camera) {
|
||||
const keys = Array.from(cameras.keys());
|
||||
@@ -1007,7 +1063,7 @@ class FrigateCard extends LitElement {
|
||||
let changedCamera = false;
|
||||
let triggerChanges = false;
|
||||
|
||||
const cameras = this._cameraManager?.getCameras();
|
||||
const cameras = this._cameraManager?.getStore().getVisibleCameras();
|
||||
for (const [cameraID, config] of cameras?.entries() ?? []) {
|
||||
const triggerEntities = config.triggers.entities ?? [];
|
||||
const diffs = getHassDifferences(this._hass, oldHass, triggerEntities, {
|
||||
@@ -1307,7 +1363,7 @@ class FrigateCard extends LitElement {
|
||||
*/
|
||||
protected _cardActionHandler(ev: CustomEvent<ActionType>): void {
|
||||
const frigateCardAction = convertActionToFrigateCardCustomAction(ev.detail);
|
||||
if (!frigateCardAction) {
|
||||
if (!this._view || !frigateCardAction) {
|
||||
return;
|
||||
}
|
||||
const action = frigateCardAction.frigate_card_action;
|
||||
@@ -1325,14 +1381,12 @@ class FrigateCard extends LitElement {
|
||||
case 'snapshot':
|
||||
case 'snapshots':
|
||||
case 'timeline':
|
||||
if (this._view) {
|
||||
this._changeView({
|
||||
view: new View({
|
||||
view: action,
|
||||
camera: this._view.camera,
|
||||
}),
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'download':
|
||||
this._downloadViewerMedia();
|
||||
@@ -1355,17 +1409,32 @@ class FrigateCard extends LitElement {
|
||||
this._refMenu.value?.toggleMenu();
|
||||
break;
|
||||
case 'camera_select':
|
||||
const cameraID = frigateCardAction.camera;
|
||||
if (this._cameraManager?.hasCameraID(cameraID) && this._view) {
|
||||
const selectCameraID = frigateCardAction.camera;
|
||||
if (
|
||||
this._view &&
|
||||
this._cameraManager?.getStore().hasVisibleCameraID(selectCameraID)
|
||||
) {
|
||||
const viewOnCameraSelect = this._getConfig().view.camera_select;
|
||||
const targetView =
|
||||
viewOnCameraSelect === 'current' ? this._view.view : viewOnCameraSelect;
|
||||
const actualView = this.isViewSupportedByCamera(cameraID, targetView)
|
||||
const actualView = this.isViewSupportedByCamera(selectCameraID, targetView)
|
||||
? targetView
|
||||
: FRIGATE_CARD_VIEW_DEFAULT;
|
||||
this._changeView({ view: new View({ view: actualView, camera: cameraID }) });
|
||||
this._changeView({
|
||||
view: new View({ view: actualView, camera: selectCameraID }),
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'live_substream_select':
|
||||
const overrides: Map<string, string> =
|
||||
this._view.context?.live?.overrides ?? new Map();
|
||||
overrides.set(this._view.camera, frigateCardAction.camera);
|
||||
this._changeView({
|
||||
view: this._view.clone().mergeInContext({
|
||||
live: { overrides: overrides },
|
||||
}),
|
||||
});
|
||||
break;
|
||||
case 'media_player':
|
||||
this._mediaPlayerAction(
|
||||
frigateCardAction.media_player,
|
||||
|
||||
+43
-20
@@ -56,6 +56,18 @@ import { dispatchMessageEvent, dispatchErrorMessageEvent } from '../message.js';
|
||||
import { HassEntity } from 'home-assistant-js-websocket';
|
||||
import { CameraEndpoints } from '../../camera-manager/types.js';
|
||||
|
||||
interface LiveViewContext {
|
||||
// A cameraID override (used for dependencies/substreams to force a different
|
||||
// camera to be live rather than the camera selected in the view).
|
||||
overrides?: Map<string, string>;
|
||||
}
|
||||
|
||||
declare module 'view' {
|
||||
interface ViewContext {
|
||||
live?: LiveViewContext;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state object or dispatch an error. Used in `ha` and `image` live
|
||||
* providers.
|
||||
@@ -339,7 +351,7 @@ export class FrigateCardLiveCarousel extends LitElement {
|
||||
}
|
||||
|
||||
protected _getSelectedCameraIndex(): number {
|
||||
const cameraIDs = this.cameraManager?.getCameraIDs();
|
||||
const cameraIDs = this.cameraManager?.getStore().getVisibleCameraIDs();
|
||||
if (!cameraIDs || !this.view) {
|
||||
return 0;
|
||||
}
|
||||
@@ -362,7 +374,7 @@ export class FrigateCardLiveCarousel extends LitElement {
|
||||
* @returns A list of EmblaOptionsTypes.
|
||||
*/
|
||||
protected _getPlugins(): EmblaCarouselPlugins {
|
||||
const cameras = this.cameraManager?.getCameraIDs();
|
||||
const cameras = this.cameraManager?.getStore().getVisibleCameraIDs();
|
||||
return [
|
||||
// Only enable wheel plugin if there is more than one camera.
|
||||
...(cameras && cameras.size > 1
|
||||
@@ -420,18 +432,27 @@ export class FrigateCardLiveCarousel extends LitElement {
|
||||
* name to slide number.
|
||||
*/
|
||||
protected _getSlides(): [TemplateResult[], Record<string, number>] {
|
||||
const cameras = this.cameraManager?.getCameras();
|
||||
if (!cameras) {
|
||||
const visibleCameras = this.cameraManager?.getStore().getVisibleCameras();
|
||||
if (!visibleCameras) {
|
||||
return [[], {}];
|
||||
}
|
||||
|
||||
const slides: TemplateResult[] = [];
|
||||
const cameraToSlide: Record<string, number> = {};
|
||||
|
||||
for (const [camera, cameraConfig] of cameras) {
|
||||
const slide = this._renderLive(camera, cameraConfig, slides.length);
|
||||
for (const [cameraID, cameraConfig] of visibleCameras) {
|
||||
const liveCameraID =
|
||||
this.view?.context?.live?.overrides?.get(cameraID) ?? cameraID;
|
||||
const liveCameraConfig =
|
||||
cameraID === liveCameraID
|
||||
? cameraConfig
|
||||
: this.cameraManager?.getStore().getCameraConfig(liveCameraID);
|
||||
|
||||
const slide = liveCameraConfig
|
||||
? this._renderLive(liveCameraID, liveCameraConfig, slides.length)
|
||||
: null;
|
||||
if (slide) {
|
||||
cameraToSlide[camera] = slides.length;
|
||||
cameraToSlide[cameraID] = slides.length;
|
||||
slides.push(slide);
|
||||
}
|
||||
}
|
||||
@@ -442,7 +463,7 @@ export class FrigateCardLiveCarousel extends LitElement {
|
||||
* Handle the user selecting a new slide in the carousel.
|
||||
*/
|
||||
protected _setViewHandler(ev: CustomEvent<CarouselSelect>): void {
|
||||
const cameras = this.cameraManager?.getCameras();
|
||||
const cameras = this.cameraManager?.getStore().getVisibleCameras();
|
||||
if (cameras && ev.detail.index !== this._getSelectedCameraIndex()) {
|
||||
this._setViewCameraID(Array.from(cameras.keys())[ev.detail.index]);
|
||||
}
|
||||
@@ -515,7 +536,7 @@ export class FrigateCardLiveCarousel extends LitElement {
|
||||
<frigate-card-live-provider
|
||||
?disabled=${this.liveConfig.lazy_load}
|
||||
.cameraConfig=${cameraConfig}
|
||||
.cameraEndpoints=${guard([this.cameraManager], () =>
|
||||
.cameraEndpoints=${guard([this.cameraManager, cameraID], () =>
|
||||
this.cameraManager?.getCameraEndpoints(cameraID),
|
||||
)}
|
||||
.label=${cameraMetadata?.title ?? ''}
|
||||
@@ -535,7 +556,7 @@ export class FrigateCardLiveCarousel extends LitElement {
|
||||
}
|
||||
|
||||
protected _getCameraIDsOfNeighbors(): [string | null, string | null] {
|
||||
const cameras = this.cameraManager?.getCameras();
|
||||
const cameras = this.cameraManager?.getStore().getVisibleCameras();
|
||||
if (!cameras || !this.view || !this.hass) {
|
||||
return [null, null];
|
||||
}
|
||||
@@ -557,15 +578,13 @@ export class FrigateCardLiveCarousel extends LitElement {
|
||||
* @returns A template to display to the user.
|
||||
*/
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this.liveConfig || !this.view || !this.hass || !this.cameraManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [slides, cameraToSlide] = this._getSlides();
|
||||
this._cameraToSlide = cameraToSlide;
|
||||
if (
|
||||
!slides.length ||
|
||||
!this.liveConfig ||
|
||||
!this.view ||
|
||||
!this.hass ||
|
||||
!this.cameraManager
|
||||
) {
|
||||
if (!slides.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -577,15 +596,19 @@ export class FrigateCardLiveCarousel extends LitElement {
|
||||
|
||||
const [prevID, nextID] = this._getCameraIDsOfNeighbors();
|
||||
|
||||
const overrideCameraID = (cameraID: string): string => {
|
||||
return this.view?.context?.live?.overrides?.get(cameraID) ?? cameraID;
|
||||
};
|
||||
|
||||
const cameraMetadataPrevious = prevID
|
||||
? this.cameraManager.getCameraMetadata(this.hass, prevID)
|
||||
? this.cameraManager.getCameraMetadata(this.hass, overrideCameraID(prevID))
|
||||
: null;
|
||||
const cameraMetadataCurrent = this.cameraManager.getCameraMetadata(
|
||||
this.hass,
|
||||
this.view.camera,
|
||||
overrideCameraID(this.view.camera),
|
||||
);
|
||||
const cameraMetadataNext = nextID
|
||||
? this.cameraManager.getCameraMetadata(this.hass, nextID)
|
||||
? this.cameraManager.getCameraMetadata(this.hass, overrideCameraID(nextID))
|
||||
: null;
|
||||
|
||||
// Notes on the below:
|
||||
|
||||
@@ -31,10 +31,7 @@ import { MediaQueriesClassifier } from '../view/media-queries-classifier';
|
||||
import { View } from '../view/view';
|
||||
import { CameraManager } from '../camera-manager/manager';
|
||||
import { HomeAssistant } from 'custom-card-helpers';
|
||||
import {
|
||||
MediaMetadata,
|
||||
QueryType,
|
||||
} from '../camera-manager/types';
|
||||
import { DataQuery, MediaMetadata, QueryType } from '../camera-manager/types';
|
||||
import format from 'date-fns/format';
|
||||
import endOfMonth from 'date-fns/endOfMonth';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
@@ -168,7 +165,7 @@ class FrigateCardMediaFilter extends ScopedRegistryHost(LitElement) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_ev: CustomEvent<{ value: unknown }>,
|
||||
): Promise<void> {
|
||||
const cameras = this.cameraManager?.getCameras();
|
||||
const cameras = this.cameraManager?.getStore().getVisibleCameras();
|
||||
if (!this.hass || !cameras || !this.cameraManager || !this.view) {
|
||||
return;
|
||||
}
|
||||
@@ -269,7 +266,7 @@ class FrigateCardMediaFilter extends ScopedRegistryHost(LitElement) {
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues): void {
|
||||
if (changedProps.has('cameraManager')) {
|
||||
const cameras = this.cameraManager?.getCameras();
|
||||
const cameras = this.cameraManager?.getStore().getVisibleCameras();
|
||||
if (cameras) {
|
||||
this._cameraOptions = Array.from(cameras.keys()).map((cameraID) => ({
|
||||
value: cameraID,
|
||||
@@ -321,7 +318,7 @@ class FrigateCardMediaFilter extends ScopedRegistryHost(LitElement) {
|
||||
|
||||
protected _getDefaultsFromView(): MediaFilterCoreDefaults | null {
|
||||
const queries = this.view?.query?.getQueries();
|
||||
const cameras = this.cameraManager?.getCameras();
|
||||
const cameras = this.cameraManager?.getStore().getVisibleCameras();
|
||||
if (!this.view || !queries || !cameras) {
|
||||
return null;
|
||||
}
|
||||
@@ -333,12 +330,12 @@ class FrigateCardMediaFilter extends ScopedRegistryHost(LitElement) {
|
||||
let favorite: MediaFilterCoreFavoriteSelection | undefined;
|
||||
|
||||
const cameraIDSets = uniqWith(
|
||||
queries.map((query) => query.cameraIDs),
|
||||
queries.map((query: DataQuery) => query.cameraIDs),
|
||||
isEqual,
|
||||
);
|
||||
// Special note: If all cameras are selected, this is the same as no
|
||||
// Special note: If all visible cameras are selected, this is the same as no
|
||||
// selector at all.
|
||||
if (cameraIDSets.length === 1 && queries[0].cameraIDs.size !== cameras.size) {
|
||||
if (cameraIDSets.length === 1 && isEqual(queries[0].cameraIDs, cameras)) {
|
||||
cameraIDs = [...queries[0].cameraIDs];
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import { ThumbnailCarouselTap } from './thumbnail-carousel.js';
|
||||
import './surround-basic.js';
|
||||
import { changeViewToRecentEventsForCameraAndDependents } from '../utils/media-to-view';
|
||||
import { getAllDependentCameras } from '../utils/camera.js';
|
||||
import type { DataQuery } from '../camera-manager/types';
|
||||
|
||||
interface ThumbnailViewContext {
|
||||
// Whether or not to fetch thumbnails.
|
||||
@@ -120,7 +121,7 @@ export class FrigateCardSurround extends LitElement {
|
||||
// user is scrubbing video).
|
||||
if (
|
||||
changedProperties.has('view') &&
|
||||
View.isMediaChange(changedProperties.get('view'), this.view)
|
||||
View.isMajorMediaChange(changedProperties.get('view'), this.view)
|
||||
) {
|
||||
this._cameraIDsForTimeline = this._getCameraIDsForTimeline() ?? undefined;
|
||||
}
|
||||
@@ -138,18 +139,20 @@ export class FrigateCardSurround extends LitElement {
|
||||
}
|
||||
|
||||
protected _getCameraIDsForTimeline(): Set<string> | null {
|
||||
const cameras = this.cameraManager?.getCameras();
|
||||
if (!this.view || !cameras) {
|
||||
if (!this.view) {
|
||||
return null;
|
||||
}
|
||||
if (this.view?.is('live')) {
|
||||
return getAllDependentCameras(cameras, this.view.camera);
|
||||
return getAllDependentCameras(
|
||||
this.cameraManager,
|
||||
this.view.camera,
|
||||
);
|
||||
}
|
||||
if (this.view.isViewerView()) {
|
||||
return new Set(
|
||||
this.view.query
|
||||
?.getQueries()
|
||||
?.map((query) => [...query.cameraIDs])
|
||||
?.map((query: DataQuery) => [...query.cameraIDs])
|
||||
.flat(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
const item = request.detail.item;
|
||||
const media = this._timelineSource?.dataset.get(item)?.media;
|
||||
const cameraConfig = media
|
||||
? this.cameraManager?.getCameraConfig(media.getCameraID()) ?? undefined
|
||||
? this.cameraManager?.getStore().getCameraConfigForMedia(media) ?? undefined
|
||||
: undefined;
|
||||
|
||||
request.detail.hass = this.hass;
|
||||
@@ -291,15 +291,9 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
* @returns A set of camera ids (may be empty).
|
||||
*/
|
||||
protected _getTimelineCameraIDs(): Set<string> | null {
|
||||
return this.cameraIDs ?? this._getAllCameraIDs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the keys of all cameras.
|
||||
* @returns A set of camera ids (may be empty).
|
||||
*/
|
||||
protected _getAllCameraIDs(): Set<string> | null {
|
||||
return this.cameraManager?.getCameraIDs() ?? null;
|
||||
return (
|
||||
this.cameraIDs ?? this.cameraManager?.getStore().getVisibleCameraIDs() ?? null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1042,11 +1036,7 @@ export class FrigateCardTimelineCore extends LitElement {
|
||||
changedProps.has('cameraIDs')
|
||||
) {
|
||||
const cameraIDs = this._getTimelineCameraIDs();
|
||||
if (
|
||||
cameraIDs &&
|
||||
this.cameraManager &&
|
||||
this.timelineConfig
|
||||
) {
|
||||
if (cameraIDs && this.cameraManager && this.timelineConfig) {
|
||||
this._timelineSource = new TimelineDataSource(
|
||||
this.cameraManager,
|
||||
cameraIDs,
|
||||
|
||||
@@ -13,6 +13,7 @@ export const CONF_CAMERAS_ARRAY_FRIGATE_LABEL =
|
||||
export const CONF_CAMERAS_ARRAY_FRIGATE_URL = `${CONF_CAMERAS}.#.frigate.url` as const;
|
||||
export const CONF_CAMERAS_ARRAY_FRIGATE_ZONE = `${CONF_CAMERAS}.#.frigate.zone` as const;
|
||||
export const CONF_CAMERAS_ARRAY_GO2RTC_MODES = `${CONF_CAMERAS}.#.go2rtc.modes` as const;
|
||||
export const CONF_CAMERAS_ARRAY_HIDE = `${CONF_CAMERAS}.#.hide` as const;
|
||||
export const CONF_CAMERAS_ARRAY_ID = `${CONF_CAMERAS}.#.id` as const;
|
||||
export const CONF_CAMERAS_ARRAY_TITLE = `${CONF_CAMERAS}.#.title` as const;
|
||||
export const CONF_CAMERAS_ARRAY_ICON = `${CONF_CAMERAS}.#.icon` as const;
|
||||
|
||||
@@ -130,6 +130,7 @@ import {
|
||||
CONF_PERFORMANCE_FEATURES_MEDIA_CHUNK_SIZE,
|
||||
MEDIA_CHUNK_SIZE_MAX,
|
||||
CONF_CAMERAS_ARRAY_GO2RTC_MODES,
|
||||
CONF_CAMERAS_ARRAY_HIDE,
|
||||
} from './const.js';
|
||||
import { localize } from './localize/localize.js';
|
||||
import frigate_card_editor_style from './scss/editor.scss';
|
||||
@@ -1341,6 +1342,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
${this._renderStringInput(
|
||||
getArrayConfigPath(CONF_CAMERAS_ARRAY_ID, cameraIndex),
|
||||
)}
|
||||
${this._renderSwitch(
|
||||
getArrayConfigPath(
|
||||
CONF_CAMERAS_ARRAY_HIDE,
|
||||
cameraIndex,
|
||||
),
|
||||
this._defaults.cameras.hide,
|
||||
)}
|
||||
${this._putInSubmenu(
|
||||
MENU_CAMERAS_FRIGATE,
|
||||
cameraIndex,
|
||||
@@ -1604,6 +1612,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
})}
|
||||
${this._renderMenuButton('frigate') /* */}
|
||||
${this._renderMenuButton('cameras') /* */}
|
||||
${this._renderMenuButton('substreams') /* */}
|
||||
${this._renderMenuButton('live') /* */}
|
||||
${this._renderMenuButton('clips') /* */}
|
||||
${this._renderMenuButton('snapshots')}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"mjpeg": "Motion JPEG (MJPEG)"
|
||||
}
|
||||
},
|
||||
"hide": "Hide camera from UI",
|
||||
"icon": "Icon for this camera (Autodetected from entity)",
|
||||
"id": "Unique id for this camera in this card",
|
||||
"live_provider": "Live view provider for this camera",
|
||||
@@ -239,6 +240,7 @@
|
||||
"media_player": "Send to media player",
|
||||
"priority": "Priority",
|
||||
"snapshots": "Snapshots",
|
||||
"substreams": "Substream(s)",
|
||||
"timeline": "Timeline",
|
||||
"recordings": "Recordings"
|
||||
},
|
||||
@@ -368,10 +370,11 @@
|
||||
"live_camera_unavailable": "Camera unavailable",
|
||||
"no_camera_engine": "Could not determine suitable engine for camera",
|
||||
"no_camera_entity": "Could not find camera entity",
|
||||
"no_camera_entity_for_triggers": "A camera entity is required in order to autodetect triggers",
|
||||
"no_camera_id": "Could not determine camera id for the following camera, may need to set 'id' parameter manually",
|
||||
"no_camera_name": "Could not determine a Frigate camera name for camera (or one of its dependents), please specify either 'camera_entity' or 'camera_name'",
|
||||
"no_cameras": "No valid cameras found, you must configure at least one camera entry",
|
||||
"no_live_camera": "The camera_entity parameter must be set and valid for this live provider",
|
||||
"no_visible_cameras": "No visible cameras found, you must configure at least one non-hidden camera",
|
||||
"reconnecting": "Reconnecting",
|
||||
"timeline_no_cameras": "No Frigate cameras to show in timeline",
|
||||
"troubleshooting": "Check troubleshooting",
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"url": "Frigate URL del server",
|
||||
"zone": "Frigate zona"
|
||||
},
|
||||
"hide": "",
|
||||
"icon": "Icona per questa telecamera (Autoidentificato dall'entità)",
|
||||
"id": "ID univoco per questa telecamera in questa carta",
|
||||
"live_provider": "Provider di visualizzazione dal vivo per questa telecamera",
|
||||
@@ -223,6 +224,7 @@
|
||||
"live": "Abitare",
|
||||
"media_player": "Invia a Media Player",
|
||||
"priority": "Priorità",
|
||||
"substreams": "",
|
||||
"snapshots": "Istantanee",
|
||||
"timeline": "Timeline"
|
||||
},
|
||||
@@ -347,10 +349,11 @@
|
||||
"live_camera_unavailable": "Telecamera non disponibile",
|
||||
"no_camera_engine": "",
|
||||
"no_camera_entity": "",
|
||||
"no_camera_entity_for_triggers": "",
|
||||
"no_camera_id": "Impossibile determinare l'ID della telecamera , potrebbe essere necessario impostare manualmente il parametro 'ID'",
|
||||
"no_camera_name": "Impossibile determinare un nome della telecamera in Frigate, si prega di specificare 'camera_enty' o 'camera_name'",
|
||||
"no_cameras": "Nessuna telecamera valida trovata, è necessario configurare almeno una voce della telecamera",
|
||||
"no_live_camera": "Il parametro fotocamera_enty deve essere impostato e valido per questo provider live",
|
||||
"no_visible_cameras": "",
|
||||
"reconnecting": "Riconnessione",
|
||||
"timeline_no_cameras": "Nessuna telecamera damostrare in Frigate nella timeline",
|
||||
"troubleshooting": "Controllare la risoluzione dei problemi",
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"url": "URL do servidor Frigate",
|
||||
"zone": "Zona do Frigate"
|
||||
},
|
||||
"hide": "",
|
||||
"icon": "Ícone para esta câmera (detectado automaticamente pela entidade)",
|
||||
"id": "ID exclusivo para esta câmera nesse cartão",
|
||||
"live_provider": "Provedor de visualização ao vivo para esta câmera",
|
||||
@@ -223,6 +224,7 @@
|
||||
"live": "Ao vivo",
|
||||
"media_player": "Enviar para o reprodutor de mídia",
|
||||
"priority": "Prioridade",
|
||||
"substreams": "",
|
||||
"snapshots": "Instantâneos",
|
||||
"timeline": "Linha do tempo"
|
||||
},
|
||||
@@ -347,10 +349,11 @@
|
||||
"live_camera_unavailable": "",
|
||||
"no_camera_engine": "",
|
||||
"no_camera_entity": "",
|
||||
"no_camera_entity_for_triggers": "",
|
||||
"no_camera_id": "Não foi possível determinar o ID da câmera para a câmera a seguir, pode ser necessário definir o parâmetro 'id' manualmente",
|
||||
"no_camera_name": "Não foi possível determinar o nome da câmera da Frigate, especifique 'camera_entity' ou 'camera_name' para a câmera a seguir",
|
||||
"no_cameras": "Nenhuma câmera válida encontrada, você deve configurar pelo menos uma câmera",
|
||||
"no_live_camera": "O parâmetro camera_entity deve ser definido e válido para este provedor ativo",
|
||||
"no_visible_cameras": "",
|
||||
"reconnecting": "Reconectando",
|
||||
"timeline_no_cameras": "Nenhuma câmera do Frigate para mostrar na linha do tempo",
|
||||
"troubleshooting": "Verifique a solução de problemas",
|
||||
|
||||
+17
-4
@@ -199,7 +199,7 @@ const noActionSchema = schemaForType<
|
||||
}),
|
||||
);
|
||||
|
||||
const frigateCardCustomactionsBaseSchema = customActionSchema.extend({
|
||||
const frigateCardCustomActionsBaseSchema = customActionSchema.extend({
|
||||
action: z
|
||||
.literal('custom:frigate-card-action')
|
||||
// Syntactic sugar to avoid 'fire-dom-event' as part of an external API.
|
||||
@@ -227,18 +227,24 @@ const FRIGATE_CARD_GENERAL_ACTIONS = [
|
||||
const FRIGATE_CARD_ACTIONS = [
|
||||
...FRIGATE_CARD_GENERAL_ACTIONS,
|
||||
'camera_select',
|
||||
'live_substream_select',
|
||||
'media_player',
|
||||
] as const;
|
||||
export type FrigateCardAction = (typeof FRIGATE_CARD_ACTIONS)[number];
|
||||
|
||||
const frigateCardGeneralActionSchema = frigateCardCustomactionsBaseSchema.extend({
|
||||
const frigateCardGeneralActionSchema = frigateCardCustomActionsBaseSchema.extend({
|
||||
frigate_card_action: z.enum(FRIGATE_CARD_GENERAL_ACTIONS),
|
||||
});
|
||||
const frigateCardCameraSelectActionSchema = frigateCardCustomactionsBaseSchema.extend({
|
||||
const frigateCardCameraSelectActionSchema = frigateCardCustomActionsBaseSchema.extend({
|
||||
frigate_card_action: z.literal('camera_select'),
|
||||
camera: z.string(),
|
||||
});
|
||||
const frigateCarMediaPlayerActionSchema = frigateCardCustomactionsBaseSchema.extend({
|
||||
const frigateCardLiveDependencySelectActionSchema =
|
||||
frigateCardCustomActionsBaseSchema.extend({
|
||||
frigate_card_action: z.literal('live_substream_select'),
|
||||
camera: z.string(),
|
||||
});
|
||||
const frigateCarMediaPlayerActionSchema = frigateCardCustomActionsBaseSchema.extend({
|
||||
frigate_card_action: z.literal('media_player'),
|
||||
media_player: z.string(),
|
||||
media_player_action: z.enum(['play', 'stop']),
|
||||
@@ -247,6 +253,7 @@ const frigateCarMediaPlayerActionSchema = frigateCardCustomactionsBaseSchema.ext
|
||||
export const frigateCardCustomActionSchema = z.union([
|
||||
frigateCardGeneralActionSchema,
|
||||
frigateCardCameraSelectActionSchema,
|
||||
frigateCardLiveDependencySelectActionSchema,
|
||||
frigateCarMediaPlayerActionSchema,
|
||||
]);
|
||||
export type FrigateCardCustomAction = z.infer<typeof frigateCardCustomActionSchema>;
|
||||
@@ -398,6 +405,7 @@ const cameraConfigDefault = {
|
||||
all_cameras: false,
|
||||
cameras: [],
|
||||
},
|
||||
hide: false,
|
||||
triggers: {
|
||||
motion: false,
|
||||
occupancy: true,
|
||||
@@ -418,6 +426,9 @@ const cameraConfigSchema = z
|
||||
icon: z.string().optional(),
|
||||
title: z.string().optional(),
|
||||
|
||||
// Used to hide the camera (e.g. when used only as a dependency).
|
||||
hide: z.boolean().optional(),
|
||||
|
||||
// Optional identifier to separate different camera configurations used in
|
||||
// this card.
|
||||
id: z.string().optional(),
|
||||
@@ -944,6 +955,7 @@ const menuConfigDefault = {
|
||||
buttons: {
|
||||
frigate: visibleButtonDefault,
|
||||
cameras: visibleButtonDefault,
|
||||
substreams: visibleButtonDefault,
|
||||
live: visibleButtonDefault,
|
||||
clips: visibleButtonDefault,
|
||||
snapshots: visibleButtonDefault,
|
||||
@@ -976,6 +988,7 @@ const menuConfigSchema = z
|
||||
.object({
|
||||
frigate: visibleButtonSchema.default(menuConfigDefault.buttons.frigate),
|
||||
cameras: visibleButtonSchema.default(menuConfigDefault.buttons.cameras),
|
||||
substreams: visibleButtonSchema.default(menuConfigDefault.buttons.substreams),
|
||||
live: visibleButtonSchema.default(menuConfigDefault.buttons.live),
|
||||
clips: visibleButtonSchema.default(menuConfigDefault.buttons.clips),
|
||||
snapshots: visibleButtonSchema.default(menuConfigDefault.buttons.snapshots),
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@ import {
|
||||
ActionConfig,
|
||||
handleActionConfig,
|
||||
hasAction,
|
||||
HomeAssistant
|
||||
HomeAssistant,
|
||||
} from 'custom-card-helpers';
|
||||
import {
|
||||
Actions,
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
ActionType,
|
||||
FrigateCardAction,
|
||||
FrigateCardCustomAction,
|
||||
frigateCardCustomActionSchema
|
||||
frigateCardCustomActionSchema,
|
||||
} from '../types.js';
|
||||
|
||||
/**
|
||||
@@ -43,7 +43,7 @@ export function createFrigateCardCustomAction(
|
||||
media_player_action?: 'play' | 'stop';
|
||||
},
|
||||
): FrigateCardCustomAction | null {
|
||||
if (action === 'camera_select') {
|
||||
if (action === 'camera_select' || action === 'live_substream_select') {
|
||||
if (!args?.camera) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+10
-4
@@ -1,3 +1,4 @@
|
||||
import { CameraManager } from '../camera-manager/manager.js';
|
||||
import { CameraConfig, RawFrigateCardConfig } from '../types.js';
|
||||
|
||||
/**
|
||||
@@ -25,14 +26,19 @@ export function getCameraID(
|
||||
|
||||
/**
|
||||
* Get all cameras that depend on a given camera.
|
||||
* @param cameras Cameras map.
|
||||
* @param cameraManager The camera manager.
|
||||
* @param cameraID ID of the target camera.
|
||||
* @returns A set of query parameters.
|
||||
* @returns A set of dependent cameraIDs or null.
|
||||
*/
|
||||
export const getAllDependentCameras = (
|
||||
cameras: Map<string, CameraConfig>,
|
||||
cameraManager?: CameraManager,
|
||||
cameraID?: string,
|
||||
): Set<string> => {
|
||||
): Set<string> | null => {
|
||||
if (!cameraManager || !cameraID) {
|
||||
return null;
|
||||
}
|
||||
const cameras = cameraManager.getStore().getCameras();
|
||||
|
||||
const cameraIDs: Set<string> = new Set();
|
||||
const getDependentCameras = (cameraID: string): void => {
|
||||
const cameraConfig = cameras.get(cameraID);
|
||||
|
||||
@@ -53,7 +53,7 @@ export class EntityRegistryManager {
|
||||
public async getExtendedEntity(
|
||||
hass: HomeAssistant,
|
||||
entityID: string,
|
||||
): Promise<ExtendedEntity | null> {
|
||||
): Promise<ExtendedEntity> {
|
||||
const cachedValue = this._extendedCache.get(entityID);
|
||||
if (cachedValue) {
|
||||
return cachedValue;
|
||||
|
||||
@@ -27,11 +27,11 @@ export const changeViewToRecentEventsForCameraAndDependents = async (
|
||||
targetView?: FrigateCardView;
|
||||
},
|
||||
): Promise<void> => {
|
||||
const cameras = cameraManager.getCameras();
|
||||
if (!cameras) {
|
||||
const cameraIDs = getAllDependentCameras(cameraManager, view.camera);
|
||||
if (!cameraIDs) {
|
||||
return;
|
||||
}
|
||||
const cameraIDs = new Set(getAllDependentCameras(cameras, view.camera));
|
||||
|
||||
const queries = createQueriesForEventsView(cameraManager, cardWideConfig, cameraIDs, {
|
||||
mediaType: options?.mediaType,
|
||||
});
|
||||
@@ -83,18 +83,16 @@ export const changeViewToRecentRecordingForCameraAndDependents = async (
|
||||
targetView?: 'recording' | 'recordings';
|
||||
},
|
||||
): Promise<void> => {
|
||||
const cameras = cameraManager.getCameras();
|
||||
if (!cameras) {
|
||||
const cameraIDs = getAllDependentCameras(cameraManager, view.camera);
|
||||
if (!cameraIDs) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cameraIDs = new Set(getAllDependentCameras(cameras, view.camera));
|
||||
const queries = createQueriesForRecordingsView(
|
||||
cameraManager,
|
||||
cardWideConfig,
|
||||
cameraIDs,
|
||||
);
|
||||
|
||||
if (!queries) {
|
||||
return;
|
||||
}
|
||||
|
||||
+6
-1
@@ -39,12 +39,17 @@ export class View {
|
||||
* @param curr The current view.
|
||||
* @returns True if the view change is a real media change.
|
||||
*/
|
||||
public static isMediaChange(prev?: View, curr?: View): boolean {
|
||||
public static isMajorMediaChange(prev?: View, curr?: View): boolean {
|
||||
return (
|
||||
!prev ||
|
||||
!curr ||
|
||||
prev.view !== curr.view ||
|
||||
prev.camera !== curr.camera ||
|
||||
// When in live mode, take overrides into account in deciding if this is a
|
||||
// major media change.
|
||||
(curr.view === 'live' &&
|
||||
prev.context?.live?.overrides?.get(prev.camera) !==
|
||||
curr.context?.live?.overrides?.get(curr.camera)) ||
|
||||
// When in the live view, the queryResults contain the events that
|
||||
// happened in the past -- not reflective of the actual live media viewer
|
||||
// the user is seeing.
|
||||
|
||||
Reference in New Issue
Block a user