2019-02-13 20:21:14 +00:00
|
|
|
"""Camera support for the Skybell HD Doorbell."""
|
2021-08-11 00:33:06 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2022-06-13 03:27:19 +00:00
|
|
|
from aiohttp import web
|
|
|
|
from haffmpeg.camera import CameraMjpeg
|
2018-12-17 23:31:10 +00:00
|
|
|
|
2022-08-19 08:56:01 +00:00
|
|
|
from homeassistant.components.camera import Camera, CameraEntityDescription
|
2022-06-13 03:27:19 +00:00
|
|
|
from homeassistant.components.ffmpeg import get_ffmpeg_manager
|
2022-06-05 02:37:08 +00:00
|
|
|
from homeassistant.config_entries import ConfigEntry
|
2022-01-05 16:34:47 +00:00
|
|
|
from homeassistant.core import HomeAssistant
|
2022-06-13 03:27:19 +00:00
|
|
|
from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream
|
2022-06-05 02:37:08 +00:00
|
|
|
from homeassistant.helpers.entity import EntityDescription
|
2022-01-05 16:34:47 +00:00
|
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
2017-10-08 18:14:39 +00:00
|
|
|
|
2022-08-19 08:56:01 +00:00
|
|
|
from .const import DOMAIN
|
2022-06-05 02:37:08 +00:00
|
|
|
from .coordinator import SkybellDataUpdateCoordinator
|
|
|
|
from .entity import SkybellEntity
|
2018-12-17 23:31:10 +00:00
|
|
|
|
2022-06-05 02:37:08 +00:00
|
|
|
CAMERA_TYPES: tuple[CameraEntityDescription, ...] = (
|
2023-07-08 17:55:10 +00:00
|
|
|
CameraEntityDescription(
|
|
|
|
key="activity",
|
|
|
|
translation_key="activity",
|
|
|
|
),
|
|
|
|
CameraEntityDescription(
|
|
|
|
key="avatar",
|
|
|
|
translation_key="camera",
|
|
|
|
),
|
2022-06-05 02:37:08 +00:00
|
|
|
)
|
2017-10-08 18:14:39 +00:00
|
|
|
|
|
|
|
|
2022-06-05 02:37:08 +00:00
|
|
|
async def async_setup_entry(
|
|
|
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
|
|
|
) -> None:
|
2022-08-19 08:56:01 +00:00
|
|
|
"""Set up Skybell camera."""
|
2022-06-13 03:27:19 +00:00
|
|
|
entities = []
|
|
|
|
for description in CAMERA_TYPES:
|
|
|
|
for coordinator in hass.data[DOMAIN][entry.entry_id]:
|
|
|
|
if description.key == "avatar":
|
|
|
|
entities.append(SkybellCamera(coordinator, description))
|
|
|
|
else:
|
|
|
|
entities.append(SkybellActivityCamera(coordinator, description))
|
|
|
|
async_add_entities(entities)
|
2017-10-08 18:14:39 +00:00
|
|
|
|
|
|
|
|
2022-06-05 02:37:08 +00:00
|
|
|
class SkybellCamera(SkybellEntity, Camera):
|
2017-10-08 18:14:39 +00:00
|
|
|
"""A camera implementation for Skybell devices."""
|
|
|
|
|
2022-06-05 02:37:08 +00:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
coordinator: SkybellDataUpdateCoordinator,
|
|
|
|
description: EntityDescription,
|
|
|
|
) -> None:
|
2017-10-08 18:14:39 +00:00
|
|
|
"""Initialize a camera for a Skybell device."""
|
2022-06-05 02:37:08 +00:00
|
|
|
super().__init__(coordinator, description)
|
2017-10-08 18:14:39 +00:00
|
|
|
Camera.__init__(self)
|
2018-12-17 23:31:10 +00:00
|
|
|
|
2022-06-05 02:37:08 +00:00
|
|
|
async def async_camera_image(
|
2021-08-11 00:33:06 +00:00
|
|
|
self, width: int | None = None, height: int | None = None
|
|
|
|
) -> bytes | None:
|
2017-10-08 18:14:39 +00:00
|
|
|
"""Get the latest camera image."""
|
2022-06-05 02:37:08 +00:00
|
|
|
return self._device.images[self.entity_description.key]
|
2022-06-13 03:27:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
class SkybellActivityCamera(SkybellCamera):
|
|
|
|
"""A camera implementation for latest Skybell activity."""
|
|
|
|
|
|
|
|
async def handle_async_mjpeg_stream(
|
|
|
|
self, request: web.Request
|
|
|
|
) -> web.StreamResponse:
|
|
|
|
"""Generate an HTTP MJPEG stream from the latest recorded activity."""
|
|
|
|
stream = CameraMjpeg(get_ffmpeg_manager(self.hass).binary)
|
|
|
|
url = await self.coordinator.device.async_get_activity_video_url()
|
|
|
|
await stream.open_camera(url, extra_cmd="-r 210")
|
|
|
|
|
|
|
|
try:
|
|
|
|
return await async_aiohttp_proxy_stream(
|
|
|
|
self.hass,
|
|
|
|
request,
|
|
|
|
await stream.get_reader(),
|
|
|
|
get_ffmpeg_manager(self.hass).ffmpeg_stream_content_type,
|
|
|
|
)
|
|
|
|
finally:
|
|
|
|
await stream.close()
|