Add control of Amcrest indicator light (#23986)

Enable feature by default but allow it to be disabled by "control_light: false" in config.

Get brand from camera instead of assuming Amcrest (since this works with other cameras, too.)

Retrieve RTSP URL in update method instead of in stream_source property and in handle_async_mjpeg_stream method.

Move amcrest imports from methods to global.
This commit is contained in:
Phil Bruckner 2019-05-31 15:41:48 -05:00 committed by Paulus Schoutsen
parent 3c1cdecb88
commit d966e0cfce
3 changed files with 45 additions and 30 deletions

View file

@ -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: