diff --git a/homeassistant/components/amcrest/__init__.py b/homeassistant/components/amcrest/__init__.py index 6de31caa90e..58df1d8e504 100644 --- a/homeassistant/components/amcrest/__init__.py +++ b/homeassistant/components/amcrest/__init__.py @@ -3,6 +3,7 @@ import logging from datetime import timedelta import aiohttp +from amcrest import AmcrestCamera, AmcrestError import voluptuous as vol from homeassistant.auth.permissions.const import POLICY_CONTROL @@ -32,6 +33,7 @@ _LOGGER = logging.getLogger(__name__) CONF_RESOLUTION = 'resolution' CONF_STREAM_SOURCE = 'stream_source' CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments' +CONF_CONTROL_LIGHT = 'control_light' DEFAULT_NAME = 'Amcrest Camera' DEFAULT_PORT = 80 @@ -103,6 +105,7 @@ AMCREST_SCHEMA = vol.All( _deprecated_sensor_values), vol.Optional(CONF_SWITCHES): vol.All(cv.ensure_list, [vol.In(SWITCHES)]), + vol.Optional(CONF_CONTROL_LIGHT, default=True): cv.boolean, }), _deprecated_switches ) @@ -114,8 +117,6 @@ CONFIG_SCHEMA = vol.Schema({ def setup(hass, config): """Set up the Amcrest IP Camera component.""" - from amcrest import AmcrestCamera, AmcrestError - hass.data.setdefault(DATA_AMCREST, {'devices': {}, 'cameras': []}) devices = config[DOMAIN] @@ -149,6 +150,7 @@ def setup(hass, config): sensors = device.get(CONF_SENSORS) switches = device.get(CONF_SWITCHES) stream_source = device[CONF_STREAM_SOURCE] + control_light = device.get(CONF_CONTROL_LIGHT) # currently aiohttp only works with basic authentication # only valid for mjpeg streaming @@ -159,7 +161,7 @@ def setup(hass, config): hass.data[DATA_AMCREST]['devices'][name] = AmcrestDevice( api, authentication, ffmpeg_arguments, stream_source, - resolution) + resolution, control_light) discovery.load_platform( hass, CAMERA, DOMAIN, { @@ -245,10 +247,11 @@ class AmcrestDevice: """Representation of a base Amcrest discovery device.""" def __init__(self, api, authentication, ffmpeg_arguments, - stream_source, resolution): + stream_source, resolution, control_light): """Initialize the entity.""" self.api = api self.authentication = authentication self.ffmpeg_arguments = ffmpeg_arguments self.stream_source = stream_source self.resolution = resolution + self.control_light = control_light diff --git a/homeassistant/components/amcrest/binary_sensor.py b/homeassistant/components/amcrest/binary_sensor.py index 0eb9e42e707..fe4eb25b3db 100644 --- a/homeassistant/components/amcrest/binary_sensor.py +++ b/homeassistant/components/amcrest/binary_sensor.py @@ -2,6 +2,8 @@ from datetime import timedelta import logging +from amcrest import AmcrestError + from homeassistant.components.binary_sensor import ( BinarySensorDevice, DEVICE_CLASS_MOTION) from homeassistant.const import CONF_NAME, CONF_BINARY_SENSORS @@ -58,8 +60,6 @@ class AmcrestBinarySensor(BinarySensorDevice): def update(self): """Update entity.""" - from amcrest import AmcrestError - _LOGGER.debug('Pulling data from %s binary sensor', self._name) try: diff --git a/homeassistant/components/amcrest/camera.py b/homeassistant/components/amcrest/camera.py index d75475dbb26..3b8c8f38f8b 100644 --- a/homeassistant/components/amcrest/camera.py +++ b/homeassistant/components/amcrest/camera.py @@ -2,6 +2,7 @@ import asyncio import logging +from amcrest import AmcrestError import voluptuous as vol from homeassistant.components.camera import ( @@ -94,19 +95,20 @@ class AmcrestCam(Camera): self._stream_source = device.stream_source self._resolution = device.resolution self._token = self._auth = device.authentication + self._control_light = device.control_light self._is_recording = False self._motion_detection_enabled = None + self._brand = None self._model = None self._audio_enabled = None self._motion_recording_enabled = None self._color_bw = None + self._rtsp_url = None self._snapshot_lock = asyncio.Lock() self._unsub_dispatcher = [] async def async_camera_image(self): """Return a still image response from the camera.""" - from amcrest import AmcrestError - if not self.is_on: _LOGGER.error( 'Attempt to take snaphot when %s camera is off', self.name) @@ -143,7 +145,7 @@ class AmcrestCam(Camera): # streaming via ffmpeg from haffmpeg.camera import CameraMjpeg - streaming_url = self._api.rtsp_url(typeno=self._resolution) + streaming_url = self._rtsp_url stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop) await stream.open_camera( streaming_url, extra_cmd=self._ffmpeg_arguments) @@ -191,7 +193,7 @@ class AmcrestCam(Camera): @property def brand(self): """Return the camera brand.""" - return 'Amcrest' + return self._brand @property def motion_detection_enabled(self): @@ -205,7 +207,7 @@ class AmcrestCam(Camera): async def stream_source(self): """Return the source of the stream.""" - return self._api.rtsp_url(typeno=self._resolution) + return self._rtsp_url @property def is_on(self): @@ -231,9 +233,19 @@ class AmcrestCam(Camera): def update(self): """Update entity status.""" - from amcrest import AmcrestError - _LOGGER.debug('Pulling data from %s camera', self.name) + if self._brand is None: + try: + resp = self._api.vendor_information.strip() + if resp.startswith('vendor='): + self._brand = resp.split('=')[-1] + else: + self._brand = 'unknown' + except AmcrestError as error: + _LOGGER.error( + 'Could not get %s camera brand due to error: %s', + self.name, error) + self._brand = 'unknwown' if self._model is None: try: self._model = self._api.device_type.split('=')[-1].strip() @@ -241,7 +253,7 @@ class AmcrestCam(Camera): _LOGGER.error( 'Could not get %s camera model due to error: %s', self.name, error) - self._model = '' + self._model = 'unknown' try: self.is_streaming = self._api.video_enabled self._is_recording = self._api.record_mode == 'Manual' @@ -251,6 +263,7 @@ class AmcrestCam(Camera): self._motion_recording_enabled = ( self._api.is_record_on_motion_detection()) self._color_bw = _CBW[self._api.day_night_color] + self._rtsp_url = self._api.rtsp_url(typeno=self._resolution) except AmcrestError as error: _LOGGER.error( 'Could not get %s camera attributes due to error: %s', @@ -322,8 +335,6 @@ class AmcrestCam(Camera): def _enable_video_stream(self, enable): """Enable or disable camera video stream.""" - from amcrest import AmcrestError - # Given the way the camera's state is determined by # is_streaming and is_recording, we can't leave # recording on if video stream is being turned off. @@ -338,11 +349,11 @@ class AmcrestCam(Camera): else: self.is_streaming = enable self.schedule_update_ha_state() + if self._control_light: + self._enable_light(self._audio_enabled or self.is_streaming) def _enable_recording(self, enable): """Turn recording on or off.""" - from amcrest import AmcrestError - # Given the way the camera's state is determined by # is_streaming and is_recording, we can't leave # video stream off if recording is being turned on. @@ -362,8 +373,6 @@ class AmcrestCam(Camera): def _enable_motion_detection(self, enable): """Enable or disable motion detection.""" - from amcrest import AmcrestError - try: self._api.motion_detection = str(enable).lower() except AmcrestError as error: @@ -376,8 +385,6 @@ class AmcrestCam(Camera): def _enable_audio(self, enable): """Enable or disable audio stream.""" - from amcrest import AmcrestError - try: self._api.audio_enabled = enable except AmcrestError as error: @@ -387,11 +394,22 @@ class AmcrestCam(Camera): else: self._audio_enabled = enable self.schedule_update_ha_state() + if self._control_light: + self._enable_light(self._audio_enabled or self.is_streaming) + + def _enable_light(self, enable): + """Enable or disable indicator light.""" + try: + self._api.command( + 'configManager.cgi?action=setConfig&LightGlobal[0].Enable={}' + .format(str(enable).lower())) + except AmcrestError as error: + _LOGGER.error( + 'Could not %s %s camera indicator light due to error: %s', + 'enable' if enable else 'disable', self.name, error) def _enable_motion_recording(self, enable): """Enable or disable motion recording.""" - from amcrest import AmcrestError - try: self._api.motion_recording = str(enable).lower() except AmcrestError as error: @@ -404,8 +422,6 @@ class AmcrestCam(Camera): def _goto_preset(self, preset): """Move camera position and zoom to preset.""" - from amcrest import AmcrestError - try: self._api.go_to_preset( action='start', preset_point_number=preset) @@ -416,8 +432,6 @@ class AmcrestCam(Camera): def _set_color_bw(self, cbw): """Set camera color mode.""" - from amcrest import AmcrestError - try: self._api.day_night_color = _CBW.index(cbw) except AmcrestError as error: @@ -430,8 +444,6 @@ class AmcrestCam(Camera): def _start_tour(self, start): """Start camera tour.""" - from amcrest import AmcrestError - try: self._api.tour(start=start) except AmcrestError as error: