feat: Add TPLink camera engine support (#2257)

* Related #2183

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Introduce `tplink` camera engine with relative PTZ via button
entities, add icons/docs support, refactor to `EntityCamera`, and extend
tests/schema/factory for auto-detection.
> 
> - **Engines**
>   - **New `tplink` engine**:
>     - Add `Engine.TPLink`, schema support (`engine: 'tplink'`).
> - Implement `TPLinkCameraManagerEngine` and `TPLinkCamera` (relative
PTZ via `button.*` entities; no presets/zoom; stop is no-op).
> - Update `engine-factory` to create/auto-detect `tplink` (platform
`tplink`).
> - **Refactor**
> - Replace `BrowseMediaCamera` with `EntityCamera`; update `motioneye`
and `reolink` cameras and `motioneye` engine type checks.
> - **UI/Icons**
> - Add `tplink.svg`; wire into `IconController`; update docs icon copy
script.
> - **Docs**
> - Add `tplink` to engine capability and live-provider matrices; new
`tplink` section incl. PTZ note; add custom icon entry.
> - **Tests**
> - Add TPLink camera/engine tests and factory tests; remove obsolete
`BrowseMediaCamera` test.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
74a688c3bd602912d63a22c3c6fa2f4b60ec14f9. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This commit is contained in:
Dermot Duffy
2025-12-07 16:57:33 -08:00
committed by GitHub
parent 3279481b0e
commit 596a37903f
20 changed files with 917 additions and 88 deletions
@@ -1,48 +0,0 @@
import { describe, expect, it } from 'vitest';
import { mock } from 'vitest-mock-extended';
import { BrowseMediaCamera } from '../../../src/camera-manager/browse-media/camera';
import { CameraManagerEngine } from '../../../src/camera-manager/engine';
import { StateWatcherSubscriptionInterface } from '../../../src/card-controller/hass/state-watcher';
import { Entity, EntityRegistryManager } from '../../../src/ha/registry/entity/types';
import { createCameraConfig, createHASS } from '../../test-utils';
describe('BrowseMediaCamera', () => {
describe('should initialize', () => {
it('without a camera_entity', async () => {
const camera = new BrowseMediaCamera(
createCameraConfig(),
mock<CameraManagerEngine>(),
);
expect(
async () =>
await camera.initialize({
hass: createHASS(),
stateWatcher: mock<StateWatcherSubscriptionInterface>(),
entityRegistryManager: mock<EntityRegistryManager>(),
}),
).rejects.toThrowError(/Could not find camera entity/);
});
it('with a camera_entity', async () => {
const camera = new BrowseMediaCamera(
createCameraConfig({
camera_entity: 'camera.foo',
}),
mock<CameraManagerEngine>(),
);
const entityRegistryManager = mock<EntityRegistryManager>();
const entity = mock<Entity>();
entityRegistryManager.getEntity.mockResolvedValue(entity);
expect(
await camera.initialize({
hass: createHASS(),
stateWatcher: mock<StateWatcherSubscriptionInterface>(),
entityRegistryManager: entityRegistryManager,
}),
).toBe(camera);
expect(camera.getEntity()).toBe(entity);
});
});
});