From 8d9180000c5d137763d1d639aeddc367f789d033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Nyi=CC=81ri?= Date: Thu, 28 Nov 2024 12:49:09 +0100 Subject: [PATCH 1/6] Add: Switches for alert event types --- README.md | 2 +- custom_components/tapo_control/const.py | 2 +- custom_components/tapo_control/manifest.json | 4 +- custom_components/tapo_control/switch.py | 59 ++++++++++++++++++++ custom_components/tapo_control/utils.py | 6 ++ 5 files changed, 69 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f29b70d..2372bd3 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ This custom component creates: - Binary sensor for motion after the motion is detected for the first time - Light entity, if the camera supports a floodlight switch - Buttons for Calibrate, Format, Manual Alarm start & stop, Moving the camera, Reboot and syncing time -- Switch entities for Auto track, Flip setting, LED Indicator, Lens Distortion Correction, (Rich) Notifications, Recording, Microphone Mute, Microphone Noise Cancelling, Automatically Upgrade Firmware, HDR mode and Privacy mode +- Switch entities for Auto track, Flip setting, LED Indicator, Lens Distortion Correction, (Rich) Notifications, Recording, Microphone Mute, Microphone Noise Cancelling, Automatically Upgrade Firmware, HDR mode, Alarm Trigger Event types and Privacy mode - Select entities for Automatic Alarm, Light Frequency, Motion Detection, Night Vision, Spotlight Intensity, Alarm Type and Move to Preset - Number entity for Movement Angle, Speaker Volume, Microphone Volume, Spotlight Intensity, Siren Volume, Siren Duration and Motion Detection Digital Sensitivity - Media Source for browsing and playing recordings stored on camera diff --git a/custom_components/tapo_control/const.py b/custom_components/tapo_control/const.py index b6b0722..d8efc97 100644 --- a/custom_components/tapo_control/const.py +++ b/custom_components/tapo_control/const.py @@ -5,7 +5,7 @@ from homeassistant.helpers import config_validation as cv -PYTAPO_REQUIRED_VERSION = "3.3.32" +PYTAPO_REQUIRED_VERSION = "3.3.35" DOMAIN = "tapo_control" BRAND = "TP-Link" ALARM_MODE = "alarm_mode" diff --git a/custom_components/tapo_control/manifest.json b/custom_components/tapo_control/manifest.json index 3fa8ba3..16e1d6b 100644 --- a/custom_components/tapo_control/manifest.json +++ b/custom_components/tapo_control/manifest.json @@ -6,9 +6,9 @@ "codeowners": [ "@JurajNyiri" ], - "version": "5.8.0", + "version": "5.8.2", "requirements": [ - "pytapo==3.3.32" + "pytapo==3.3.35" ], "dependencies": [ "ffmpeg", diff --git a/custom_components/tapo_control/switch.py b/custom_components/tapo_control/switch.py index a8b3e9f..7affb38 100644 --- a/custom_components/tapo_control/switch.py +++ b/custom_components/tapo_control/switch.py @@ -35,6 +35,17 @@ async def setupEntities(entry): await entry_storage.async_save({ENABLE_MEDIA_SYNC: False}) entry_stored_data = await entry_storage.async_load() + if ( + "alert_event_types" in entry["camData"] + and entry["camData"]["alert_event_types"] + ): + for alertEventType in entry["camData"]["alert_event_types"]: + switches.append( + TapoAlarmEventTypeSwitch( + entry, hass, config_entry, alertEventType["name"] + ) + ) + tapoEnableMediaSyncSwitch = TapoEnableMediaSyncSwitch( entry, hass, @@ -535,6 +546,54 @@ def updateTapo(self, camData): self._attr_state = "on" if self._attr_is_on else "off" +class TapoAlarmEventTypeSwitch(TapoSwitchEntity): + def __init__(self, entry: dict, hass: HomeAssistant, config_entry, eventType: str): + self.eventType = eventType + TapoSwitchEntity.__init__( + self, + f"Trigger alarm on {eventType}", + entry, + hass, + config_entry, + "mdi:exclamation", + ) + + async def async_update(self) -> None: + await self._coordinator.async_request_refresh() + + async def async_turn_on(self) -> None: + result = await self._hass.async_add_executor_job( + self._controller.setAlertEventType, + self.eventType, + True, + ) + if "error_code" not in result or result["error_code"] == 0: + self._attr_state = "on" + self.async_write_ha_state() + await self._coordinator.async_request_refresh() + + async def async_turn_off(self) -> None: + result = await self._hass.async_add_executor_job( + self._controller.setAlertEventType, + self.eventType, + False, + ) + if "error_code" not in result or result["error_code"] == 0: + self._attr_state = "on" + self.async_write_ha_state() + await self._coordinator.async_request_refresh() + + def updateTapo(self, camData): + if not camData: + self._attr_state = STATE_UNAVAILABLE + else: + if "alert_event_types" in camData and camData["alert_event_types"]: + for alertEventType in camData["alert_event_types"]: + if alertEventType["name"] == self.eventType: + self._attr_is_on = alertEventType["enabled"] == "on" + self._attr_state = "on" if self._attr_is_on else "off" + + class TapoLensDistortionCorrectionSwitch(TapoSwitchEntity): def __init__(self, entry: dict, hass: HomeAssistant, config_entry): TapoSwitchEntity.__init__( diff --git a/custom_components/tapo_control/utils.py b/custom_components/tapo_control/utils.py index a2f3004..de919a5 100644 --- a/custom_components/tapo_control/utils.py +++ b/custom_components/tapo_control/utils.py @@ -741,6 +741,12 @@ async def getCamData(hass, controller): timezone_timezone = None camData["timezone_timezone"] = timezone_timezone + try: + alert_event_types = data["getAlertEventType"][0]["msg_alarm"]["msg_alarm_type"] + except Exception: + alert_event_types = None + camData["alert_event_types"] = alert_event_types + try: timezone_zone_id = data["getTimezone"][0]["system"]["basic"]["zone_id"] except Exception: From 47087ad784187148b8b58d85282bcc9fdccb9bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Nyi=CC=81ri?= Date: Thu, 28 Nov 2024 13:07:33 +0100 Subject: [PATCH 2/6] Add: Manual Alarm Start / Stop supports triggering alarm via test sounds if native not available --- custom_components/tapo_control/button.py | 81 +++++++++++++++++++++++- custom_components/tapo_control/siren.py | 23 +------ custom_components/tapo_control/utils.py | 21 ++++++ 3 files changed, 100 insertions(+), 25 deletions(-) diff --git a/custom_components/tapo_control/button.py b/custom_components/tapo_control/button.py index 33e1695..0241d70 100644 --- a/custom_components/tapo_control/button.py +++ b/custom_components/tapo_control/button.py @@ -8,7 +8,7 @@ from .const import DOMAIN, LOGGER from .tapo.entities import TapoButtonEntity -from .utils import syncTime, check_and_create +from .utils import syncTime, check_and_create, result_has_error async def async_setup_entry( @@ -118,11 +118,49 @@ def __init__(self, entry: dict, hass: HomeAssistant, config_entry): ) async def async_press(self) -> None: - await self._hass.async_add_executor_job(self._controller.startManualAlarm) + result = False + result2 = False + try: + result = await self._hass.async_add_executor_job( + self._controller.startManualAlarm, + ) + except Exception as e: + LOGGER.debug(e) + + try: + result2 = await self._hass.async_add_executor_job( + self._controller.setSirenStatus, True + ) + except Exception as e: + LOGGER.debug(e) + + if result_has_error(result) and result_has_error(result2): + if self.sirenType is not None: + result3 = await self._hass.async_add_executor_job( + self._controller.testUsrDefAudio, self.sirenType, True + ) + if result_has_error(result3): + self._attr_available = False + raise Exception("Camera does not support triggering the siren.") + else: + raise Exception("Camera does not support triggering the siren.") + + def updateTapo(self, camData): + if not camData or camData["privacy_mode"] == "on": + self.camData = STATE_UNAVAILABLE + else: + if ( + "alarm_config" in camData + and camData["alarm_config"] + and "siren_type" in camData["alarm_config"] + and camData["alarm_config"]["siren_type"] + ): + self.sirenType = camData["alarm_config"]["siren_type"] class TapoStopManualAlarmButton(TapoButtonEntity): def __init__(self, entry: dict, hass: HomeAssistant, config_entry): + self.sirenType = None TapoButtonEntity.__init__( self, "Manual Alarm Stop", @@ -132,7 +170,44 @@ def __init__(self, entry: dict, hass: HomeAssistant, config_entry): ) async def async_press(self) -> None: - await self._hass.async_add_executor_job(self._controller.stopManualAlarm) + result = False + result2 = False + try: + result = await self._hass.async_add_executor_job( + self._controller.stopManualAlarm, + ) + except Exception as e: + LOGGER.debug(e) + + try: + result2 = await self._hass.async_add_executor_job( + self._controller.setSirenStatus, False + ) + except Exception as e: + LOGGER.debug(e) + + if result_has_error(result) and result_has_error(result2): + if self.sirenType is not None: + result3 = await self._hass.async_add_executor_job( + self._controller.testUsrDefAudio, self.sirenType, False + ) + if result_has_error(result3): + self._attr_available = False + raise Exception("Camera does not support triggering the siren.") + else: + raise Exception("Camera does not support triggering the siren.") + + def updateTapo(self, camData): + if not camData or camData["privacy_mode"] == "on": + self.camData = STATE_UNAVAILABLE + else: + if ( + "alarm_config" in camData + and camData["alarm_config"] + and "siren_type" in camData["alarm_config"] + and camData["alarm_config"]["siren_type"] + ): + self.sirenType = camData["alarm_config"]["siren_type"] class TapoCalibrateButton(TapoButtonEntity): diff --git a/custom_components/tapo_control/siren.py b/custom_components/tapo_control/siren.py index 6c310a6..b4b0faa 100644 --- a/custom_components/tapo_control/siren.py +++ b/custom_components/tapo_control/siren.py @@ -8,7 +8,7 @@ from .const import DOMAIN, LOGGER from .tapo.entities import TapoEntity -from .utils import check_and_create +from .utils import check_and_create, result_has_error async def async_setup_entry( @@ -164,24 +164,3 @@ def updateTapo(self, camData): else: self._attr_available = True self._is_on = camData["alarm_status"] == "on" - - -def result_has_error(result): - if ( - result is not False - and "result" in result - and "responses" in result["result"] - and any( - map( - lambda x: "error_code" not in x or x["error_code"] == 0, - result["result"]["responses"], - ) - ) - ): - return False - if result is not False and ( - "error_code" not in result or result["error_code"] == 0 - ): - return False - else: - return True diff --git a/custom_components/tapo_control/utils.py b/custom_components/tapo_control/utils.py index de919a5..3538694 100644 --- a/custom_components/tapo_control/utils.py +++ b/custom_components/tapo_control/utils.py @@ -615,6 +615,27 @@ async def isRtspStreamWorking(hass, host, username, password, full_url=""): return not image == b"" +def result_has_error(result): + if ( + result is not False + and "result" in result + and "responses" in result["result"] + and any( + map( + lambda x: "error_code" not in x or x["error_code"] == 0, + result["result"]["responses"], + ) + ) + ): + return False + if result is not False and ( + "error_code" not in result or result["error_code"] == 0 + ): + return False + else: + return True + + async def initOnvifEvents(hass, host, username, password): device = ONVIFCamera( host, From 22c725356364029aea85f91d46a9a551cf88fed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Nyi=CC=81ri?= Date: Thu, 28 Nov 2024 13:55:48 +0100 Subject: [PATCH 3/6] wip --- custom_components/tapo_control/button.py | 25 ++++++---- custom_components/tapo_control/const.py | 2 +- custom_components/tapo_control/manifest.json | 2 +- custom_components/tapo_control/select.py | 52 +++++++++++++++++++- custom_components/tapo_control/utils.py | 48 ++++++++++-------- 5 files changed, 96 insertions(+), 33 deletions(-) diff --git a/custom_components/tapo_control/button.py b/custom_components/tapo_control/button.py index 0241d70..396a8c2 100644 --- a/custom_components/tapo_control/button.py +++ b/custom_components/tapo_control/button.py @@ -136,11 +136,13 @@ async def async_press(self) -> None: if result_has_error(result) and result_has_error(result2): if self.sirenType is not None: - result3 = await self._hass.async_add_executor_job( - self._controller.testUsrDefAudio, self.sirenType, True - ) - if result_has_error(result3): - self._attr_available = False + try: + result3 = await self._hass.async_add_executor_job( + self._controller.testUsrDefAudio, self.sirenType, True + ) + if result_has_error(result3): + raise Exception("Camera does not support triggering the siren.") + except Exception: raise Exception("Camera does not support triggering the siren.") else: raise Exception("Camera does not support triggering the siren.") @@ -188,11 +190,14 @@ async def async_press(self) -> None: if result_has_error(result) and result_has_error(result2): if self.sirenType is not None: - result3 = await self._hass.async_add_executor_job( - self._controller.testUsrDefAudio, self.sirenType, False - ) - if result_has_error(result3): - self._attr_available = False + try: + result3 = await self._hass.async_add_executor_job( + self._controller.testUsrDefAudio, self.sirenType, False + ) + if result_has_error(result3): + self._attr_available = False + raise Exception("Camera does not support triggering the siren.") + except Exception: raise Exception("Camera does not support triggering the siren.") else: raise Exception("Camera does not support triggering the siren.") diff --git a/custom_components/tapo_control/const.py b/custom_components/tapo_control/const.py index d8efc97..3691636 100644 --- a/custom_components/tapo_control/const.py +++ b/custom_components/tapo_control/const.py @@ -5,7 +5,7 @@ from homeassistant.helpers import config_validation as cv -PYTAPO_REQUIRED_VERSION = "3.3.35" +PYTAPO_REQUIRED_VERSION = "3.3.36" DOMAIN = "tapo_control" BRAND = "TP-Link" ALARM_MODE = "alarm_mode" diff --git a/custom_components/tapo_control/manifest.json b/custom_components/tapo_control/manifest.json index 16e1d6b..8b600a4 100644 --- a/custom_components/tapo_control/manifest.json +++ b/custom_components/tapo_control/manifest.json @@ -8,7 +8,7 @@ ], "version": "5.8.2", "requirements": [ - "pytapo==3.3.35" + "pytapo==3.3.36" ], "dependencies": [ "ffmpeg", diff --git a/custom_components/tapo_control/select.py b/custom_components/tapo_control/select.py index fc6a7b5..4d24ff8 100644 --- a/custom_components/tapo_control/select.py +++ b/custom_components/tapo_control/select.py @@ -87,8 +87,11 @@ async def setupEntities(entry): entry, hass, TapoSirenTypeSelect, "getSirenTypeList", config_entry ) if tapoSirenTypeSelect: - LOGGER.debug("Adding tapoSirenTypeSelect...") + LOGGER.warning("Adding tapoSirenTypeSelect...") selects.append(tapoSirenTypeSelect) + else: + LOGGER.warning("Adding tapoBasicSirenTypeSelect...") + # selects.append(TapoBasicSirenTypeSelect(entry, hass, config_entry)) tapoAlertTypeSelect = await check_and_create( entry, hass, TapoAlertTypeSelect, "getAlertTypeList", config_entry @@ -1044,6 +1047,53 @@ def entity_category(self): return None +class TapoBasicSirenTypeSelect(TapoSelectEntity): + def __init__(self, entry: dict, hass: HomeAssistant, config_entry): + self._attr_options = entry["camData"]["alarm_siren_type_list"] + self._attr_current_option = entry["camData"]["alarm_config"]["siren_type"] + self.hub = entry["camData"]["alarm_is_hubSiren"] + TapoSelectEntity.__init__( + self, + "Siren Type", + entry, + hass, + config_entry, + "mdi:home-sound-in-outline", + ) + + def updateTapo(self, camData): + if not camData: + self._attr_state = STATE_UNAVAILABLE + else: + if "siren_type" in camData["alarm_config"]: + self._attr_current_option = camData["alarm_config"]["siren_type"] + else: + self._attr_state = STATE_UNAVAILABLE + + self._attr_state = self._attr_current_option + LOGGER.debug("Updating TapoHubSirenTypeSelect to: " + str(self._attr_state)) + + async def async_select_option(self, option: str) -> None: + if self.hub: + result = await self.hass.async_add_executor_job( + self._controller.setHubSirenConfig, None, option + ) + else: + result = await self.hass.async_add_executor_job( + self._controller.executeFunction, + "setAlarmConfig", + { + "msg_alarm": { + "siren_type": option, + } + }, + ) + if "error_code" not in result or result["error_code"] == 0: + self._attr_state = option + self.async_write_ha_state() + await self._coordinator.async_request_refresh() + + class TapoSirenTypeSelect(TapoSelectEntity): def __init__(self, entry: dict, hass: HomeAssistant, config_entry): self._attr_options = entry["camData"]["alarm_siren_type_list"] diff --git a/custom_components/tapo_control/utils.py b/custom_components/tapo_control/utils.py index 3538694..7445fb2 100644 --- a/custom_components/tapo_control/utils.py +++ b/custom_components/tapo_control/utils.py @@ -1171,33 +1171,41 @@ async def getCamData(hass, controller): except Exception as err: LOGGER.error(f"getSirenTypeList unexpected error {err=}, {type(err)=}") + if len(alarmSirenTypeList) == 0: + # Some cameras have hardcoded 0 and 1 values (Siren, Tone) + alarmSirenTypeList.append("Siren") + alarmSirenTypeList.append("Tone") + alarm_user_sounds = None try: - if ( - data["getAlertConfig"][0] is not False - and "msg_alarm" in data["getAlertConfig"][0] - and "usr_def_audio" in data["getAlertConfig"][0]["msg_alarm"] - ): - alarm_user_sounds = [] - for alarm_sound in data["getAlertConfig"][0]["msg_alarm"]["usr_def_audio"]: - first_key = next(iter(alarm_sound)) - first_value = alarm_sound[first_key] - alarm_user_sounds.append(first_value) + for alertConfig in data["getAlertConfig"]: + if ( + alertConfig is not False + and "msg_alarm" in alertConfig + and "usr_def_audio" in alertConfig["msg_alarm"] + and (alarm_user_sounds is None or len(alarm_user_sounds) == 0) + ): + alarm_user_sounds = [] + for alarm_sound in alertConfig["msg_alarm"]["usr_def_audio"]: + first_key = next(iter(alarm_sound)) + first_value = alarm_sound[first_key] + alarm_user_sounds.append(first_value) except Exception: alarm_user_sounds = None alarm_user_start_id = None try: - if ( - data["getAlertConfig"][0] is not False - and "msg_alarm" in data["getAlertConfig"][0] - and "capability" in data["getAlertConfig"][0]["msg_alarm"] - and "usr_def_start_file_id" - in data["getAlertConfig"][0]["msg_alarm"]["capability"] - ): - alarm_user_start_id = data["getAlertConfig"][0]["msg_alarm"]["capability"][ - "usr_def_start_file_id" - ] + for alertConfig in data["getAlertConfig"]: + if ( + alertConfig is not False + and "msg_alarm" in alertConfig + and "capability" in alertConfig["msg_alarm"] + and "usr_def_start_file_id" in alertConfig["msg_alarm"]["capability"] + and alarm_user_start_id is None + ): + alarm_user_start_id = alertConfig["msg_alarm"]["capability"][ + "usr_def_start_file_id" + ] except Exception: alarm_user_start_id = None camData["alarm_user_start_id"] = alarm_user_start_id From 48988538e5eb612b6c7968d829b30b45c9bb4b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Nyi=CC=81ri?= Date: Thu, 28 Nov 2024 14:17:54 +0100 Subject: [PATCH 4/6] Add: Siren Type for more cameras --- custom_components/tapo_control/select.py | 63 ++++-------------------- 1 file changed, 9 insertions(+), 54 deletions(-) diff --git a/custom_components/tapo_control/select.py b/custom_components/tapo_control/select.py index 4d24ff8..cd4da49 100644 --- a/custom_components/tapo_control/select.py +++ b/custom_components/tapo_control/select.py @@ -87,18 +87,18 @@ async def setupEntities(entry): entry, hass, TapoSirenTypeSelect, "getSirenTypeList", config_entry ) if tapoSirenTypeSelect: - LOGGER.warning("Adding tapoSirenTypeSelect...") + LOGGER.debug("Adding tapoSirenTypeSelect...") selects.append(tapoSirenTypeSelect) - else: - LOGGER.warning("Adding tapoBasicSirenTypeSelect...") - # selects.append(TapoBasicSirenTypeSelect(entry, hass, config_entry)) tapoAlertTypeSelect = await check_and_create( entry, hass, TapoAlertTypeSelect, "getAlertTypeList", config_entry ) if tapoAlertTypeSelect: - LOGGER.debug("Adding tapoAlertTypeSelect...") + LOGGER.warning("Adding tapoAlertTypeSelect...") selects.append(tapoAlertTypeSelect) + else: + LOGGER.warning("Adding tapoAlertTypeSelect with start ID 0...") + selects.append(TapoAlertTypeSelect(entry, hass, config_entry, 0)) tapoMotionDetectionSelect = await check_and_create( entry, hass, TapoMotionDetectionSelect, "getMotionDetection", config_entry @@ -1047,53 +1047,6 @@ def entity_category(self): return None -class TapoBasicSirenTypeSelect(TapoSelectEntity): - def __init__(self, entry: dict, hass: HomeAssistant, config_entry): - self._attr_options = entry["camData"]["alarm_siren_type_list"] - self._attr_current_option = entry["camData"]["alarm_config"]["siren_type"] - self.hub = entry["camData"]["alarm_is_hubSiren"] - TapoSelectEntity.__init__( - self, - "Siren Type", - entry, - hass, - config_entry, - "mdi:home-sound-in-outline", - ) - - def updateTapo(self, camData): - if not camData: - self._attr_state = STATE_UNAVAILABLE - else: - if "siren_type" in camData["alarm_config"]: - self._attr_current_option = camData["alarm_config"]["siren_type"] - else: - self._attr_state = STATE_UNAVAILABLE - - self._attr_state = self._attr_current_option - LOGGER.debug("Updating TapoHubSirenTypeSelect to: " + str(self._attr_state)) - - async def async_select_option(self, option: str) -> None: - if self.hub: - result = await self.hass.async_add_executor_job( - self._controller.setHubSirenConfig, None, option - ) - else: - result = await self.hass.async_add_executor_job( - self._controller.executeFunction, - "setAlarmConfig", - { - "msg_alarm": { - "siren_type": option, - } - }, - ) - if "error_code" not in result or result["error_code"] == 0: - self._attr_state = option - self.async_write_ha_state() - await self._coordinator.async_request_refresh() - - class TapoSirenTypeSelect(TapoSelectEntity): def __init__(self, entry: dict, hass: HomeAssistant, config_entry): self._attr_options = entry["camData"]["alarm_siren_type_list"] @@ -1142,9 +1095,9 @@ async def async_select_option(self, option: str) -> None: class TapoAlertTypeSelect(TapoSelectEntity): - def __init__(self, entry: dict, hass: HomeAssistant, config_entry): + def __init__(self, entry: dict, hass: HomeAssistant, config_entry, startID): self.hub = entry["camData"]["alarm_is_hubSiren"] - self.startID = 10 + self.startID = startID self.alarm_siren_type_list = entry["camData"]["alarm_siren_type_list"] self.typeOfAlarm = entry["camData"]["alarm_config"]["typeOfAlarm"] @@ -1177,6 +1130,8 @@ def updateTapo(self, camData): currentSirenType = int(camData["alarm_config"]["siren_type"]) if currentSirenType == 0: self._attr_current_option = camData["alarm_siren_type_list"][0] + elif currentSirenType == 1: + self._attr_current_option = camData["alarm_siren_type_list"][1] elif currentSirenType < self.startID: # on these cameras, the 0 is the first entry, but then it starts from 3 # and it has 3 and 4 values, assuming -2 for the rest From 72623839ee405f07b5a8487150e95835ec84ac8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Nyi=CC=81ri?= Date: Thu, 28 Nov 2024 14:19:44 +0100 Subject: [PATCH 5/6] Update: Cleanup --- custom_components/tapo_control/select.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/custom_components/tapo_control/select.py b/custom_components/tapo_control/select.py index cd4da49..39a64a9 100644 --- a/custom_components/tapo_control/select.py +++ b/custom_components/tapo_control/select.py @@ -94,10 +94,10 @@ async def setupEntities(entry): entry, hass, TapoAlertTypeSelect, "getAlertTypeList", config_entry ) if tapoAlertTypeSelect: - LOGGER.warning("Adding tapoAlertTypeSelect...") + LOGGER.debug("Adding tapoAlertTypeSelect...") selects.append(tapoAlertTypeSelect) else: - LOGGER.warning("Adding tapoAlertTypeSelect with start ID 0...") + LOGGER.debug("Adding tapoAlertTypeSelect with start ID 0...") selects.append(TapoAlertTypeSelect(entry, hass, config_entry, 0)) tapoMotionDetectionSelect = await check_and_create( @@ -1095,7 +1095,7 @@ async def async_select_option(self, option: str) -> None: class TapoAlertTypeSelect(TapoSelectEntity): - def __init__(self, entry: dict, hass: HomeAssistant, config_entry, startID): + def __init__(self, entry: dict, hass: HomeAssistant, config_entry, startID=10): self.hub = entry["camData"]["alarm_is_hubSiren"] self.startID = startID self.alarm_siren_type_list = entry["camData"]["alarm_siren_type_list"] From 972a783c364d29d3de04419f8dec2cf2f64299d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Nyi=CC=81ri?= Date: Thu, 28 Nov 2024 14:32:37 +0100 Subject: [PATCH 6/6] Fix: Siren Type not working on really old cameras --- custom_components/tapo_control/select.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/custom_components/tapo_control/select.py b/custom_components/tapo_control/select.py index 39a64a9..bcf4e0c 100644 --- a/custom_components/tapo_control/select.py +++ b/custom_components/tapo_control/select.py @@ -1119,11 +1119,12 @@ def updateTapo(self, camData): else: self._attr_options = camData["alarm_siren_type_list"] self.user_sounds = {} - for user_sound in camData["alarm_user_sounds"]: - if "name" in user_sound: - self._attr_options.append(user_sound["name"]) - if "id" in user_sound: - self.user_sounds[user_sound["id"]] = user_sound["name"] + if camData["alarm_user_sounds"] is not None: + for user_sound in camData["alarm_user_sounds"]: + if "name" in user_sound: + self._attr_options.append(user_sound["name"]) + if "id" in user_sound: + self.user_sounds[user_sound["id"]] = user_sound["name"] self.alarm_enabled = camData["alarm_config"]["automatic"] == "on" self.alarm_mode = camData["alarm_config"]["mode"]