mirror of
https://github.com/home-assistant/core.git
synced 2026-04-02 00:20:30 +01:00
155 lines
5.0 KiB
Python
155 lines
5.0 KiB
Python
"""Support for Tuya cameras."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from tuya_device_handlers import TUYA_QUIRKS_REGISTRY
|
|
from tuya_device_handlers.definition.camera import (
|
|
CameraQuirk,
|
|
TuyaCameraDefinition,
|
|
get_default_definition,
|
|
)
|
|
from tuya_sharing import CustomerDevice, Manager
|
|
|
|
from homeassistant.components import ffmpeg
|
|
from homeassistant.components.camera import (
|
|
Camera as CameraEntity,
|
|
CameraEntityDescription,
|
|
CameraEntityFeature,
|
|
)
|
|
from homeassistant.core import HomeAssistant, callback
|
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
|
|
|
from . import TuyaConfigEntry
|
|
from .const import TUYA_DISCOVERY_NEW, DeviceCategory
|
|
from .entity import TuyaEntity
|
|
|
|
CAMERAS: dict[DeviceCategory, CameraEntityDescription] = {
|
|
DeviceCategory.DGHSXJ: CameraEntityDescription(key=""),
|
|
DeviceCategory.SP: CameraEntityDescription(key=""),
|
|
}
|
|
|
|
|
|
def _get_quirk_entity_description(
|
|
entity_quirk: CameraQuirk,
|
|
) -> CameraEntityDescription:
|
|
return CameraEntityDescription(key=entity_quirk.key)
|
|
|
|
|
|
def _get_quirk_entities(
|
|
manager: Manager, device: CustomerDevice
|
|
) -> list[TuyaCameraEntity] | None:
|
|
if (quirk := TUYA_QUIRKS_REGISTRY.get_quirk_for_device(device)) is None or (
|
|
entity_quirks := quirk.camera_quirks
|
|
) is None:
|
|
return None
|
|
return [
|
|
TuyaCameraEntity(
|
|
device,
|
|
manager,
|
|
_get_quirk_entity_description(entity_quirk),
|
|
definition,
|
|
)
|
|
for entity_quirk in entity_quirks
|
|
if (definition := entity_quirk.definition_fn(device))
|
|
]
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
entry: TuyaConfigEntry,
|
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
|
) -> None:
|
|
"""Set up Tuya cameras dynamically through Tuya discovery."""
|
|
manager = entry.runtime_data.manager
|
|
|
|
@callback
|
|
def async_discover_device(device_ids: list[str]) -> None:
|
|
"""Discover and add a discovered Tuya camera."""
|
|
entities: list[TuyaCameraEntity] = []
|
|
for device_id in device_ids:
|
|
device = manager.device_map[device_id]
|
|
if (quirk_entities := _get_quirk_entities(manager, device)) is not None:
|
|
entities.extend(quirk_entities)
|
|
continue
|
|
if description := CAMERAS.get(device.category):
|
|
entities.append(
|
|
TuyaCameraEntity(
|
|
device, manager, description, get_default_definition(device)
|
|
)
|
|
)
|
|
|
|
async_add_entities(entities)
|
|
|
|
async_discover_device([*manager.device_map])
|
|
|
|
entry.async_on_unload(
|
|
async_dispatcher_connect(hass, TUYA_DISCOVERY_NEW, async_discover_device)
|
|
)
|
|
|
|
|
|
class TuyaCameraEntity(TuyaEntity, CameraEntity):
|
|
"""Tuya Camera Entity."""
|
|
|
|
_attr_supported_features = CameraEntityFeature.STREAM
|
|
_attr_brand = "Tuya"
|
|
_attr_name = None
|
|
|
|
def __init__(
|
|
self,
|
|
device: CustomerDevice,
|
|
device_manager: Manager,
|
|
description: CameraEntityDescription,
|
|
definition: TuyaCameraDefinition,
|
|
) -> None:
|
|
"""Init Tuya Camera."""
|
|
super().__init__(device, device_manager, description)
|
|
CameraEntity.__init__(self)
|
|
self._attr_model = device.product_name
|
|
self._motion_detection_switch = definition.motion_detection_switch
|
|
self._recording_status = definition.recording_status
|
|
|
|
@property
|
|
def is_recording(self) -> bool:
|
|
"""Return true if the device is recording."""
|
|
if (status := self._read_wrapper(self._recording_status)) is not None:
|
|
return status
|
|
return False
|
|
|
|
@property
|
|
def motion_detection_enabled(self) -> bool:
|
|
"""Return the camera motion detection status."""
|
|
if (status := self._read_wrapper(self._motion_detection_switch)) is not None:
|
|
return status
|
|
return False
|
|
|
|
async def stream_source(self) -> str | None:
|
|
"""Return the source of the stream."""
|
|
return await self.hass.async_add_executor_job(
|
|
self.device_manager.get_device_stream_allocate,
|
|
self.device.id,
|
|
"rtsp",
|
|
)
|
|
|
|
async def async_camera_image(
|
|
self, width: int | None = None, height: int | None = None
|
|
) -> bytes | None:
|
|
"""Return a still image response from the camera."""
|
|
stream_source = await self.stream_source()
|
|
if not stream_source:
|
|
return None
|
|
return await ffmpeg.async_get_image(
|
|
self.hass,
|
|
stream_source,
|
|
width=width,
|
|
height=height,
|
|
)
|
|
|
|
async def async_enable_motion_detection(self) -> None:
|
|
"""Enable motion detection in the camera."""
|
|
await self._async_send_wrapper_updates(self._motion_detection_switch, True)
|
|
|
|
async def async_disable_motion_detection(self) -> None:
|
|
"""Disable motion detection in camera."""
|
|
await self._async_send_wrapper_updates(self._motion_detection_switch, False)
|