diff --git a/homeassistant/components/camera/canary.py b/homeassistant/components/camera/canary.py index 302758eee947594a59d5eafef5caecd186af7b1c..a230e0f6d4a211d078529d3fb873202f09977508 100644 --- a/homeassistant/components/camera/canary.py +++ b/homeassistant/components/camera/canary.py @@ -4,19 +4,30 @@ Support for Canary camera. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/camera.canary/ """ +import asyncio import logging +from datetime import timedelta -import requests +import voluptuous as vol -from homeassistant.components.camera import Camera +from homeassistant.components.camera import Camera, PLATFORM_SCHEMA from homeassistant.components.canary import DATA_CANARY, DEFAULT_TIMEOUT +from homeassistant.components.ffmpeg import DATA_FFMPEG +from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream +from homeassistant.util import Throttle -DEPENDENCIES = ['canary'] +CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' + +DEPENDENCIES = ['canary', 'ffmpeg'] _LOGGER = logging.getLogger(__name__) -ATTR_MOTION_START_TIME = "motion_start_time" -ATTR_MOTION_END_TIME = "motion_end_time" +MIN_TIME_BETWEEN_SESSION_RENEW = timedelta(seconds=90) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string, +}) def setup_platform(hass, config, add_devices, discovery_info=None): @@ -25,10 +36,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devices = [] for location in data.locations: - entries = data.get_motion_entries(location.location_id) - if entries: - devices.append(CanaryCamera(data, location.location_id, - DEFAULT_TIMEOUT)) + for device in location.devices: + if device.is_online: + devices.append( + CanaryCamera(hass, data, location, device, DEFAULT_TIMEOUT, + config.get(CONF_FFMPEG_ARGUMENTS))) add_devices(devices, True) @@ -36,60 +48,65 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class CanaryCamera(Camera): """An implementation of a Canary security camera.""" - def __init__(self, data, location_id, timeout): + def __init__(self, hass, data, location, device, timeout, ffmpeg_args): """Initialize a Canary security camera.""" super().__init__() + + self._ffmpeg = hass.data[DATA_FFMPEG] + self._ffmpeg_arguments = ffmpeg_args self._data = data - self._location_id = location_id + self._location = location + self._device = device self._timeout = timeout - - self._location = None - self._motion_entry = None - self._image_content = None - - def camera_image(self): - """Update the status of the camera and return bytes of camera image.""" - self.update() - return self._image_content + self._live_stream_session = None @property def name(self): """Return the name of this device.""" - return self._location.name + return self._device.name @property def is_recording(self): """Return true if the device is recording.""" return self._location.is_recording - @property - def device_state_attributes(self): - """Return device specific state attributes.""" - if self._motion_entry is None: - return None - - return { - ATTR_MOTION_START_TIME: self._motion_entry.start_time, - ATTR_MOTION_END_TIME: self._motion_entry.end_time, - } - - def update(self): - """Update the status of the camera.""" - self._data.update() - self._location = self._data.get_location(self._location_id) - - entries = self._data.get_motion_entries(self._location_id) - if entries: - current = entries[0] - previous = self._motion_entry - - if previous is None or previous.entry_id != current.entry_id: - self._motion_entry = current - self._image_content = requests.get( - current.thumbnails[0].image_url, - timeout=self._timeout).content - @property def motion_detection_enabled(self): """Return the camera motion detection status.""" return not self._location.is_recording + + @asyncio.coroutine + def async_camera_image(self): + """Return a still image response from the camera.""" + self.renew_live_stream_session() + + from haffmpeg import ImageFrame, IMAGE_JPEG + ffmpeg = ImageFrame(self._ffmpeg.binary, loop=self.hass.loop) + image = yield from asyncio.shield(ffmpeg.get_image( + self._live_stream_session.live_stream_url, + output_format=IMAGE_JPEG, + extra_cmd=self._ffmpeg_arguments), loop=self.hass.loop) + return image + + @asyncio.coroutine + def handle_async_mjpeg_stream(self, request): + """Generate an HTTP MJPEG stream from the camera.""" + if self._live_stream_session is None: + return + + from haffmpeg import CameraMjpeg + stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop) + yield from stream.open_camera( + self._live_stream_session.live_stream_url, + extra_cmd=self._ffmpeg_arguments) + + yield from async_aiohttp_proxy_stream( + self.hass, request, stream, + 'multipart/x-mixed-replace;boundary=ffserver') + yield from stream.close() + + @Throttle(MIN_TIME_BETWEEN_SESSION_RENEW) + def renew_live_stream_session(self): + """Renew live stream session.""" + self._live_stream_session = self._data.get_live_stream_session( + self._device) diff --git a/homeassistant/components/canary.py b/homeassistant/components/canary.py index 4d45f31ae598ccf116194c06bed44aff92ea865b..dfef4976eb897d8dd26c6115672f760074314336 100644 --- a/homeassistant/components/canary.py +++ b/homeassistant/components/canary.py @@ -15,7 +15,7 @@ from homeassistant.const import CONF_USERNAME, CONF_PASSWORD, CONF_TIMEOUT from homeassistant.helpers import discovery from homeassistant.util import Throttle -REQUIREMENTS = ['py-canary==0.2.3'] +REQUIREMENTS = ['py-canary==0.4.0'] _LOGGER = logging.getLogger(__name__) @@ -122,3 +122,7 @@ class CanaryData(object): """Set location mode.""" self._api.set_location_mode(location_id, mode_name, is_private) self.update(no_throttle=True) + + def get_live_stream_session(self, device): + """Return live stream session.""" + return self._api.get_live_stream_session(device) diff --git a/requirements_all.txt b/requirements_all.txt index 7270a4bf103546c4a8668dffd840334aaa68daa5..72bbee3e860d195a5cffa98c01c92060f1f39fae 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -618,7 +618,7 @@ pushetta==1.0.15 pwmled==1.2.1 # homeassistant.components.canary -py-canary==0.2.3 +py-canary==0.4.0 # homeassistant.components.sensor.cpuspeed py-cpuinfo==3.3.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b9a43eea0252b7b8772f39a569d6664af544c7b1..b4116a8b44c38ffa4250b97e9ae5b5004c0cdabc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -121,7 +121,7 @@ prometheus_client==0.1.0 pushbullet.py==0.11.0 # homeassistant.components.canary -py-canary==0.2.3 +py-canary==0.4.0 # homeassistant.components.zwave pydispatcher==2.0.5