Always render a view even if default is unsupported.
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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', () => {
|
||||||
|
|||||||
@@ -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],
|
||||||
|
|||||||
Reference in New Issue
Block a user