1 """Support for IP Cameras."""
3 from __future__
import annotations
6 from collections.abc
import AsyncIterator
7 from contextlib
import suppress
10 from aiohttp
import web
21 HTTP_BASIC_AUTHENTICATION,
22 HTTP_DIGEST_AUTHENTICATION,
26 async_aiohttp_proxy_web,
27 async_get_clientsession,
33 from .const
import CONF_MJPEG_URL, CONF_STILL_IMAGE_URL, DOMAIN, LOGGER
42 async_add_entities: AddEntitiesCallback,
44 """Set up a MJPEG IP Camera based on a config entry."""
49 authentication=entry.options[CONF_AUTHENTICATION],
50 username=entry.options.get(CONF_USERNAME),
51 password=entry.options[CONF_PASSWORD],
52 mjpeg_url=entry.options[CONF_MJPEG_URL],
53 still_image_url=entry.options.get(CONF_STILL_IMAGE_URL),
54 verify_ssl=entry.options[CONF_VERIFY_SSL],
55 unique_id=entry.entry_id,
58 identifiers={(DOMAIN, entry.entry_id)},
66 """Take in a MJPEG stream object, return the jpg from it."""
69 async
for chunk
in stream:
71 jpg_end = data.find(b
"\xff\xd9")
76 jpg_start = data.find(b
"\xff\xd8")
81 return data[jpg_start : jpg_end + 2]
87 """An implementation of an IP camera that is reachable over a URL."""
92 name: str |
None =
None,
94 still_image_url: str |
None,
95 authentication: str |
None =
None,
96 username: str |
None =
None,
98 verify_ssl: bool =
True,
99 unique_id: str |
None =
None,
100 device_info: DeviceInfo |
None =
None,
102 """Initialize a MJPEG camera."""
120 if unique_id
is not None:
122 if device_info
is not None:
126 """Return the stream source."""
129 url = url.with_user(self.
_username_username)
131 url = url.with_password(self.
_password_password)
135 self, width: int |
None =
None, height: int |
None =
None
137 """Return a still image response from the camera."""
146 async
with asyncio.timeout(TIMEOUT):
149 return await response.read()
152 LOGGER.error(
"Timeout getting camera image from %s", self.
namename)
154 except aiohttp.ClientError
as err:
155 LOGGER.error(
"Error getting new camera image from %s: %s", self.
namename, err)
160 """Return a httpx auth object."""
162 digest_auth = self.
_authentication_authentication == HTTP_DIGEST_AUTHENTICATION
163 cls = httpx.DigestAuth
if digest_auth
else httpx.BasicAuth
164 return cls(username, self.
_password_password)
167 """Return a still image response from the camera using digest authentication."""
173 with suppress(TimeoutError, httpx.HTTPError):
180 async
with client.stream(
181 "get", self.
_mjpeg_url_mjpeg_url, auth=auth, timeout=TIMEOUT
184 stream.aiter_bytes(BUFFER_SIZE)
188 LOGGER.error(
"Timeout getting camera image from %s", self.
namename)
190 except httpx.HTTPError
as err:
191 LOGGER.error(
"Error getting new camera image from %s: %s", self.
namename, err)
196 self, request: web.Request
197 ) -> web.StreamResponse |
None:
198 """Generate an HTTP MJPEG stream from the camera using digest authentication."""
202 response = web.StreamResponse(headers=stream.headers)
203 await response.prepare(request)
205 with suppress(TimeoutError, httpx.HTTPError):
206 async
for chunk
in stream.aiter_bytes(BUFFER_SIZE):
207 if not self.
hasshass.is_running:
209 async
with asyncio.timeout(TIMEOUT):
210 await response.write(chunk)
214 self, request: web.Request
215 ) -> web.StreamResponse |
None:
216 """Generate an HTTP MJPEG stream from the camera."""
223 stream_coro = websession.get(self.
_mjpeg_url_mjpeg_url, auth=self.
_auth_auth)
bytes|None async_camera_image(self, int|None width=None, int|None height=None)
web.StreamResponse|None _handle_async_mjpeg_digest_stream(self, web.Request request)
web.StreamResponse|None handle_async_mjpeg_stream(self, web.Request request)
bytes|None _async_digest_or_fallback_camera_image(self)
httpx.Auth _get_httpx_auth(self)
None __init__(self, *str|None name=None, str mjpeg_url, str|None still_image_url, str|None authentication=None, str|None username=None, str password="", bool verify_ssl=True, str|None unique_id=None, DeviceInfo|None device_info=None)
str|UndefinedType|None name(self)
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
bytes|None async_extract_image_from_mjpeg(AsyncIterator[bytes] stream)
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)
web.StreamResponse|None async_aiohttp_proxy_web(HomeAssistant hass, web.BaseRequest request, Awaitable[aiohttp.ClientResponse] web_coro, int buffer_size=102400, int timeout=10)
httpx.AsyncClient get_async_client(HomeAssistant hass, bool verify_ssl=True)