1 """Support for Canary camera."""
3 from __future__
import annotations
5 from datetime
import timedelta
7 from typing
import Final
9 from aiohttp.web
import Request, StreamResponse
10 from canary.live_stream_api
import LiveStreamSession
11 from canary.model
import Device, Location
12 from haffmpeg.camera
import CameraMjpeg
13 import voluptuous
as vol
17 PLATFORM_SCHEMA
as CAMERA_PLATFORM_SCHEMA,
31 CONF_FFMPEG_ARGUMENTS,
33 DEFAULT_FFMPEG_ARGUMENTS,
37 from .coordinator
import CanaryDataUpdateCoordinator
39 FORCE_CAMERA_REFRESH_INTERVAL: Final =
timedelta(minutes=15)
41 PLATFORM_SCHEMA: Final = vol.All(
42 cv.deprecated(CONF_FFMPEG_ARGUMENTS),
43 CAMERA_PLATFORM_SCHEMA.extend(
46 CONF_FFMPEG_ARGUMENTS, default=DEFAULT_FFMPEG_ARGUMENTS
52 _LOGGER = logging.getLogger(__name__)
58 async_add_entities: AddEntitiesCallback,
60 """Set up Canary sensors based on a config entry."""
61 coordinator: CanaryDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][
64 ffmpeg_arguments: str = entry.options.get(
65 CONF_FFMPEG_ARGUMENTS, DEFAULT_FFMPEG_ARGUMENTS
77 for location_id, location
in coordinator.data[
"locations"].items()
78 for device
in location.devices
86 """An implementation of a Canary security camera."""
91 coordinator: CanaryDataUpdateCoordinator,
96 """Initialize a Canary security camera."""
107 identifiers={(DOMAIN,
str(device.device_id))},
108 manufacturer=MANUFACTURER,
109 model=device.device_type[
"name"],
112 self.
_image_image: bytes |
None =
None
115 "%s %s has been initialized", self.
namenamename, device.device_type[
"name"]
120 """Return information about the location."""
121 return self.coordinator.data[
"locations"][self.
_location_id_location_id]
125 """Return true if the device is recording."""
126 return self.
locationlocation.is_recording
130 """Return the camera motion detection status."""
131 return not self.
locationlocation.is_recording
134 self, width: int |
None =
None, height: int |
None =
None
136 """Return a still image response from the camera."""
137 utcnow = dt_util.utcnow()
139 _LOGGER.debug(
"Grabbing a live view image from %s", self.
namenamename)
145 if not (live_stream_url := live_stream_session.live_stream_url):
148 image = await ffmpeg.async_get_image(
158 self.
_expires_at_expires_at = FORCE_CAMERA_REFRESH_INTERVAL + utcnow
159 _LOGGER.debug(
"Grabbed a live view image from %s", self.
namenamename)
160 await self.
hasshasshass.async_add_executor_job(live_stream_session.stop_session)
161 _LOGGER.debug(
"Stopped live session from %s", self.
namenamename)
166 self, request: Request
167 ) -> StreamResponse |
None:
168 """Generate an HTTP MJPEG stream from the camera."""
172 live_stream_url = await self.
hasshasshass.async_add_executor_job(
175 stream = CameraMjpeg(self._ffmpeg.binary)
176 await stream.open_camera(live_stream_url, extra_cmd=self.
_ffmpeg_arguments_ffmpeg_arguments)
179 stream_reader = await stream.get_reader()
184 self._ffmpeg.ffmpeg_stream_content_type,
190 """Renew live stream session."""
196 "Live Stream URL for %s is %s",
None renew_live_stream_session(self)
None __init__(self, HomeAssistant hass, CanaryDataUpdateCoordinator coordinator, str location_id, Device device, str ffmpeg_args)
StreamResponse|None handle_async_mjpeg_stream(self, Request request)
bool motion_detection_enabled(self)
bytes|None async_camera_image(self, int|None width=None, int|None height=None)
str|UndefinedType|None name(self)
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
FFmpegManager get_ffmpeg_manager(HomeAssistant hass)
web.StreamResponse async_aiohttp_proxy_stream(HomeAssistant hass, web.BaseRequest request, aiohttp.StreamReader stream, str|None content_type, int buffer_size=102400, int timeout=10)