diff --git a/src/card-controller/card-element-manager.ts b/src/card-controller/card-element-manager.ts index 14aa2c48..f8587919 100644 --- a/src/card-controller/card-element-manager.ts +++ b/src/card-controller/card-element-manager.ts @@ -79,6 +79,10 @@ export class CardElementManager { 'mousemove', this._api.getInteractionManager().reportInteraction, ); + this._element.addEventListener( + 'wheel', + this._api.getInteractionManager().reportInteraction, + ); this._element.addEventListener( 'll-custom', this._api.getActionsManager().handleCustomActionEvent, @@ -129,11 +133,15 @@ export class CardElementManager { // Uninitialize cameras to cause them to reinitialize on // reconnection, to ensure the state subscription/unsubscription works // correctly for triggers. - this._api.getInitializationManager().uninitialize(InitializationAspect.CAMERAS), - this._element.removeEventListener( - 'mousemove', - this._api.getInteractionManager().reportInteraction, - ); + this._api.getInitializationManager().uninitialize(InitializationAspect.CAMERAS); + this._element.removeEventListener( + 'mousemove', + this._api.getInteractionManager().reportInteraction, + ); + this._element.removeEventListener( + 'wheel', + this._api.getInteractionManager().reportInteraction, + ); this._element.removeEventListener( 'll-custom', this._api.getActionsManager().handleCustomActionEvent, diff --git a/src/card-controller/default-manager.ts b/src/card-controller/default-manager.ts index 6d782b0e..88d10027 100644 --- a/src/card-controller/default-manager.ts +++ b/src/card-controller/default-manager.ts @@ -3,6 +3,7 @@ import { DestroyCallback, subscribeToTrigger } from '../utils/ha'; import { isActionAllowedBasedOnInteractionState } from '../utils/interaction-mode'; import { Timer } from '../utils/timer'; import { CardDefaultManagerAPI } from './types'; +import { createGeneralAction } from '../utils/action'; /** * Manages automated resetting to the default view. @@ -25,6 +26,22 @@ export class DefaultManager { public async initialize(): Promise { const result = await this._initializationLimit.add(() => this._reconfigure()); this._startTimer(); + + if (this._api.getConfigManager().getConfig()?.view.default_reset.after_interaction) { + this._api.getAutomationsManager().addAutomations([ + { + actions: [createGeneralAction('default')], + conditions: [ + { + condition: 'interaction' as const, + interaction: false, + }, + ], + tag: this, + }, + ]); + } + return !!result; } @@ -32,6 +49,7 @@ export class DefaultManager { this._timer.stop(); this._unsubscribeCallback?.(); this._unsubscribeCallback = null; + this._api.getAutomationsManager().deleteAutomations(this); } protected async _reconfigure(): Promise { diff --git a/src/card-controller/interaction-manager.ts b/src/card-controller/interaction-manager.ts index 350a6fcb..287fe4ca 100644 --- a/src/card-controller/interaction-manager.ts +++ b/src/card-controller/interaction-manager.ts @@ -39,16 +39,7 @@ export class InteractionManager { this._timer.start(timeoutSeconds, () => { this._api.getConditionsManager().setState({ interaction: false }); - - if (!this._api.getTriggersManager().isTriggered()) { - if ( - this._api.getConfigManager().getConfig()?.view.default_reset - .after_interaction - ) { - this._api.getViewManager().setViewDefault(); - } - this._api.getStyleManager().setLightOrDarkMode(); - } + this._api.getStyleManager().setLightOrDarkMode(); }); } } diff --git a/src/card-controller/types.ts b/src/card-controller/types.ts index a77d89af..f5c627c6 100644 --- a/src/card-controller/types.ts +++ b/src/card-controller/types.ts @@ -99,6 +99,7 @@ export interface CardConfigLoaderAPI { } export interface CardDefaultManagerAPI { + getAutomationsManager(): AutomationsManager; getConfigManager(): ConfigManager; getHASSManager(): HASSManager; getInteractionManager(): InteractionManager; diff --git a/src/utils/action.ts b/src/utils/action.ts index b3ec8805..d26af9be 100644 --- a/src/utils/action.ts +++ b/src/utils/action.ts @@ -39,7 +39,7 @@ export function createGeneralAction( options?: { cardID?: string; }, -): FrigateCardCustomAction | null { +): FrigateCardCustomAction { return { action: 'fire-dom-event', frigate_card_action: action, diff --git a/tests/card-controller/default-manager.test.ts b/tests/card-controller/default-manager.test.ts index fedb496e..5f3be314 100644 --- a/tests/card-controller/default-manager.test.ts +++ b/tests/card-controller/default-manager.test.ts @@ -160,4 +160,68 @@ describe('DefaultManager', () => { expect(mock.calls.length).toBe(0); }); }); + + describe('interaction based', () => { + it('should not register automation on initialization', async () => { + const api = createCardAPI(); + vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS()); + vi.mocked(api.getConfigManager().getConfig).mockReturnValue( + createConfig({ + view: { + default_reset: { + after_interaction: false, + }, + }, + }), + ); + + const manager = new DefaultManager(api); + await manager.initialize(); + + expect(api.getAutomationsManager().addAutomations).not.toBeCalled(); + }); + + it('should register automation on initialization', async () => { + const api = createCardAPI(); + vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS()); + vi.mocked(api.getConfigManager().getConfig).mockReturnValue( + createConfig({ + view: { + default_reset: { + after_interaction: true, + }, + }, + }), + ); + + const manager = new DefaultManager(api); + await manager.initialize(); + + expect(api.getAutomationsManager().addAutomations).toBeCalledWith([ + { + actions: [ + { + action: 'fire-dom-event', + frigate_card_action: 'default', + }, + ], + conditions: [ + { + condition: 'interaction', + interaction: false, + }, + ], + tag: expect.anything(), + }, + ]); + }); + + it('should remove automation on uninitalize', async () => { + const api = createCardAPI(); + const manager = new DefaultManager(api); + await manager.uninitialize(); + + expect(api.getAutomationsManager().deleteAutomations).toBeCalledWith(manager); + }); + }); }); diff --git a/tests/card-controller/interaction-manager.test.ts b/tests/card-controller/interaction-manager.test.ts index b0e2546a..d0426f0d 100644 --- a/tests/card-controller/interaction-manager.test.ts +++ b/tests/card-controller/interaction-manager.test.ts @@ -23,58 +23,6 @@ describe('InteractionManager', () => { expect(api.getConditionsManager().setState).toBeCalledWith({ interaction: false }); }); - it('should take action after interaction ends', () => { - const api = createCardAPI(); - vi.mocked(api.getConfigManager().getConfig).mockReturnValue( - createConfig({ - view: { - interaction_seconds: 10, - default_reset: { - after_interaction: true, - }, - }, - }), - ); - const manager = new InteractionManager(api); - vi.useFakeTimers(); - vi.setSystemTime(start); - - manager.reportInteraction(); - - expect(manager.hasInteraction()).toBeTruthy(); - expect(api.getViewManager().setViewDefault).not.toBeCalled(); - - vi.mocked(api.getTriggersManager().isTriggered).mockReturnValue(false); - vi.setSystemTime(add(start, { seconds: 10 })); - vi.runOnlyPendingTimers(); - - expect(api.getViewManager().setViewDefault).toBeCalled(); - }); - - it('should not take action when triggered', () => { - const api = createCardAPI(); - vi.mocked(api.getConfigManager().getConfig).mockReturnValue( - createConfig({ - view: { - interaction_seconds: 10, - }, - }), - ); - const manager = new InteractionManager(api); - vi.useFakeTimers(); - vi.setSystemTime(start); - - manager.reportInteraction(); - - vi.mocked(api.getTriggersManager().isTriggered).mockReturnValue(true); - vi.setSystemTime(add(start, { seconds: 10 })); - vi.runOnlyPendingTimers(); - - // First call is blocked by triggers (above), so interaction will report - // true but the default view will not have been set. - expect(api.getViewManager().setViewDefault).not.toBeCalled(); - }); - it('should not take action without an interaction timeout', () => { const api = createCardAPI(); vi.mocked(api.getConfigManager().getConfig).mockReturnValue( @@ -88,35 +36,7 @@ describe('InteractionManager', () => { manager.reportInteraction(); - // First call is blocked by triggers (above), so interaction will report - // true but the default view will not have been set. - expect(api.getViewManager().setViewDefault).not.toBeCalled(); - }); - - it('should not take action without default_reset.after_interaction', () => { - const api = createCardAPI(); - vi.mocked(api.getConfigManager().getConfig).mockReturnValue( - createConfig({ - view: { - default_reset: { - after_interaction: false, - }, - interaction_seconds: 10, - }, - }), - ); - const manager = new InteractionManager(api); - vi.useFakeTimers(); - vi.setSystemTime(start); - - manager.reportInteraction(); - - vi.setSystemTime(add(start, { seconds: 10 })); - vi.runOnlyPendingTimers(); - - // First call is blocked by triggers (above), so interaction will report - // true but the default view will not have been set. - expect(api.getViewManager().setViewDefault).not.toBeCalled(); + expect(manager.hasInteraction()).toBeFalsy(); }); it('should set condition state', () => { @@ -140,6 +60,7 @@ describe('InteractionManager', () => { interaction: true, }), ); + expect(manager.hasInteraction()).toBeTruthy(); vi.setSystemTime(add(start, { seconds: 10 })); vi.runOnlyPendingTimers(); @@ -149,5 +70,6 @@ describe('InteractionManager', () => { interaction: false, }), ); + expect(manager.hasInteraction()).toBeFalsy(); }); });