"""Support for Netgear Arlo IP cameras.""" import logging from haffmpeg.camera import CameraMjpeg import voluptuous as vol from homeassistant.components.camera import PLATFORM_SCHEMA, Camera from homeassistant.components.ffmpeg import DATA_FFMPEG from homeassistant.const import ATTR_BATTERY_LEVEL from homeassistant.core import callback from homeassistant.helpers.aiohttp_client import async_aiohttp_proxy_stream import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_connect from . import DATA_ARLO, DEFAULT_BRAND, SIGNAL_UPDATE_ARLO _LOGGER = logging.getLogger(__name__) ARLO_MODE_ARMED = "armed" ARLO_MODE_DISARMED = "disarmed" ATTR_BRIGHTNESS = "brightness" ATTR_FLIPPED = "flipped" ATTR_MIRRORED = "mirrored" ATTR_MOTION = "motion_detection_sensitivity" ATTR_POWERSAVE = "power_save_mode" ATTR_SIGNAL_STRENGTH = "signal_strength" ATTR_UNSEEN_VIDEOS = "unseen_videos" ATTR_LAST_REFRESH = "last_refresh" CONF_FFMPEG_ARGUMENTS = "ffmpeg_arguments" DEFAULT_ARGUMENTS = "-pred 1" POWERSAVE_MODE_MAPPING = {1: "best_battery_life", 2: "optimized", 3: "best_video"} PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( {vol.Optional(CONF_FFMPEG_ARGUMENTS, default=DEFAULT_ARGUMENTS): cv.string} ) def setup_platform(hass, config, add_entities, discovery_info=None): """Set up an Arlo IP Camera.""" arlo = hass.data[DATA_ARLO] cameras = [] for camera in arlo.cameras: cameras.append(ArloCam(hass, camera, config)) add_entities(cameras) class ArloCam(Camera): """An implementation of a Netgear Arlo IP camera.""" def __init__(self, hass, camera, device_info): """Initialize an Arlo camera.""" super().__init__() self._camera = camera self._name = self._camera.name self._motion_status = False self._ffmpeg = hass.data[DATA_FFMPEG] self._ffmpeg_arguments = device_info.get(CONF_FFMPEG_ARGUMENTS) self._last_refresh = None self.attrs = {} def camera_image(self): """Return a still image response from the camera.""" return self._camera.last_image_from_cache async def async_added_to_hass(self): """Register callbacks.""" async_dispatcher_connect(self.hass, SIGNAL_UPDATE_ARLO, self._update_callback) @callback def _update_callback(self): """Call update method.""" self.async_schedule_update_ha_state() async def handle_async_mjpeg_stream(self, request): """Generate an HTTP MJPEG stream from the camera.""" video = self._camera.last_video if not video: error_msg = "Video not found for {0}. Is it older than {1} days?".format( self.name, self._camera.min_days_vdo_cache ) _LOGGER.error(error_msg) return stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop) await stream.open_camera(video.video_url, extra_cmd=self._ffmpeg_arguments) try: stream_reader = await stream.get_reader() return await async_aiohttp_proxy_stream( self.hass, request, stream_reader, self._ffmpeg.ffmpeg_stream_content_type, ) finally: await stream.close() @property def name(self): """Return the name of this camera.""" return self._name @property def device_state_attributes(self): """Return the state attributes.""" return { name: value for name, value in ( (ATTR_BATTERY_LEVEL, self._camera.battery_level), (ATTR_BRIGHTNESS, self._camera.brightness), (ATTR_FLIPPED, self._camera.flip_state), (ATTR_MIRRORED, self._camera.mirror_state), (ATTR_MOTION, self._camera.motion_detection_sensitivity), ( ATTR_POWERSAVE, POWERSAVE_MODE_MAPPING.get(self._camera.powersave_mode), ), (ATTR_SIGNAL_STRENGTH, self._camera.signal_strength), (ATTR_UNSEEN_VIDEOS, self._camera.unseen_videos), ) if value is not None } @property def model(self): """Return the camera model.""" return self._camera.model_id @property def brand(self): """Return the camera brand.""" return DEFAULT_BRAND @property def motion_detection_enabled(self): """Return the camera motion detection status.""" return self._motion_status def set_base_station_mode(self, mode): """Set the mode in the base station.""" # Get the list of base stations identified by library base_stations = self.hass.data[DATA_ARLO].base_stations # Some Arlo cameras does not have base station # So check if there is base station detected first # if yes, then choose the primary base station # Set the mode on the chosen base station if base_stations: primary_base_station = base_stations[0] primary_base_station.mode = mode def enable_motion_detection(self): """Enable the Motion detection in base station (Arm).""" self._motion_status = True self.set_base_station_mode(ARLO_MODE_ARMED) def disable_motion_detection(self): """Disable the motion detection in base station (Disarm).""" self._motion_status = False self.set_base_station_mode(ARLO_MODE_DISARMED)