Always render a view even if default is unsupported.

This commit is contained in:
Dermot Duffy
2024-03-08 20:00:35 -08:00
parent e261c17f75
commit ecdd02c7ce
7 changed files with 98 additions and 7 deletions
+1 -1
View File
@@ -128,7 +128,7 @@ export class FrigateCamera extends Camera {
hass: HomeAssistant, hass: HomeAssistant,
cameraConfig: CameraConfig, cameraConfig: CameraConfig,
): Promise<PTZCapabilities | null> { ): Promise<PTZCapabilities | null> {
if (!cameraConfig.frigate.camera_name) { if (!cameraConfig.frigate.camera_name || isBirdseye(cameraConfig)) {
return null; return null;
} }
+5
View File
@@ -24,6 +24,7 @@ export interface CameraManagerReadOnlyConfigStore {
getCameraIDs(): Set<string>; getCameraIDs(): Set<string>;
getVisibleCameraIDs(): Set<string>; getVisibleCameraIDs(): Set<string>;
getDefaultCameraID(): string | null;
getAllDependentCameras(cameraID: string): Set<string>; getAllDependentCameras(cameraID: string): Set<string>;
} }
@@ -61,6 +62,10 @@ export class CameraManagerStore implements CameraManagerReadOnlyConfigStore {
return this.getVisibleCameraIDs().size; return this.getVisibleCameraIDs().size;
} }
public getDefaultCameraID(): string | null {
return this._cameras.keys().next().value ?? null;
}
public getCameras(): Map<string, Camera> { public getCameras(): Map<string, Camera> {
return this._cameras; return this._cameras;
} }
@@ -98,7 +98,7 @@ export class InitializationManager {
if (hasViewRelatedActions) { if (hasViewRelatedActions) {
this._api.getQueryStringManager().executeViewRelated(); this._api.getQueryStringManager().executeViewRelated();
} else { } else {
this._api.getViewManager().setViewDefault(); this._api.getViewManager().setViewDefault({ failSafe: true });
} }
} }
+22 -4
View File
@@ -1,13 +1,23 @@
import { ViewContext } from 'view'; import { ViewContext } from 'view';
import { FrigateCardConfig, FrigateCardView, ViewDisplayMode } from '../config/types'; import {
import { View } from '../view/view'; FRIGATE_CARD_VIEW_DEFAULT,
FrigateCardConfig,
FrigateCardView,
ViewDisplayMode,
} from '../config/types';
import { log } from '../utils/debug'; import { log } from '../utils/debug';
import { executeMediaQueryForView } from '../utils/media-to-view'; import { executeMediaQueryForView } from '../utils/media-to-view';
import { View } from '../view/view';
import { CardViewAPI } from './types'; import { CardViewAPI } from './types';
interface ViewManagerSetViewDefaultParameters { interface ViewManagerSetViewDefaultParameters {
cameraID?: string; cameraID?: string;
substream?: string; substream?: string;
// When failSafe is true, the view will be changed to an "always-works" view
// (e.g. `live`) if the proposed view is unsupported. By default the view will
// just not be changed.
failSafe?: boolean;
} }
export interface ViewManagerSetViewParameters export interface ViewManagerSetViewParameters
@@ -75,8 +85,16 @@ export class ViewManager {
cameraID = cameras.keys().next().value; cameraID = cameras.keys().next().value;
} }
} }
const viewName = params?.viewName ?? this._view?.view ?? config.view.default; let viewName = params?.viewName ?? this._view?.view ?? config.view.default;
if (cameraID && viewName && this.isViewSupportedByCamera(cameraID, viewName)) { if (cameraID && viewName) {
if (!this.isViewSupportedByCamera(cameraID, viewName)) {
if (params.failSafe) {
viewName = FRIGATE_CARD_VIEW_DEFAULT;
} else {
return;
}
}
const displayMode = const displayMode =
this._view?.displayMode ?? this._view?.displayMode ??
this._getDefaultDisplayModeForView(viewName, config); this._getDefaultDisplayModeForView(viewName, config);
+7 -1
View File
@@ -143,13 +143,18 @@ describe('FrigateCamera', () => {
it('basic non-birdseye', async () => { it('basic non-birdseye', async () => {
const camera = new FrigateCamera( const camera = new FrigateCamera(
createCameraConfig(), createCameraConfig({
frigate: {
camera_name: 'front_door',
},
}),
mock<CameraManagerEngine>(), mock<CameraManagerEngine>(),
); );
await camera.initialize(createHASS(), mock<EntityRegistryManager>()); await camera.initialize(createHASS(), mock<EntityRegistryManager>());
expect(camera.getCapabilities()).toEqual(nonBirdseyeBaseCapabilities); expect(camera.getCapabilities()).toEqual(nonBirdseyeBaseCapabilities);
expect(vi.mocked(getPTZInfo)).toBeCalled();
}); });
it('basic birdseye', async () => { it('basic birdseye', async () => {
@@ -173,6 +178,7 @@ describe('FrigateCamera', () => {
supportsRecordings: false, supportsRecordings: false,
supportsTimeline: false, supportsTimeline: false,
}); });
expect(vi.mocked(getPTZInfo)).not.toBeCalled();
}); });
describe('with ptz', () => { describe('with ptz', () => {
+11
View File
@@ -61,6 +61,17 @@ describe('CameraManagerStore', async () => {
expect(store.getVisibleCameraCount()).toBe(1); expect(store.getVisibleCameraCount()).toBe(1);
}); });
describe('getDefaultCameraID', () => {
it('with camera', async () => {
const store = setupStore();
expect(store.getDefaultCameraID()).toBe('camera-visible');
});
it('without camera', async () => {
const store = new CameraManagerStore();
expect(store.getDefaultCameraID()).toBeNull();
});
});
describe('getCamera', async () => { describe('getCamera', async () => {
it('present', async () => { it('present', async () => {
const store = setupStore(); const store = setupStore();
@@ -324,6 +324,57 @@ describe('ViewManager.setViewByParameters', () => {
expect(manager.getView()).toBeNull(); expect(manager.getView()).toBeNull();
}); });
describe('should handle unsupported view', () => {
it('without failsafe', () => {
const api = createCardAPI();
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager());
vi.mocked(api.getCameraManager().getStore).mockReturnValue(
createStore([
{
cameraID: 'camera.kitchen',
},
]),
);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
vi.mocked(api.getCameraManager().getCameraCapabilities).mockReturnValue(createCameraCapabilities({
supportsSnapshots: false,
}));
const manager = new ViewManager(api);
manager.setViewByParameters({
viewName: 'snapshots',
});
expect(manager.hasView()).toBeFalsy();
expect(manager.getView()).toBeNull();
});
it('with failsafe', () => {
const api = createCardAPI();
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager());
vi.mocked(api.getCameraManager().getStore).mockReturnValue(
createStore([
{
cameraID: 'camera.kitchen',
},
]),
);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
vi.mocked(api.getCameraManager().getCameraCapabilities).mockReturnValue(createCameraCapabilities({
supportsSnapshots: false,
}));
const manager = new ViewManager(api);
manager.setViewByParameters({
viewName: 'snapshots',
failSafe: true,
});
expect(manager.hasView()).toBeTruthy();
expect(manager.getView()?.view).toBe('live');
});
});
describe('should set view by parameters and respect display mode in config for view', () => { describe('should set view by parameters and respect display mode in config for view', () => {
it.each([ it.each([
['media' as const], ['media' as const],