Home Assistant Unofficial Reference 2024.12.1
camera.py
Go to the documentation of this file.
1 """Support for ESPHome cameras."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from collections.abc import Callable
7 from functools import partial
8 from typing import Any
9 
10 from aioesphomeapi import CameraInfo, CameraState
11 from aiohttp import web
12 
13 from homeassistant.components import camera
14 from homeassistant.components.camera import Camera
15 from homeassistant.core import callback
16 
17 from .entity import EsphomeEntity, platform_async_setup_entry
18 
19 
20 class EsphomeCamera(Camera, EsphomeEntity[CameraInfo, CameraState]):
21  """A camera implementation for ESPHome."""
22 
23  def __init__(self, *args: Any, **kwargs: Any) -> None:
24  """Initialize."""
25  Camera.__init__(self)
26  EsphomeEntity.__init__(self, *args, **kwargs)
27  self._loop_loop = asyncio.get_running_loop()
28  self._image_futures: list[asyncio.Future[bool | None]] = []
29 
30  @callback
31  def _set_futures(self, result: bool) -> None:
32  """Set futures to done."""
33  for future in self._image_futures:
34  if not future.done():
35  future.set_result(result)
36  self._image_futures.clear()
37 
38  @callback
39  def _on_device_update(self) -> None:
40  """Handle device going available or unavailable."""
41  super()._on_device_update()
42  if not self.availableavailableavailable:
43  self._set_futures_set_futures(False)
44 
45  @callback
46  def _on_state_update(self) -> None:
47  """Notify listeners of new image when update arrives."""
48  super()._on_state_update()
49  self._set_futures_set_futures(True)
50 
51  async def async_camera_image(
52  self, width: int | None = None, height: int | None = None
53  ) -> bytes | None:
54  """Return single camera image bytes."""
55  return await self._async_request_image_async_request_image(self._client_client.request_single_image)
56 
58  self, request_method: Callable[[], None]
59  ) -> bytes | None:
60  """Wait for an image to be available and return it."""
61  if not self.availableavailableavailable:
62  return None
63  image_future = self._loop_loop.create_future()
64  self._image_futures.append(image_future)
65  request_method()
66  if not await image_future:
67  return None
68  return self._state_state.data
69 
71  self, request: web.Request
72  ) -> web.StreamResponse:
73  """Serve an HTTP MJPEG stream from the camera."""
74  stream_request = partial(
75  self._async_request_image_async_request_image, self._client_client.request_image_stream
76  )
77  return await camera.async_get_still_stream(
78  request, stream_request, camera.DEFAULT_CONTENT_TYPE, 0.0
79  )
80 
81 
82 async_setup_entry = partial(
83  platform_async_setup_entry,
84  info_type=CameraInfo,
85  entity_type=EsphomeCamera,
86  state_type=CameraState,
87 )
None __init__(self, *Any args, **Any kwargs)
Definition: camera.py:23
bytes|None async_camera_image(self, int|None width=None, int|None height=None)
Definition: camera.py:53
bytes|None _async_request_image(self, Callable[[], None] request_method)
Definition: camera.py:59
web.StreamResponse handle_async_mjpeg_stream(self, web.Request request)
Definition: camera.py:72