fix: Honor configured home preset / merge presets. (#2553)
- Closes: https://github.com/dermotduffy/advanced-camera-card/issues/2525
This commit is contained in:
committed by
dermotduffy
parent
d833edb65b
commit
5c97e55a56
@@ -507,6 +507,47 @@ describe('ReolinkCamera', () => {
|
||||
zoomOut: ['continuous'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should union configured presets with detected presets', async () => {
|
||||
const config = createCameraConfig({
|
||||
camera_entity: 'camera.office_reolink',
|
||||
ptz: {
|
||||
presets: {
|
||||
home: {
|
||||
action: 'perform-action',
|
||||
perform_action: 'button.press',
|
||||
target: { entity_id: 'button.office_guard' },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const camera = new ReolinkCamera(config, mock<CameraManagerEngine>());
|
||||
|
||||
await camera.initialize({
|
||||
hassManager: createHASSManager({
|
||||
hass: createHASS({
|
||||
'select.office_reolink_ptz_preset': createStateEntity({
|
||||
state: 'foo',
|
||||
attributes: {
|
||||
options: ['preset-one', 'preset-two'],
|
||||
},
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
entityRegistryManager: ptzPopulatedEntityRegistryManager,
|
||||
deviceRegistryManager: mock<DeviceRegistryManager>(),
|
||||
});
|
||||
|
||||
expect(camera.getCapabilities()?.getPTZCapabilities()).toEqual({
|
||||
left: ['continuous'],
|
||||
right: ['continuous'],
|
||||
up: ['continuous'],
|
||||
down: ['continuous'],
|
||||
zoomIn: ['continuous'],
|
||||
zoomOut: ['continuous'],
|
||||
presets: ['home', 'preset-one', 'preset-two'],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@ import {
|
||||
getConfiguredPTZAction,
|
||||
getConfiguredPTZMovementType,
|
||||
getPTZCapabilitiesFromCameraConfig,
|
||||
mergePTZCapabilities,
|
||||
} from '../../../src/camera-manager/utils/ptz';
|
||||
import type { PTZAction } from '../../../src/config/schema/actions/custom/ptz';
|
||||
import { PTZMovementType } from '../../../src/types';
|
||||
import { createCameraConfig } from '../../test-utils';
|
||||
|
||||
const action = {
|
||||
@@ -186,3 +188,64 @@ describe('getPTZCapabilitiesFromCameraConfig', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('mergePTZCapabilities', () => {
|
||||
it('should return null when both are null', () => {
|
||||
expect(mergePTZCapabilities(null, null)).toBeNull();
|
||||
});
|
||||
|
||||
it('should return engine capabilities when no config capabilities', () => {
|
||||
expect(
|
||||
mergePTZCapabilities(
|
||||
{ left: [PTZMovementType.Continuous], presets: ['Staw', 'Piwnica'] },
|
||||
null,
|
||||
),
|
||||
).toEqual({
|
||||
left: [PTZMovementType.Continuous],
|
||||
presets: ['Staw', 'Piwnica'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return config capabilities when no engine capabilities', () => {
|
||||
expect(mergePTZCapabilities(null, { presets: ['home'] })).toEqual({
|
||||
presets: ['home'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should union presets with configured presets first', () => {
|
||||
expect(
|
||||
mergePTZCapabilities(
|
||||
{ left: [PTZMovementType.Continuous], presets: ['Staw', 'Piwnica'] },
|
||||
{ presets: ['home'] },
|
||||
),
|
||||
).toEqual({
|
||||
left: [PTZMovementType.Continuous],
|
||||
presets: ['home', 'Staw', 'Piwnica'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should not duplicate presets present in both sources', () => {
|
||||
expect(
|
||||
mergePTZCapabilities({ presets: ['home', 'Staw'] }, { presets: ['home'] }),
|
||||
).toEqual({
|
||||
presets: ['home', 'Staw'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should let configured movement actions override engine equivalents', () => {
|
||||
expect(
|
||||
mergePTZCapabilities(
|
||||
{ left: [PTZMovementType.Continuous] },
|
||||
{ left: [PTZMovementType.Relative] },
|
||||
),
|
||||
).toEqual({
|
||||
left: [PTZMovementType.Relative],
|
||||
});
|
||||
});
|
||||
|
||||
it('should omit presets when neither source has any', () => {
|
||||
expect(mergePTZCapabilities({ left: [PTZMovementType.Continuous] }, null)).toEqual({
|
||||
left: [PTZMovementType.Continuous],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -205,6 +205,101 @@ describe('should handle ptz action', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should call configured home preset in preference to first preset', async () => {
|
||||
// See: https://github.com/dermotduffy/advanced-camera-card/issues/2525
|
||||
const api = createCardAPI();
|
||||
const store = createStore([
|
||||
{
|
||||
cameraID: 'camera.office',
|
||||
config: createCameraConfig({
|
||||
ptz: {
|
||||
presets: {
|
||||
home: {
|
||||
action: 'perform-action',
|
||||
perform_action: 'button.press',
|
||||
data: { entity_id: 'button.office_guard' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
// An engine (e.g. Reolink) auto-detects presets that would otherwise
|
||||
// shadow the configured home action.
|
||||
capabilities: new Capabilities({
|
||||
ptz: { presets: ['Staw', 'Piwnica'] },
|
||||
}),
|
||||
},
|
||||
]);
|
||||
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager(store));
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(
|
||||
createView({ camera: 'camera.office' }),
|
||||
);
|
||||
|
||||
const action = new PTZAction(
|
||||
{},
|
||||
{
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'ptz',
|
||||
},
|
||||
);
|
||||
|
||||
await action.execute(api);
|
||||
|
||||
expect(api.getCameraManager().executePTZAction).toBeCalledWith(
|
||||
'camera.office',
|
||||
'preset',
|
||||
{
|
||||
phase: undefined,
|
||||
preset: 'home',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should call first preset when no home preset is configured', async () => {
|
||||
const api = createCardAPI();
|
||||
const store = createStore([
|
||||
{
|
||||
cameraID: 'camera.office',
|
||||
config: createCameraConfig({
|
||||
ptz: {
|
||||
presets: {
|
||||
window: {
|
||||
action: 'perform-action',
|
||||
perform_action: 'button.press',
|
||||
data: { entity_id: 'button.office_window' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
capabilities: new Capabilities({
|
||||
ptz: { presets: ['Staw', 'Piwnica'] },
|
||||
}),
|
||||
},
|
||||
]);
|
||||
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager(store));
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(
|
||||
createView({ camera: 'camera.office' }),
|
||||
);
|
||||
|
||||
const action = new PTZAction(
|
||||
{},
|
||||
{
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'ptz',
|
||||
},
|
||||
);
|
||||
|
||||
await action.execute(api);
|
||||
|
||||
expect(api.getCameraManager().executePTZAction).toBeCalledWith(
|
||||
'camera.office',
|
||||
'preset',
|
||||
{
|
||||
phase: undefined,
|
||||
preset: 'Staw',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should not call preset when there are no presets', async () => {
|
||||
const api = createCardAPI();
|
||||
const store = createStore([
|
||||
|
||||
Reference in New Issue
Block a user