From 934341ea72ab7432a5072fd2e74c051da9682b39 Mon Sep 17 00:00:00 2001 From: Ronnie Roller Date: Sun, 26 Dec 2021 15:38:26 -0800 Subject: [PATCH] Add a switch to enable/disable SmartMotionDetection --- custom_components/dahua/__init__.py | 37 ++++++++++++++---------- custom_components/dahua/client.py | 20 +++++++++++-- custom_components/dahua/switch.py | 44 ++++++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 19 deletions(-) diff --git a/custom_components/dahua/__init__.py b/custom_components/dahua/__init__.py index 6ca9e55..4f7ed0b 100644 --- a/custom_components/dahua/__init__.py +++ b/custom_components/dahua/__init__.py @@ -109,6 +109,7 @@ def __init__(self, hass: HomeAssistant, events: list, address: str, port: int, r self.events: list = events self._supports_coaxial_control = False self._supports_disarming_linkage = False + self._supports_smart_motion_detection = False self._serial_number: str self._profile_mode = "0" self._supports_profile_mode = False @@ -216,6 +217,12 @@ async def _async_update_data(self): except ClientError: self._supports_disarming_linkage = False + try: + await self.client.async_get_smart_motion_detection() + self._supports_smart_motion_detection = True + except ClientError: + self._supports_smart_motion_detection = False + is_doorbell = self.is_doorbell() if not is_doorbell: @@ -260,6 +267,8 @@ async def _async_update_data(self): coros.append(asyncio.ensure_future(self.client.async_get_disarming_linkage())) if self._supports_coaxial_control: coros.append(asyncio.ensure_future(self.client.async_get_coaxial_control_io_status())) + if self._supports_smart_motion_detection: + coros.append(asyncio.ensure_future(self.client.async_get_smart_motion_detection())) # Gather results and update the data map results = await asyncio.gather(*coros) @@ -473,16 +482,12 @@ def supports_security_light(self) -> bool: return "-AS-PV" in self.model or self.model == "AD410" def is_doorbell(self) -> bool: - """ - Returns true if this is a doorbell (VTO) - """ + """ Returns true if this is a doorbell (VTO) """ m = self.model.upper() return m.startswith("VTO") or m.startswith("DHI") or self.is_amcrest_doorbell() def is_amcrest_doorbell(self) -> bool: - """ - Returns true if this is an Amcrest doorbell - """ + """ Returns true if this is an Amcrest doorbell """ return self.model.upper().startswith("AD") def supports_infrared_light(self) -> bool: @@ -500,21 +505,19 @@ def supports_illuminator(self) -> bool: return not self.is_amcrest_doorbell() and "table.Lighting_V2[{0}][0][0].Mode".format(self._channel) in self.data def is_motion_detection_enabled(self) -> bool: - """ - Returns true if motion detection is enabled for the camera - """ + """ Returns true if motion detection is enabled for the camera """ return self.data.get("table.MotionDetect[{0}].Enable".format(self._channel), "").lower() == "true" def is_disarming_linkage_enabled(self) -> bool: - """ - Returns true if disarming linkage is enable - """ + """ Returns true if disarming linkage is enable """ return self.data.get("table.DisableLinkage.Enable", "").lower() == "true" + def is_smart_motion_detection_enabled(self) -> bool: + """ Returns true if smart motion detection is enabled """ + return self.data.get("table.SmartMotionDetect[0].Enable", "").lower() == "true" + def is_siren_on(self) -> bool: - """ - Returns true if the camera siren is on - """ + """ Returns true if the camera siren is on """ return self.data.get("status.status.Speaker", "").lower() == "on" def get_device_name(self) -> str: @@ -598,6 +601,10 @@ def get_max_streams(self) -> int: """Returns the max number of streams supported by the device. All streams might not be enabled though""" return self._max_streams + def supports_smart_motion_detection(self) -> bool: + """ True if smart motion detection is supported""" + return self._supports_smart_motion_detection + async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Handle removal of an entry.""" diff --git a/custom_components/dahua/client.py b/custom_components/dahua/client.py index 0a56e4e..def09a6 100644 --- a/custom_components/dahua/client.py +++ b/custom_components/dahua/client.py @@ -222,14 +222,28 @@ async def async_set_all_ivs_rules(self, channel: int, enabled: bool): return await self.get(url, True) async def async_set_ivs_rule(self, channel: int, index: int, enabled: bool): - """ - Sets and IVS rules to enabled or disabled - """ + """ Sets and IVS rules to enabled or disabled """ url = "/cgi-bin/configManager.cgi?action=setConfig&VideoAnalyseRule[{0}][{1}].Enable={2}".format( channel, index, str(enabled).lower() ) return await self.get(url, True) + async def async_enabled_smart_motion_detection(self, enabled: bool): + """ Enables or disabled smart motion detection """ + url = "/cgi-bin/configManager.cgi?action=setConfig&SmartMotionDetect[0].Enable={0}".format(str(enabled).lower()) + return await self.get(url, True) + + async def async_get_smart_motion_detection(self) -> dict: + """ + Gets the status of smart motion detection. Example output: + table.SmartMotionDetect[0].Enable=true + table.SmartMotionDetect[0].ObjectTypes.Human=true + table.SmartMotionDetect[0].ObjectTypes.Vehicle=false + table.SmartMotionDetect[0].Sensitivity=Middle + """ + url = "/cgi-bin/configManager.cgi?action=getConfig&name=SmartMotionDetect" + return await self.get(url) + async def async_set_lighting_v1(self, channel: int, enabled: bool, brightness: int) -> dict: """ async_get_lighting_v1 will turn the IR light (InfraRed light) on or off """ # on = Manual, off = Off diff --git a/custom_components/dahua/switch.py b/custom_components/dahua/switch.py index 917e713..2912b8e 100644 --- a/custom_components/dahua/switch.py +++ b/custom_components/dahua/switch.py @@ -14,11 +14,15 @@ async def async_setup_entry(hass: HomeAssistant, entry, async_add_devices): coordinator: DahuaDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] # I think most cameras have a motion sensor so we'll blindly add a switch for it - devices = [DahuaMotionDetectionBinarySwitch(coordinator, entry)] + devices = [ + DahuaMotionDetectionBinarySwitch(coordinator, entry), + ] # But only some cams have a siren, very few do actually if coordinator.supports_siren(): devices.append(DahuaSirenBinarySwitch(coordinator, entry)) + if coordinator.supports_smart_motion_detection(): + devices.append(DahuaSmartMotionDetectionBinarySwitch(coordinator, entry)) try: await coordinator.client.async_get_disarming_linkage() @@ -114,6 +118,44 @@ def is_on(self): return self._coordinator.is_disarming_linkage_enabled() +class DahuaSmartMotionDetectionBinarySwitch(DahuaBaseEntity, SwitchEntity): + """Enables or disables the Smart Motion Detection option in the camera""" + + async def async_turn_on(self, **kwargs): # pylint: disable=unused-argument + """Turn on SmartMotionDetect""" + await self._coordinator.client.async_enabled_smart_motion_detection(True) + await self._coordinator.async_refresh() + + async def async_turn_off(self, **kwargs): # pylint: disable=unused-argument + """Turn off SmartMotionDetect""" + await self._coordinator.client.async_enabled_smart_motion_detection(False) + await self._coordinator.async_refresh() + + @property + def name(self): + """Return the name of the switch.""" + return self._coordinator.get_device_name() + " " + "Smart Motion Detection" + + @property + def unique_id(self): + """ + A unique identifier for this entity. Needs to be unique within a platform (ie light.hue). Should not be + configurable by the user or be changeable see + https://developers.home-assistant.io/docs/entity_registry_index/#unique-id-requirements + """ + return self._coordinator.get_serial_number() + "_smart_motion_detection" + + @property + def icon(self): + """Return the icon of this switch.""" + return MOTION_DETECTION_ICON + + @property + def is_on(self): + """ Return true if the switch is on. """ + return self._coordinator.is_smart_motion_detection_enabled() + + class DahuaSirenBinarySwitch(DahuaBaseEntity, SwitchEntity): """dahua siren switch class. Used to enable or disable camera built in sirens"""