Skip to content

Commit a6dc064

Browse files
committed
Work in progress
1 parent ca07975 commit a6dc064

File tree

1 file changed

+69
-2
lines changed

1 file changed

+69
-2
lines changed

homeassistant/components/blink/camera.py

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99
from requests.exceptions import ChunkedEncodingError
1010
import voluptuous as vol
1111

12-
from homeassistant.components.camera import Camera
12+
from homeassistant.components.camera import Camera, CameraEntityFeature
13+
from homeassistant.components.stream import Stream
1314
from homeassistant.const import CONF_FILE_PATH, CONF_FILENAME
14-
from homeassistant.core import HomeAssistant
15+
from homeassistant.core import HomeAssistant, callback
1516
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
1617
from homeassistant.helpers import config_validation as cv, entity_platform
1718
from homeassistant.helpers.device_registry import DeviceInfo
@@ -76,6 +77,7 @@ def __init__(self, coordinator: BlinkUpdateCoordinator, name, camera) -> None:
7677
super().__init__(coordinator)
7778
Camera.__init__(self)
7879
self._camera = camera
80+
self._livestream = None
7981
self._attr_unique_id = f"{camera.serial}-camera"
8082
self._attr_device_info = DeviceInfo(
8183
identifiers={(DOMAIN, camera.serial)},
@@ -200,3 +202,68 @@ async def save_video(self, filename) -> None:
200202
translation_domain=DOMAIN,
201203
translation_key="cant_write",
202204
) from err
205+
206+
_attr_supported_features = CameraEntityFeature.STREAM
207+
208+
async def async_create_stream(self) -> Stream | None:
209+
"""Create a stream."""
210+
stream = await super().async_create_stream()
211+
if stream is None:
212+
_LOGGER.error("Unable to create stream for %s", self._camera.name)
213+
return None
214+
215+
stream.pyav_options["f"] = "mpegts"
216+
stream.pyav_options["err_detect"] = "ignore_err"
217+
return stream
218+
219+
async def stream_source(self) -> str | None:
220+
"""Return the source of the stream."""
221+
if not self.is_streaming:
222+
livestream = await self._camera.init_livestream()
223+
if await livestream.start():
224+
_LOGGER.debug("%s started serving", self._camera.name)
225+
self._livestream = livestream
226+
else:
227+
_LOGGER.error("Unable to start stream for %s", self._camera.name)
228+
return None
229+
230+
name = f"livestream-{self._camera.serial}"
231+
task = self.hass.async_create_task(target=livestream.feed(), name=name)
232+
if task:
233+
_LOGGER.debug("%s started streaming", self._camera.name)
234+
task.add_done_callback(self.async_stream_done_callback)
235+
else:
236+
_LOGGER.error("Unable to create stream task for %s", self._camera.name)
237+
return None
238+
239+
if not self._livestream:
240+
_LOGGER.error("No livestream available for %s", self._camera.name)
241+
return None
242+
243+
_LOGGER.debug("Stream URL for %s: %s", self._camera.name, self._livestream.url)
244+
return self._livestream.url
245+
246+
@callback
247+
def async_stream_done_callback(self, task: Any) -> None:
248+
"""Handle the completion of the stream task."""
249+
self.stream = None
250+
251+
if self._livestream:
252+
self._livestream.stop()
253+
self._livestream = None
254+
self.async_write_ha_state()
255+
_LOGGER.debug("%s finished streaming", self._camera.name)
256+
else:
257+
_LOGGER.debug(
258+
"%s finished streaming, but no stream was active", self._camera.name
259+
)
260+
261+
@property
262+
def is_streaming(self) -> bool:
263+
"""Return True if the camera is streaming."""
264+
if self._livestream and self._livestream.is_serving:
265+
_LOGGER.debug("%s is streaming", self._camera.name)
266+
return True
267+
268+
_LOGGER.debug("%s is NOT streaming", self._camera.name)
269+
return False

0 commit comments

Comments
 (0)