Initial version of more advanced scan mode control.
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
import { FrigateCardMediaPlayer } from '../../src/types';
|
||||
import {
|
||||
convertActionToFrigateCardCustomAction,
|
||||
frigateCardHandleAction,
|
||||
frigateCardHandleActionConfig,
|
||||
getActionConfigGivenAction,
|
||||
} from '../../src/utils/action.js';
|
||||
@@ -245,7 +246,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
card_id: 'NOT_foo',
|
||||
frigate_card_action: 'default',
|
||||
@@ -259,7 +260,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'default',
|
||||
})!,
|
||||
@@ -283,7 +284,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: viewName,
|
||||
})!,
|
||||
@@ -301,7 +302,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'download',
|
||||
})!,
|
||||
@@ -314,7 +315,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'camera_ui',
|
||||
})!,
|
||||
@@ -327,7 +328,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'expand',
|
||||
})!,
|
||||
@@ -340,7 +341,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'fullscreen',
|
||||
})!,
|
||||
@@ -353,7 +354,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'menu_toggle',
|
||||
})!,
|
||||
@@ -370,7 +371,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(createView());
|
||||
vi.mocked(api.getViewManager().isViewSupportedByCamera).mockReturnValue(true);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'camera_select',
|
||||
camera: 'camera',
|
||||
@@ -397,7 +398,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
);
|
||||
vi.mocked(api.getViewManager().isViewSupportedByCamera).mockReturnValue(true);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'camera_select',
|
||||
camera: 'camera',
|
||||
@@ -430,7 +431,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
vi.mocked(api.getViewManager().isViewSupportedByCamera).mockReturnValue(true);
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'camera_select',
|
||||
camera: 'camera',
|
||||
@@ -445,11 +446,55 @@ describe('ActionsManager.executeAction', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('with triggered camera', async () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(createView());
|
||||
vi.mocked(api.getViewManager().isViewSupportedByCamera).mockReturnValue(true);
|
||||
vi.mocked(
|
||||
api.getTriggersManager().getMostRecentlyTriggeredCameraID,
|
||||
).mockReturnValue('camera');
|
||||
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'camera_select',
|
||||
triggered: true,
|
||||
})!,
|
||||
);
|
||||
|
||||
expect(api.getViewManager().setViewByParameters).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
viewName: 'live',
|
||||
cameraID: 'camera',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('without camera or triggered camera', async () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(createView());
|
||||
vi.mocked(api.getViewManager().isViewSupportedByCamera).mockReturnValue(true);
|
||||
vi.mocked(
|
||||
api.getTriggersManager().getMostRecentlyTriggeredCameraID,
|
||||
).mockReturnValue('camera');
|
||||
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'camera_select',
|
||||
})!,
|
||||
);
|
||||
|
||||
expect(api.getViewManager().setViewByParameters).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('without a current view', async () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'camera_select',
|
||||
camera: 'camera',
|
||||
@@ -469,7 +514,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'camera_select',
|
||||
camera: 'camera',
|
||||
@@ -490,7 +535,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'live_substream_select',
|
||||
camera: 'substream',
|
||||
@@ -504,7 +549,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'live_substream_off',
|
||||
})!,
|
||||
@@ -517,7 +562,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'live_substream_on',
|
||||
})!,
|
||||
@@ -531,7 +576,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'media_player',
|
||||
media_player_action: 'stop',
|
||||
@@ -552,7 +597,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
);
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'media_player',
|
||||
media_player_action: 'play',
|
||||
@@ -576,7 +621,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(view);
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'media_player',
|
||||
media_player_action: 'play',
|
||||
@@ -599,7 +644,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
);
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'media_player',
|
||||
media_player_action: 'play',
|
||||
@@ -615,7 +660,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'diagnostics',
|
||||
})!,
|
||||
@@ -632,7 +677,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'microphone_mute',
|
||||
})!,
|
||||
@@ -645,7 +690,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'microphone_unmute',
|
||||
})!,
|
||||
@@ -670,7 +715,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
);
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: action,
|
||||
})!,
|
||||
@@ -684,7 +729,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'screenshot',
|
||||
})!,
|
||||
@@ -697,7 +742,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'display_mode_select',
|
||||
display_mode: 'grid',
|
||||
@@ -715,7 +760,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
createView({ camera: 'camera.office' }),
|
||||
);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'ptz',
|
||||
ptz_action: 'left',
|
||||
@@ -736,7 +781,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'ptz',
|
||||
ptz_action: 'left',
|
||||
@@ -751,7 +796,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ActionsManager(api);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
createAction({
|
||||
frigate_card_action: 'show_ptz',
|
||||
show_ptz: true,
|
||||
@@ -768,7 +813,7 @@ describe('ActionsManager.executeAction', () => {
|
||||
|
||||
const spy = vi.spyOn(global.console, 'warn').mockImplementation(() => true);
|
||||
|
||||
await manager.executeAction(
|
||||
await manager.executeFrigateAction(
|
||||
// Have to manually create the action (vs using `createAction()`) since
|
||||
// it's malformed.
|
||||
{
|
||||
@@ -781,3 +826,36 @@ describe('ActionsManager.executeAction', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ActionsManager.executeActions', () => {
|
||||
it('should execute actions', async () => {
|
||||
const api = createCardAPI();
|
||||
const hass = createHASS();
|
||||
const element = document.createElement('div');
|
||||
|
||||
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(hass);
|
||||
|
||||
const manager = new ActionsManager(api);
|
||||
const action = createAction({
|
||||
frigate_card_action: 'default',
|
||||
})!;
|
||||
manager.executeActions(action);
|
||||
|
||||
expect(frigateCardHandleAction).toBeCalledWith(element, hass, {}, action);
|
||||
});
|
||||
|
||||
it('should not execute actions without hass', async () => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(null);
|
||||
|
||||
const manager = new ActionsManager(api);
|
||||
manager.executeActions(
|
||||
createAction({
|
||||
frigate_card_action: 'default',
|
||||
})!,
|
||||
);
|
||||
|
||||
expect(api.getViewManager().setViewDefault).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -72,10 +72,10 @@ describe('HASSManager', () => {
|
||||
|
||||
const originalHASS = createHASS();
|
||||
manager.setHASS(originalHASS);
|
||||
expect(api.getTriggersManager().updateTriggeredCameras).toBeCalledWith(null);
|
||||
expect(api.getTriggersManager().updateTriggerHAState).toBeCalledWith(null);
|
||||
|
||||
manager.setHASS(createHASS());
|
||||
expect(api.getTriggersManager().updateTriggeredCameras).toBeCalledWith(originalHASS);
|
||||
expect(api.getTriggersManager().updateTriggerHAState).toBeCalledWith(originalHASS);
|
||||
});
|
||||
|
||||
describe('should handle connection state change when', () => {
|
||||
|
||||
@@ -30,7 +30,6 @@ describe('InteractionManager', () => {
|
||||
|
||||
manager.reportInteraction();
|
||||
|
||||
expect(api.getTriggersManager().untrigger).toBeCalled();
|
||||
expect(manager.hasInteraction()).toBeTruthy();
|
||||
expect(api.getViewManager().setViewDefault).not.toBeCalled();
|
||||
|
||||
|
||||
@@ -271,7 +271,6 @@ describe('MediaPlayerManager', () => {
|
||||
},
|
||||
]),
|
||||
);
|
||||
vi.mocked(api.getQueryStringManager().generateQueryString).mockReturnValue('');
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||
const manager = new MediaPlayerManager(api);
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('QueryStringManager', () => {
|
||||
manager.executeAll();
|
||||
|
||||
expect(manager.hasViewRelatedActions()).toBeFalsy();
|
||||
expect(api.getActionsManager().executeAction).not.toBeCalled();
|
||||
expect(api.getActionsManager().executeFrigateAction).not.toBeCalled();
|
||||
expect(api.getViewManager().setViewByParameters).not.toBeCalled();
|
||||
});
|
||||
|
||||
@@ -76,7 +76,7 @@ describe('QueryStringManager', () => {
|
||||
manager.executeAll();
|
||||
|
||||
expect(manager.hasViewRelatedActions()).toBeFalsy();
|
||||
expect(api.getActionsManager().executeAction).toBeCalledWith({
|
||||
expect(api.getActionsManager().executeFrigateAction).toBeCalledWith({
|
||||
action: 'fire-dom-event',
|
||||
card_id: 'id',
|
||||
frigate_card_action: action,
|
||||
@@ -97,7 +97,7 @@ describe('QueryStringManager', () => {
|
||||
expect(api.getViewManager().setViewDefault).toBeCalled();
|
||||
|
||||
expect(manager.hasViewRelatedActions()).toBeTruthy();
|
||||
expect(api.getActionsManager().executeAction).not.toBeCalled();
|
||||
expect(api.getActionsManager().executeFrigateAction).not.toBeCalled();
|
||||
expect(api.getViewManager().setViewByParameters).not.toBeCalled();
|
||||
});
|
||||
|
||||
@@ -114,7 +114,7 @@ describe('QueryStringManager', () => {
|
||||
});
|
||||
|
||||
expect(manager.hasViewRelatedActions()).toBeTruthy();
|
||||
expect(api.getActionsManager().executeAction).not.toBeCalled();
|
||||
expect(api.getActionsManager().executeFrigateAction).not.toBeCalled();
|
||||
expect(api.getViewManager().setViewDefault).not.toBeCalled();
|
||||
});
|
||||
|
||||
@@ -131,7 +131,7 @@ describe('QueryStringManager', () => {
|
||||
});
|
||||
|
||||
expect(manager.hasViewRelatedActions()).toBeTruthy();
|
||||
expect(api.getActionsManager().executeAction).not.toBeCalled();
|
||||
expect(api.getActionsManager().executeFrigateAction).not.toBeCalled();
|
||||
expect(api.getViewManager().setViewDefault).not.toBeCalled();
|
||||
});
|
||||
|
||||
@@ -147,7 +147,7 @@ describe('QueryStringManager', () => {
|
||||
manager.executeAll();
|
||||
|
||||
expect(manager.hasViewRelatedActions()).toBeFalsy();
|
||||
expect(api.getActionsManager().executeAction).not.toBeCalled();
|
||||
expect(api.getActionsManager().executeFrigateAction).not.toBeCalled();
|
||||
expect(api.getViewManager().setViewDefault).not.toBeCalled();
|
||||
expect(api.getViewManager().setViewByParameters).not.toBeCalled();
|
||||
},
|
||||
@@ -165,7 +165,7 @@ describe('QueryStringManager', () => {
|
||||
manager.executeAll();
|
||||
|
||||
expect(manager.hasViewRelatedActions()).toBeFalsy();
|
||||
expect(api.getActionsManager().executeAction).not.toBeCalled();
|
||||
expect(api.getActionsManager().executeFrigateAction).not.toBeCalled();
|
||||
expect(api.getViewManager().setViewDefault).not.toBeCalled();
|
||||
expect(api.getViewManager().setViewByParameters).not.toBeCalled();
|
||||
expect(consoleSpy).toBeCalled();
|
||||
@@ -212,7 +212,7 @@ describe('QueryStringManager', () => {
|
||||
|
||||
manager.executeAll();
|
||||
|
||||
expect(api.getActionsManager().executeAction).not.toBeCalled();
|
||||
expect(api.getActionsManager().executeFrigateAction).not.toBeCalled();
|
||||
expect(api.getViewManager().setViewDefault).not.toBeCalled();
|
||||
expect(api.getViewManager().setViewByParameters).not.toBeCalled();
|
||||
});
|
||||
@@ -296,77 +296,7 @@ describe('QueryStringManager', () => {
|
||||
|
||||
manager.executeViewRelated();
|
||||
|
||||
expect(api.getActionsManager().executeAction).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('should generate query string', () => {
|
||||
describe('that require no arguments', () => {
|
||||
it.each([
|
||||
['camera_ui' as const],
|
||||
['clip' as const],
|
||||
['clips' as const],
|
||||
['default' as const],
|
||||
['diagnostics' as const],
|
||||
['download' as const],
|
||||
['expand' as const],
|
||||
['image' as const],
|
||||
['live' as const],
|
||||
['menu_toggle' as const],
|
||||
['recording' as const],
|
||||
['recordings' as const],
|
||||
['snapshot' as const],
|
||||
['snapshots' as const],
|
||||
['timeline' as const],
|
||||
])('%s', (actionName: FrigateCardGeneralAction | FrigateCardUserSpecifiedView) => {
|
||||
const manager = new QueryStringManager(createCardAPI());
|
||||
expect(
|
||||
manager.generateQueryString({
|
||||
action: 'fire-dom-event',
|
||||
frigate_card_action: actionName,
|
||||
}),
|
||||
).toBe(`frigate-card-action.${actionName}=`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('that require camera argument', () => {
|
||||
it.each([['camera_select' as const], ['live_substream_select' as const]])(
|
||||
'%s',
|
||||
(actionName: 'camera_select' | 'live_substream_select') => {
|
||||
const manager = new QueryStringManager(createCardAPI());
|
||||
expect(
|
||||
manager.generateQueryString({
|
||||
action: 'fire-dom-event',
|
||||
frigate_card_action: actionName,
|
||||
camera: 'camera',
|
||||
}),
|
||||
).toBe(`frigate-card-action.${actionName}=camera`);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('that include a card_id', () => {
|
||||
const manager = new QueryStringManager(createCardAPI());
|
||||
expect(
|
||||
manager.generateQueryString({
|
||||
action: 'fire-dom-event',
|
||||
frigate_card_action: 'clips',
|
||||
card_id: 'card-id',
|
||||
}),
|
||||
).toBe(`frigate-card-action.card-id.clips=`);
|
||||
});
|
||||
|
||||
it('that include an unsupported action', () => {
|
||||
const spy = vi.spyOn(global.console, 'warn').mockReturnValue(undefined);
|
||||
const manager = new QueryStringManager(createCardAPI());
|
||||
|
||||
expect(
|
||||
manager.generateQueryString({
|
||||
action: 'fire-dom-event',
|
||||
frigate_card_action: 'microphone_unmute',
|
||||
}),
|
||||
).toBeNull();
|
||||
expect(spy).toBeCalled();
|
||||
expect(api.getActionsManager().executeFrigateAction).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,6 +15,28 @@ import {
|
||||
createView,
|
||||
} from '../test-utils';
|
||||
|
||||
const baseScanConfig: Partial<ScanOptions> = {
|
||||
enabled: true,
|
||||
untrigger_seconds: 10,
|
||||
interaction_mode: 'inactive' as const,
|
||||
trigger_filter_camera: 'all' as const,
|
||||
trigger_action: [
|
||||
{
|
||||
action: 'fire-dom-event' as const,
|
||||
frigate_card_action: 'camera_select' as const,
|
||||
triggered: true,
|
||||
},
|
||||
{
|
||||
action: 'fire-dom-event' as const,
|
||||
frigate_card_action: 'live' as const,
|
||||
},
|
||||
],
|
||||
untrigger_action: {
|
||||
action: 'fire-dom-event' as const,
|
||||
frigate_card_action: 'default' as const,
|
||||
},
|
||||
};
|
||||
|
||||
// Creating and mocking a trigger API is a lot of boilerplate, this convenience
|
||||
// function reduces it.
|
||||
const createTriggerAPI = (options?: {
|
||||
@@ -26,11 +48,7 @@ const createTriggerAPI = (options?: {
|
||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||
createConfig({
|
||||
view: {
|
||||
scan: options?.config ?? {
|
||||
enabled: true,
|
||||
untrigger_reset: true,
|
||||
untrigger_seconds: 10,
|
||||
},
|
||||
scan: options?.config ?? baseScanConfig,
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -83,11 +101,28 @@ describe('TriggersManager', () => {
|
||||
});
|
||||
const manager = new TriggersManager(api);
|
||||
|
||||
manager.updateTriggeredCameras(null);
|
||||
manager.updateTriggerHAState(null);
|
||||
|
||||
expect(manager.isTriggered()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not trigger without a scan config', () => {
|
||||
const api = createTriggerAPI({
|
||||
hassStates: hassActiveState,
|
||||
});
|
||||
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(
|
||||
createView({
|
||||
camera: 'camera_1' as const,
|
||||
}),
|
||||
);
|
||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(null);
|
||||
|
||||
const manager = new TriggersManager(api);
|
||||
manager.updateView(null);
|
||||
expect(manager.isTriggered()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should trigger and untrigger based on entity state', () => {
|
||||
const start = new Date('2023-10-01T17:14');
|
||||
vi.setSystemTime(start);
|
||||
@@ -96,22 +131,29 @@ describe('TriggersManager', () => {
|
||||
});
|
||||
const manager = new TriggersManager(api);
|
||||
|
||||
manager.updateTriggeredCameras(createHASS(hassInactiveState));
|
||||
manager.updateTriggerHAState(createHASS(hassInactiveState));
|
||||
|
||||
expect(manager.isTriggered()).toBeTruthy();
|
||||
expect(api.getViewManager().setViewByParameters).toBeCalledWith({
|
||||
viewName: 'live',
|
||||
cameraID: 'camera_1',
|
||||
});
|
||||
expect(api.getActionsManager().executeActions).toBeCalledWith([
|
||||
{
|
||||
action: 'fire-dom-event' as const,
|
||||
frigate_card_action: 'camera_select' as const,
|
||||
triggered: true,
|
||||
},
|
||||
{
|
||||
action: 'fire-dom-event' as const,
|
||||
frigate_card_action: 'live' as const,
|
||||
},
|
||||
]);
|
||||
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(
|
||||
createHASS(hassInactiveState),
|
||||
);
|
||||
|
||||
manager.updateTriggeredCameras(createHASS(hassActiveState));
|
||||
manager.updateTriggerHAState(createHASS(hassActiveState));
|
||||
|
||||
// Intentional state update with no change.
|
||||
manager.updateTriggeredCameras(createHASS(hassActiveState));
|
||||
manager.updateTriggerHAState(createHASS(hassActiveState));
|
||||
|
||||
// Will still be triggered, but untrigger timer will be running.
|
||||
expect(manager.isTriggered()).toBeTruthy();
|
||||
@@ -120,26 +162,10 @@ describe('TriggersManager', () => {
|
||||
vi.runOnlyPendingTimers();
|
||||
|
||||
expect(manager.isTriggered()).toBeFalsy();
|
||||
expect(api.getViewManager().setViewDefault).toBeCalled();
|
||||
});
|
||||
|
||||
it('should trigger and set view if current view is wrong', () => {
|
||||
const api = createTriggerAPI({
|
||||
hassStates: hassActiveState,
|
||||
});
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(
|
||||
createView({
|
||||
// Correct camera, but wrong view.
|
||||
view: 'clips',
|
||||
camera: 'camera_1',
|
||||
}),
|
||||
);
|
||||
const manager = new TriggersManager(api);
|
||||
manager.updateTriggeredCameras(null);
|
||||
|
||||
expect(api.getViewManager().setViewByParameters).toBeCalledWith({
|
||||
viewName: 'live',
|
||||
cameraID: 'camera_1',
|
||||
expect(api.getActionsManager().executeActions).toBeCalledWith({
|
||||
action: 'fire-dom-event' as const,
|
||||
frigate_card_action: 'default' as const,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -150,40 +176,196 @@ describe('TriggersManager', () => {
|
||||
const manager = new TriggersManager(api);
|
||||
expect(manager.isTriggered()).toBeFalsy();
|
||||
|
||||
manager.updateTriggeredCameras(null);
|
||||
manager.updateTriggerHAState(null);
|
||||
expect(manager.isTriggered()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should untrigger manually', () => {
|
||||
const api = createTriggerAPI({
|
||||
hassStates: hassActiveState,
|
||||
});
|
||||
const manager = new TriggersManager(api);
|
||||
|
||||
// Untriggering when not triggered.
|
||||
manager.untrigger();
|
||||
expect(api.getViewManager().setViewDefault).not.toBeCalled();
|
||||
|
||||
manager.updateTriggeredCameras(null);
|
||||
expect(manager.isTriggered()).toBeTruthy();
|
||||
|
||||
manager.untrigger();
|
||||
expect(manager.isTriggered()).toBeFalsy();
|
||||
expect(api.getViewManager().setViewDefault).toBeCalled();
|
||||
});
|
||||
|
||||
it('should take no actions when automated actions are not allowed', () => {
|
||||
it('should take no actions with human interactions', () => {
|
||||
const start = new Date('2023-10-01T17:14');
|
||||
const api = createTriggerAPI({
|
||||
hassStates: hassActiveState,
|
||||
// Interaction present.
|
||||
interaction: true,
|
||||
});
|
||||
const manager = new TriggersManager(api);
|
||||
manager.updateTriggeredCameras(null);
|
||||
manager.updateTriggerHAState(createHASS(hassInactiveState));
|
||||
expect(manager.isTriggered()).toBeTruthy();
|
||||
expect(api.getViewManager().setViewByParameters).not.toBeCalled();
|
||||
|
||||
manager.untrigger();
|
||||
expect(api.getViewManager().setViewDefault).not.toBeCalled();
|
||||
expect(api.getActionsManager().executeActions).not.toBeCalled();
|
||||
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(
|
||||
createHASS(hassInactiveState),
|
||||
);
|
||||
manager.updateTriggerHAState(createHASS(hassActiveState));
|
||||
|
||||
vi.setSystemTime(add(start, { seconds: 10 }));
|
||||
vi.runOnlyPendingTimers();
|
||||
|
||||
expect(manager.isTriggered()).toBeFalsy();
|
||||
|
||||
expect(api.getActionsManager().executeActions).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should take actions with human interactions when interaction mode is active', () => {
|
||||
const start = new Date('2023-10-01T17:14');
|
||||
const api = createTriggerAPI({
|
||||
hassStates: hassActiveState,
|
||||
// Interaction present.
|
||||
interaction: true,
|
||||
config: {
|
||||
...baseScanConfig,
|
||||
interaction_mode: 'active',
|
||||
},
|
||||
});
|
||||
const manager = new TriggersManager(api);
|
||||
manager.updateTriggerHAState(createHASS(hassInactiveState));
|
||||
expect(manager.isTriggered()).toBeTruthy();
|
||||
|
||||
expect(api.getActionsManager().executeActions).toBeCalledTimes(1);
|
||||
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(
|
||||
createHASS(hassInactiveState),
|
||||
);
|
||||
manager.updateTriggerHAState(createHASS(hassActiveState));
|
||||
|
||||
vi.setSystemTime(add(start, { seconds: 10 }));
|
||||
vi.runOnlyPendingTimers();
|
||||
|
||||
expect(manager.isTriggered()).toBeFalsy();
|
||||
|
||||
expect(api.getActionsManager().executeActions).toBeCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should report multiple triggered cameras', () => {
|
||||
const start = new Date('2023-10-01T17:14');
|
||||
const bothOnState = {
|
||||
'binary_sensor.one': createStateEntity({ state: 'on' }),
|
||||
'binary_sensor.two': createStateEntity({ state: 'on' }),
|
||||
};
|
||||
const api = createTriggerAPI({
|
||||
hassStates: bothOnState,
|
||||
});
|
||||
vi.mocked(api.getCameraManager().getStore).mockReturnValue(
|
||||
createStore([
|
||||
{
|
||||
cameraID: 'camera_1',
|
||||
config: createCameraConfig({
|
||||
triggers: {
|
||||
entities: ['binary_sensor.one'],
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
cameraID: 'camera_2',
|
||||
config: createCameraConfig({
|
||||
triggers: {
|
||||
entities: ['binary_sensor.two'],
|
||||
},
|
||||
}),
|
||||
},
|
||||
]),
|
||||
);
|
||||
|
||||
const manager = new TriggersManager(api);
|
||||
|
||||
expect(manager.isTriggered()).toBeFalsy();
|
||||
expect(manager.getMostRecentlyTriggeredCameraID()).toBeNull();
|
||||
expect(manager.getTriggeredCameraIDs()).toEqual(new Set());
|
||||
|
||||
manager.updateTriggerHAState(null);
|
||||
expect(manager.isTriggered()).toBeTruthy();
|
||||
expect(manager.getTriggeredCameraIDs()).toEqual(new Set(['camera_1', 'camera_2']));
|
||||
|
||||
// Either is the most recently triggered.
|
||||
expect(['camera_1', 'camera_2']).toContain(
|
||||
manager.getMostRecentlyTriggeredCameraID(),
|
||||
);
|
||||
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(
|
||||
createHASS({
|
||||
'binary_sensor.one': createStateEntity({ state: 'off' }),
|
||||
'binary_sensor.two': createStateEntity({ state: 'on' }),
|
||||
}),
|
||||
);
|
||||
|
||||
manager.updateTriggerHAState(createHASS(bothOnState));
|
||||
|
||||
vi.setSystemTime(add(start, { seconds: 10 }));
|
||||
vi.runOnlyPendingTimers();
|
||||
|
||||
expect(manager.getTriggeredCameraIDs()).toEqual(new Set(['camera_2']));
|
||||
expect(manager.getMostRecentlyTriggeredCameraID()).toBe('camera_2');
|
||||
});
|
||||
|
||||
it('should filter triggers by camera', () => {
|
||||
const start = new Date('2023-10-01T17:14');
|
||||
const api = createTriggerAPI({
|
||||
config: {
|
||||
...baseScanConfig,
|
||||
// Filter triggers to selected camera only.
|
||||
trigger_filter_camera: 'selected' as const,
|
||||
},
|
||||
hassStates: hassActiveState,
|
||||
});
|
||||
const manager = new TriggersManager(api);
|
||||
manager.updateTriggerHAState(createHASS(hassInactiveState));
|
||||
expect(manager.isTriggered()).toBeFalsy();
|
||||
|
||||
const otherCameraSelected = createView({
|
||||
camera: 'camera_SOME_OTHER_CAMERA' as const,
|
||||
});
|
||||
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(otherCameraSelected);
|
||||
manager.updateView(null);
|
||||
expect(manager.isTriggered()).toBeFalsy();
|
||||
|
||||
const thisCameraSelected = createView({
|
||||
camera: 'camera_1' as const,
|
||||
});
|
||||
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(thisCameraSelected);
|
||||
manager.updateView(otherCameraSelected);
|
||||
expect(manager.isTriggered()).toBeTruthy();
|
||||
|
||||
// Ensure a view change to the same camera stays triggered.
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(thisCameraSelected);
|
||||
manager.updateView(thisCameraSelected);
|
||||
expect(manager.isTriggered()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not untrigger triggers by camera', () => {
|
||||
const start = new Date('2023-10-01T17:14');
|
||||
const api = createTriggerAPI({
|
||||
config: {
|
||||
...baseScanConfig,
|
||||
// Filter triggers to selected camera only.
|
||||
trigger_filter_camera: 'selected' as const,
|
||||
},
|
||||
hassStates: hassActiveState,
|
||||
});
|
||||
const manager = new TriggersManager(api);
|
||||
manager.updateTriggerHAState(createHASS(hassInactiveState));
|
||||
expect(manager.isTriggered()).toBeFalsy();
|
||||
|
||||
const otherCameraSelected = createView({
|
||||
camera: 'camera_SOME_OTHER_CAMERA' as const,
|
||||
});
|
||||
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(otherCameraSelected);
|
||||
manager.updateView(null);
|
||||
expect(manager.isTriggered()).toBeFalsy();
|
||||
|
||||
const thisCameraSelected = createView({
|
||||
camera: 'camera_1' as const,
|
||||
});
|
||||
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(thisCameraSelected);
|
||||
manager.updateView(otherCameraSelected);
|
||||
expect(manager.isTriggered()).toBeTruthy();
|
||||
|
||||
// Ensure a view change to the same camera stays triggered.
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(thisCameraSelected);
|
||||
manager.updateView(thisCameraSelected);
|
||||
expect(manager.isTriggered()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user