fix: Reolink PTZ support should detect presets (#1997)

- Related: #1964
This commit is contained in:
Dermot Duffy
2025-04-06 17:47:43 +01:00
committed by GitHub
parent e7a9e20c9e
commit a24c9628c5
6 changed files with 253 additions and 63 deletions
+115 -1
View File
@@ -7,7 +7,12 @@ import { ActionsExecutor } from '../../../src/card-controller/actions/types';
import { StateWatcher } from '../../../src/card-controller/hass/state-watcher';
import { ProxyConfig } from '../../../src/config/schema/cameras';
import { EntityRegistryManagerLive } from '../../../src/utils/ha/registry/entity';
import { createCameraConfig, createHASS, createRegistryEntity } from '../../test-utils';
import {
createCameraConfig,
createHASS,
createRegistryEntity,
createStateEntity,
} from '../../test-utils';
import { EntityRegistryManagerMock } from '../../utils/ha/registry/entity/mock';
describe('ReolinkCamera', () => {
@@ -51,6 +56,11 @@ describe('ReolinkCamera', () => {
unique_id: '85270002TS7D4RUP_0_ptz_stop',
platform: 'reolink',
});
const selectEntityPTZ = createRegistryEntity({
entity_id: 'select.office_reolink_ptz_preset',
unique_id: '85270002TS7D4RUP_0_ptz_preset',
platform: 'reolink',
});
const ptzPopulatedEntityRegistryManager = new EntityRegistryManagerMock([
cameraEntity,
@@ -61,6 +71,7 @@ describe('ReolinkCamera', () => {
buttonEntityPTZZoomIn,
buttonEntityPTZZoomOut,
buttonEntityPTZStop,
selectEntityPTZ,
// Unrelated button.
createRegistryEntity({
@@ -178,6 +189,36 @@ describe('ReolinkCamera', () => {
});
});
it('should find PTZ select entity', async () => {
const config = createCameraConfig({
camera_entity: 'camera.office_reolink',
});
const camera = new ReolinkCamera(config, mock<CameraManagerEngine>());
await camera.initialize({
hass: createHASS({
'select.office_reolink_ptz_preset': createStateEntity({
state: 'foo',
attributes: {
options: ['preset-one', 'preset-two'],
},
}),
}),
entityRegistryManager: ptzPopulatedEntityRegistryManager,
stateWatcher: mock<StateWatcher>(),
});
expect(camera.getCapabilities()?.getPTZCapabilities()).toEqual({
left: ['continuous'],
right: ['continuous'],
up: ['continuous'],
down: ['continuous'],
zoomIn: ['continuous'],
zoomOut: ['continuous'],
presets: ['preset-one', 'preset-two'],
});
});
it('should allow configured PTZ actions to override', async () => {
const config = createCameraConfig({
camera_entity: 'camera.office_reolink',
@@ -428,5 +469,78 @@ describe('ReolinkCamera', () => {
],
});
});
it('should ignore relative actions', async () => {
const config = createCameraConfig({
camera_entity: 'camera.office_reolink',
});
const camera = new ReolinkCamera(config, mock<CameraManagerEngine>());
await camera.initialize({
hass: createHASS(),
entityRegistryManager: ptzPopulatedEntityRegistryManager,
stateWatcher: mock<StateWatcher>(),
});
const executor = mock<ActionsExecutor>();
await camera.executePTZAction(executor, 'left');
expect(executor.executeActions).not.toHaveBeenCalled();
});
describe('should execute preset', () => {
it('for existing preset', async () => {
const config = createCameraConfig({
camera_entity: 'camera.office_reolink',
});
const camera = new ReolinkCamera(config, mock<CameraManagerEngine>());
await camera.initialize({
hass: createHASS({
'select.office_reolink_ptz_preset': createStateEntity({
state: 'foo',
attributes: {
options: ['preset-one', 'preset-two'],
},
}),
}),
entityRegistryManager: ptzPopulatedEntityRegistryManager,
stateWatcher: mock<StateWatcher>(),
});
const executor = mock<ActionsExecutor>();
await camera.executePTZAction(executor, 'preset', { preset: 'preset-two' });
expect(executor.executeActions).toHaveBeenLastCalledWith({
actions: [
{
action: 'perform-action',
perform_action: 'select.select_option',
target: {
entity_id: 'select.office_reolink_ptz_preset',
},
data: {
option: 'preset-two',
},
},
],
});
});
it('for non-existant preset', async () => {
const config = createCameraConfig({
camera_entity: 'camera.office_reolink',
});
const camera = new ReolinkCamera(config, mock<CameraManagerEngine>());
await camera.initialize({
hass: createHASS(),
entityRegistryManager: ptzPopulatedEntityRegistryManager,
stateWatcher: mock<StateWatcher>(),
});
const executor = mock<ActionsExecutor>();
await camera.executePTZAction(executor, 'preset');
expect(executor.executeActions).not.toHaveBeenCalled();
});
});
});
});