Repository: dermotduffy/frigate-hass-card
Pull request #1571 (2024-09-22 22:57:13)

fix: Untriggering to default should reset to default camera


Patch:
diff --git a/src/card-controller/view/factory.ts b/src/card-controller/view/factory.ts
index 0327a7e0..69a8fadc 100644
--- a/src/card-controller/view/factory.ts
+++ b/src/card-controller/view/factory.ts
@@ -35,27 +35,37 @@ export class ViewFactory {
       return null;
     }
 
-    let forceCameraID: string | null = options?.params?.camera ?? null;
+    // Neither options.baseView.camera nor options.baseView.view are respected
+    // here, since this is the default view / camera.
+    // See: https://github.com/dermotduffy/frigate-hass-card/issues/1564
+
+    let cameraID: string | null = null;
     const viewName = options?.params?.view ?? config.view.default;
 
-    if (
-      !forceCameraID &&
-      options?.baseView?.camera &&
-      config.view.default_cycle_camera
-    ) {
+    if (options?.params?.camera) {
+      cameraID = options.params.camera;
+    } else {
       const cameraIDs = [
         ...getCameraIDsForViewName(this._api.getCameraManager(), viewName),
       ];
-      const currentIndex = cameraIDs.indexOf(options?.baseView?.camera);
-      const targetIndex = currentIndex + 1 >= cameraIDs.length ? 0 : currentIndex + 1;
-      forceCameraID = cameraIDs[targetIndex];
+      if (!cameraIDs.length) {
+        return null;
+      }
+
+      if (options?.baseView?.camera && config.view.default_cycle_camera) {
+        const currentIndex = cameraIDs.indexOf(options.baseView.camera);
+        const targetIndex = currentIndex + 1 >= cameraIDs.length ? 0 : currentIndex + 1;
+        cameraID = cameraIDs[targetIndex];
+      } else {
+        cameraID = cameraIDs[0];
+      }
     }
 
     return this.getViewByParameters({
       params: {
         ...options?.params,
         view: viewName,
-        ...(forceCameraID && { camera: forceCameraID }),
+        camera: cameraID,
       },
       baseView: options?.baseView,
     });


Test patch:
diff --git a/tests/card-controller/view/factory.test.ts b/tests/card-controller/view/factory.test.ts
index c0fcae8c..6a293be7 100644
--- a/tests/card-controller/view/factory.test.ts
+++ b/tests/card-controller/view/factory.test.ts
@@ -27,6 +27,33 @@ describe('getViewDefault', () => {
     expect(factory.getViewDefault()).toBeNull();
   });
 
+  it('should return null if no cameras support view', () => {
+    const api = createCardAPI();
+    vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
+    vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager());
+
+    // No cameras support live.
+    vi.mocked(api.getCameraManager().getStore).mockReturnValue(
+      createStore([
+        {
+          cameraID: 'camera.office',
+          capabilities: createCapabilities({
+            live: false,
+          }),
+        },
+        {
+          cameraID: 'camera.kitchen',
+          capabilities: createCapabilities({
+            live: false,
+          }),
+        },
+      ]),
+    );
+
+    const factory = new ViewFactory(api);
+    expect(factory.getViewDefault()).toBeNull();
+  });
+
   it('should create view', () => {
     const factory = new ViewFactory(createPopulatedAPI());
     const view = factory.getViewDefault();
@@ -64,16 +91,30 @@ describe('getViewDefault', () => {
     expect(view?.camera).toBe('camera.office');
   });
 
+  it('should use default camera when camera unspecified', () => {
+    // Even though baseView has a camera, since default is called it should
+    // use that camera.
+
+    const factory = new ViewFactory(createPopulatedAPI());
+    const baseView = createView({ camera: 'camera.kitchen' });
+    const view = factory.getViewDefault({
+      baseView: baseView,
+    });
+
+    expect(view?.is('live')).toBeTruthy();
+    expect(view?.camera).toBe('camera.office');
+  });
+
   it('should respect parameters', () => {
     const factory = new ViewFactory(createPopulatedAPI());
     const view = factory.getViewDefault({
       params: {
-        camera: 'camera.office',
+        camera: 'camera.kitchen',
       },
     });
 
     expect(view?.is('live')).toBeTruthy();
-    expect(view?.camera).toBe('camera.office');
+    expect(view?.camera).toBe('camera.kitchen');
   });
 });