Skip to content

Commit

Permalink
Cache Canary camera image (home-assistant#73923)
Browse files Browse the repository at this point in the history
* Cache camera image

Cache camera image so a new image isn't generated each call.
Adds debug logging

* Apply suggestions from code review

code compression with walrus operator

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* fix after walrus operator suggested tweak

fully use the live_stream_session variable in async_camera_image

* Invalidate cached image after 15 minutes

requested code change; invalidate cached image

* Removed unnecessary if statement

based on code review

* Image capture flow updates

now sets the image expiration upon getting an updated image
updates the cache image only when a new image is captured

Co-authored-by: Erik Montnemery <erik@montnemery.com>
  • Loading branch information
0bmay and emontnemery authored Jul 11, 2022
1 parent 5774f2e commit 2d2fd3e
Showing 1 changed file with 43 additions and 14 deletions.
57 changes: 43 additions & 14 deletions homeassistant/components/canary/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from __future__ import annotations

from datetime import timedelta
import logging
from typing import Final

from aiohttp.web import Request, StreamResponse
Expand All @@ -23,7 +24,7 @@
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util import Throttle
from homeassistant.util import dt as dt_util

from .const import (
CONF_FFMPEG_ARGUMENTS,
Expand All @@ -34,7 +35,7 @@
)
from .coordinator import CanaryDataUpdateCoordinator

MIN_TIME_BETWEEN_SESSION_RENEW: Final = timedelta(seconds=90)
FORCE_CAMERA_REFRESH_INTERVAL: Final = timedelta(minutes=15)

PLATFORM_SCHEMA: Final = vol.All(
cv.deprecated(CONF_FFMPEG_ARGUMENTS),
Expand All @@ -47,6 +48,8 @@
),
)

_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(
hass: HomeAssistant,
Expand Down Expand Up @@ -105,6 +108,11 @@ def __init__(
model=device.device_type["name"],
name=device.name,
)
self._image: bytes | None = None
self._expires_at = dt_util.utcnow()
_LOGGER.debug(
"%s %s has been initialized", self.name, device.device_type["name"]
)

@property
def location(self) -> Location:
Expand All @@ -125,17 +133,33 @@ async def async_camera_image(
self, width: int | None = None, height: int | None = None
) -> bytes | None:
"""Return a still image response from the camera."""
await self.hass.async_add_executor_job(self.renew_live_stream_session)
live_stream_url = await self.hass.async_add_executor_job(
getattr, self._live_stream_session, "live_stream_url"
)
return await ffmpeg.async_get_image(
self.hass,
live_stream_url,
extra_cmd=self._ffmpeg_arguments,
width=width,
height=height,
)
utcnow = dt_util.utcnow()
if self._expires_at <= utcnow:
_LOGGER.debug("Grabbing a live view image from %s", self.name)
await self.hass.async_add_executor_job(self.renew_live_stream_session)

if (live_stream_session := self._live_stream_session) is None:
return None

if not (live_stream_url := live_stream_session.live_stream_url):
return None

image = await ffmpeg.async_get_image(
self.hass,
live_stream_url,
extra_cmd=self._ffmpeg_arguments,
width=width,
height=height,
)

if image:
self._image = image
self._expires_at = FORCE_CAMERA_REFRESH_INTERVAL + utcnow
_LOGGER.debug("Grabbed a live view image from %s", self.name)
await self.hass.async_add_executor_job(live_stream_session.stop_session)
_LOGGER.debug("Stopped live session from %s", self.name)

return self._image

async def handle_async_mjpeg_stream(
self, request: Request
Expand All @@ -161,9 +185,14 @@ async def handle_async_mjpeg_stream(
finally:
await stream.close()

@Throttle(MIN_TIME_BETWEEN_SESSION_RENEW)
def renew_live_stream_session(self) -> None:
"""Renew live stream session."""
self._live_stream_session = self.coordinator.canary.get_live_stream_session(
self._device
)

_LOGGER.debug(
"Live Stream URL for %s is %s",
self.name,
self._live_stream_session.live_stream_url,
)

0 comments on commit 2d2fd3e

Please sign in to comment.