Repository: dermotduffy/frigate-hass-card
Pull request #1823 (2025-01-11 23:27:58)

fix: Don't hide PTZ control menu button unnecessarily
 - Closes: #1819


Patch:
diff --git a/docs/configuration/live.md b/docs/configuration/live.md
index c3bc2843..f26e28f1 100644
--- a/docs/configuration/live.md
+++ b/docs/configuration/live.md
@@ -74,7 +74,7 @@ live:
 | `hide_home`     | `false`        | When `true` the Home button of the control is hidden                                                                                                                                                                            |
 | `hide_pan_tilt` | `false`        | When `true` the Pan & Tilt buttons of the control is hidden                                                                                                                                                                     |
 | `hide_zoom`     | `false`        | When `true` the Zoom button of the control is hidden                                                                                                                                                                            |
-| `mode`          | `auto`         | If `on` or `off` will always or never show PTZ controls respectively, if `auto` will show PTZ controls only if the camera supports real PTZ.                                                                                    |
+| `mode`          | `auto`         | If `on` or `off`, by default will always or never show PTZ controls respectively, if `auto` will show PTZ controls only if the camera supports real PTZ.                                                                        |
 | `orientation`   | `horizontal`   | Whether to show a `vertical` or `horizontal` PTZ control.                                                                                                                                                                       |
 | `position`      | `bottom-right` | Whether to position the control on the `top-left`, `top-right`, `bottom-left` or `bottom-right`. This may be overridden by using the `style` parameter to precisely control placement.                                          |
 | `style`         |                | Optionally position and style the element using CSS. Similar to [Picture Element styling](https://www.home-assistant.io/dashboards/picture-elements/#how-to-use-the-style-object), except without any default, e.g. `left: 42%` |
diff --git a/src/components-lib/menu-button-controller.ts b/src/components-lib/menu-button-controller.ts
index d7125df0..557cc5e2 100644
--- a/src/components-lib/menu-button-controller.ts
+++ b/src/components-lib/menu-button-controller.ts
@@ -582,20 +582,20 @@ export class MenuButtonController {
         ? config.media_viewer.controls.ptz
         : null;
 
-    if (!view || !ptzConfig || ptzConfig.mode === 'off') {
+    if (!view || !ptzConfig) {
       return null;
     }
 
     const ptzTarget = getPTZTarget(view, {
       cameraManager: cameraManager,
-      ...(ptzConfig.mode === 'auto' && { type: 'ptz' }),
     });
 
     if (ptzTarget) {
       const isOn =
-        view.context?.ptzControls?.enabled !== false &&
-        (ptzConfig.mode === 'on' ||
-          (ptzConfig.mode === 'auto' && ptzTarget.type === 'ptz'));
+        view.context?.ptzControls?.enabled !== undefined
+          ? view.context.ptzControls.enabled
+          : ptzConfig.mode === 'on' ||
+            (ptzConfig.mode === 'auto' && ptzTarget.type === 'ptz');
       return {
         icon: 'mdi:pan',
         ...config.menu.buttons.ptz_controls,


Test patch:
diff --git a/tests/components-lib/menu-button-controller.test.ts b/tests/components-lib/menu-button-controller.test.ts
index 0fba379d..4192f70d 100644
--- a/tests/components-lib/menu-button-controller.test.ts
+++ b/tests/components-lib/menu-button-controller.test.ts
@@ -1428,16 +1428,17 @@ describe('MenuButtonController', () => {
   });
 
   describe('should have show ptz button', () => {
-    it('when the selected camera is not PTZ enabled', () => {
+    it('should not show when not in live view', () => {
       const store = createStore([
         {
           cameraID: 'camera-1',
+          capabilities: new Capabilities({ ptz: { left: ['relative'] } }),
         },
       ]);
 
       const buttons = calculateButtons(controller, {
         cameraManager: createCameraManager(store),
-        view: createView({ view: 'live' }),
+        view: createView({ view: 'clips' }),
       });
 
       expect(buttons).not.toContainEqual(
@@ -1447,7 +1448,7 @@ describe('MenuButtonController', () => {
       );
     });
 
-    it('when not in live view', () => {
+    it('should show when in live view', () => {
       const store = createStore([
         {
           cameraID: 'camera-1',
@@ -1457,17 +1458,17 @@ describe('MenuButtonController', () => {
 
       const buttons = calculateButtons(controller, {
         cameraManager: createCameraManager(store),
-        view: createView({ view: 'clips' }),
+        view: createView({ view: 'live' }),
       });
 
-      expect(buttons).not.toContainEqual(
+      expect(buttons).toContainEqual(
         expect.objectContaining({
           title: 'Show PTZ controls',
         }),
       );
     });
 
-    it('when the selected camera is PTZ enabled', () => {
+    it('should show when the context has PTZ enabled', () => {
       const store = createStore([
         {
           cameraID: 'camera-1',
@@ -1475,8 +1476,13 @@ describe('MenuButtonController', () => {
         },
       ]);
 
+      const view = createView({
+        camera: 'camera-1',
+        context: { ptzControls: { enabled: true } },
+      });
       const buttons = calculateButtons(controller, {
         cameraManager: createCameraManager(store),
+        view: view,
       });
 
       expect(buttons).toContainEqual({
@@ -1496,7 +1502,7 @@ describe('MenuButtonController', () => {
       });
     });
 
-    it('when the context has PTZ disabled', () => {
+    it('should show when the context has PTZ disabled', () => {
       const store = createStore([
         {
           cameraID: 'camera-1',
@@ -1528,7 +1534,41 @@ describe('MenuButtonController', () => {
       });
     });
 
-    it('when a substream is PTZ enabled', () => {
+    it('should detect current status without context in auto mode', () => {
+      const store = createStore([
+        {
+          cameraID: 'camera-1',
+          capabilities: new Capabilities({ ptz: { left: ['relative'] } }),
+        },
+      ]);
+
+      const view = createView({
+        camera: 'camera-1',
+      });
+      const buttons = calculateButtons(controller, {
+        cameraManager: createCameraManager(store),
+        config: createConfig({ live: { controls: { ptz: { mode: 'auto' } } } }),
+        view: view,
+      });
+
+      expect(buttons).toContainEqual({
+        enabled: false,
+        icon: 'mdi:pan',
+        priority: 50,
+        style: {
+          color: 'var(--frigate-card-menu-button-active-color)',
+        },
+        tap_action: {
+          action: 'fire-dom-event',
+          frigate_card_action: 'ptz_controls',
+          enabled: false,
+        },
+        title: 'Show PTZ controls',
+        type: 'custom:frigate-card-menu-icon',
+      });
+    });
+
+    it('should show when a substream is PTZ enabled', () => {
       const store = createStore([
         {
           cameraID: 'camera-1',