diff --git a/custom_components/dahua/__init__.py b/custom_components/dahua/__init__.py index 9a6b2f0..fc4a4e0 100644 --- a/custom_components/dahua/__init__.py +++ b/custom_components/dahua/__init__.py @@ -114,6 +114,9 @@ def __init__(self, hass: HomeAssistant, events: list, address: str, port: int, r self._profile_mode = "0" self._supports_profile_mode = False + # TODO: Support multiple channels + self._channel = 0 + # This is the name for the device given by the user during setup self._name = name @@ -224,7 +227,7 @@ async def _async_update_data(self): # Figure out which APIs we need to call and then fan out and gather the results coros = [ - asyncio.ensure_future(self.client.async_common_config(self._profile_mode)), + asyncio.ensure_future(self.client.async_common_config(self._channel, self._profile_mode)), ] if self._supports_disarming_linkage: coros.append(asyncio.ensure_future(self.client.async_get_disarming_linkage())) @@ -493,6 +496,10 @@ def get_profile_mode(self) -> str: # profile_mode 0=day, 1=night, 2=scene return self._profile_mode + def get_channel(self) -> int: + """returns the channel index of this camera. 0 based. Channel index 0 is channel number 1""" + return self._channel + async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Handle removal of an entry.""" diff --git a/custom_components/dahua/camera.py b/custom_components/dahua/camera.py index 9bdf7a2..82135cf 100644 --- a/custom_components/dahua/camera.py +++ b/custom_components/dahua/camera.py @@ -35,10 +35,6 @@ SERVICE_ENABLE_ALL_IVS_RULES = "enable_all_ivs_rules" SERVICE_ENABLE_IVS_RULE = "enable_ivs_rule" -# For now we'll only support 1 channel. I don't have any cams where I can test a second channel. -# I'm not really sure what channel 2 means anyways, it doesn't seem to be the substream. -CHANNEL = 1 - async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entities): """Add a Dahua IP camera from a config entry.""" @@ -89,7 +85,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie platform.async_register_entity_service( SERVICE_ENABLE_CHANNEL_TITLE, { - vol.Required("channel", default=0): int, vol.Required("enabled", default=True): bool, }, "async_set_enable_channel_title" @@ -98,7 +93,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie platform.async_register_entity_service( SERVICE_ENABLE_TIME_OVERLay, { - vol.Required("channel", default=0): int, vol.Required("enabled", default=True): bool, }, "async_set_enable_time_overlay" @@ -107,7 +101,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie platform.async_register_entity_service( SERVICE_ENABLE_TEXT_OVERLAY, { - vol.Required("channel", default=0): int, vol.Required("group", default=1): int, vol.Required("enabled", default=False): bool, }, @@ -117,7 +110,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie platform.async_register_entity_service( SERVICE_ENABLE_CUSTOM_OVERLAY, { - vol.Required("channel", default=0): int, vol.Required("group", default=0): int, vol.Required("enabled", default=False): bool, }, @@ -127,7 +119,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie platform.async_register_entity_service( SERVICE_ENABLE_ALL_IVS_RULES, { - vol.Required("channel", default=0): int, vol.Required("enabled", default=True): bool, }, "async_set_enable_all_ivs_rules" @@ -136,7 +127,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie platform.async_register_entity_service( SERVICE_ENABLE_IVS_RULE, { - vol.Required("channel", default=0): int, vol.Required("index", default=1): int, vol.Required("enabled", default=True): bool, }, @@ -146,7 +136,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie platform.async_register_entity_service( SERVICE_SET_CHANNEL_TITLE, { - vol.Required("channel", default=0): int, vol.Optional("text1", default=""): str, vol.Optional("text2", default=""): str, }, @@ -155,7 +144,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie platform.async_register_entity_service( SERVICE_SET_TEXT_OVERLAY, { - vol.Required("channel", default=0): int, vol.Required("group", default=0): int, vol.Optional("text1", default=""): str, vol.Optional("text2", default=""): str, @@ -168,7 +156,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie platform.async_register_entity_service( SERVICE_SET_CUSTOM_OVERLAY, { - vol.Required("channel", default=0): int, vol.Required("group", default=0): int, vol.Optional("text1", default=""): str, vol.Optional("text2", default=""): str, @@ -191,7 +178,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie SERVICE_SET_INFRARED_MODE, { vol.Required("mode"): vol.In(["On", "on", "Off", "off", "Auto", "auto"]), - vol.Optional('brightness', default=100): vol.All(vol.Coerce(int), vol.Range(min=0, max=100)) + vol.Optional('brightness', default=100): vol.All(vol.Coerce(int), vol.Range(min=0, max=100)), }, "async_set_infrared_mode" ) @@ -206,12 +193,13 @@ def __init__(self, coordinator: DahuaDataUpdateCoordinator, stream_index: int, c Camera.__init__(self) name = coordinator.client.to_stream_name(stream_index) - self.coordinator = coordinator + channel = coordinator.get_channel() + self._coordinator = coordinator self._name = "{0} {1}".format(config_entry.title, name) self._unique_id = coordinator.get_serial_number() + "_" + name self._stream_index = stream_index self._motion_status = False - self._stream_source = coordinator.client.get_rtsp_stream_url(CHANNEL, stream_index) + self._stream_source = coordinator.client.get_rtsp_stream_url(channel + 1, stream_index) @property def unique_id(self): @@ -221,7 +209,8 @@ def unique_id(self): async def async_camera_image(self): """Return a still image response from the camera.""" # Send the request to snap a picture and return raw jpg data - return await self.coordinator.client.async_get_snapshot(CHANNEL) + channel = self._coordinator.get_channel() + return await self._coordinator.client.async_get_snapshot(channel + 1) @property def supported_features(self): @@ -235,21 +224,23 @@ async def stream_source(self): @property def motion_detection_enabled(self): """Camera Motion Detection Status.""" - return self.coordinator.is_motion_detection_enabled() + return self._coordinator.is_motion_detection_enabled() async def async_enable_motion_detection(self): """Enable motion detection in camera.""" try: - await self.coordinator.client.enable_motion_detection(True) - await self.coordinator.async_refresh() + channel = self._coordinator.get_channel() + await self._coordinator.client.enable_motion_detection(channel, True) + await self._coordinator.async_refresh() except TypeError: _LOGGER.debug("Failed enabling motion detection on '%s'. Is it supported by the device?", self._name) async def async_disable_motion_detection(self): """Disable motion detection.""" try: - await self.coordinator.client.enable_motion_detection(False) - await self.coordinator.async_refresh() + channel = self._coordinator.get_channel() + await self._coordinator.client.enable_motion_detection(channel, False) + await self._coordinator.async_refresh() except TypeError: _LOGGER.debug("Failed disabling motion detection on '%s'. Is it supported by the device?", self._name) @@ -260,51 +251,63 @@ def name(self): async def async_set_infrared_mode(self, mode: str, brightness: int): """ Handles the service call from SERVICE_SET_INFRARED_MODE to set infrared mode and brightness """ - await self.coordinator.client.async_set_lighting_v1_mode(mode, brightness) - await self.coordinator.async_refresh() + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_lighting_v1_mode(channel, mode, brightness) + await self._coordinator.async_refresh() async def async_set_record_mode(self, mode: str): """ Handles the service call from SERVICE_SET_RECORD_MODE to set the record mode """ - await self.coordinator.client.async_set_record_mode(mode) - await self.coordinator.async_refresh() + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_record_mode(channel, mode) + await self._coordinator.async_refresh() async def async_set_video_profile_mode(self, mode: str): """ Handles the service call from SERVICE_SET_VIDEO_PROFILE_MODE to set profile mode to day/night """ - await self.coordinator.client.async_set_video_profile_mode(mode) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_video_profile_mode(channel, mode) - async def async_set_enable_channel_title(self, channel: int, enabled: bool): + async def async_set_enable_channel_title(self, enabled: bool): """ Handles the service call from SERVICE_ENABLE_CHANNEL_TITLE """ - await self.coordinator.client.async_enable_channel_title(channel, enabled) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_enable_channel_title(channel, enabled) - async def async_set_enable_time_overlay(self, channel: int, enabled: bool): + async def async_set_enable_time_overlay(self, enabled: bool): """ Handles the service call from SERVICE_ENABLE_TIME_OVERLAY """ - await self.coordinator.client.async_enable_time_overlay(channel, enabled) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_enable_time_overlay(channel, enabled) - async def async_set_enable_text_overlay(self, channel: int, group: int, enabled: bool): + async def async_set_enable_text_overlay(self, group: int, enabled: bool): """ Handles the service call from SERVICE_ENABLE_TEXT_OVERLAY """ - await self.coordinator.client.async_enable_text_overlay(channel, group, enabled) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_enable_text_overlay(channel, group, enabled) - async def async_set_enable_custom_overlay(self, channel: int, group: int, enabled: bool): + async def async_set_enable_custom_overlay(self, group: int, enabled: bool): """ Handles the service call from SERVICE_ENABLE_CUSTOM_OVERLAY """ - await self.coordinator.client.async_enable_custom_overlay(channel, group, enabled) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_enable_custom_overlay(channel, group, enabled) - async def async_set_enable_all_ivs_rules(self, channel: int, enabled: bool): + async def async_set_enable_all_ivs_rules(self, enabled: bool): """ Handles the service call from SERVICE_ENABLE_ALL_IVS_RULES """ - await self.coordinator.client.async_set_all_ivs_rules(channel, enabled) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_all_ivs_rules(channel, enabled) - async def async_enable_ivs_rule(self, channel: int, index: int, enabled: bool): + async def async_enable_ivs_rule(self, index: int, enabled: bool): """ Handles the service call from SERVICE_ENABLE_IVS_RULE """ - await self.coordinator.client.async_set_ivs_rule(channel, index, enabled) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_ivs_rule(channel, index, enabled) - async def async_set_service_set_channel_title(self, channel: int, text1: str, text2: str): + async def async_set_service_set_channel_title(self, text1: str, text2: str): """ Handles the service call from SERVICE_SET_CHANNEL_TITLE to set profile mode to day/night """ - await self.coordinator.client.async_set_service_set_channel_title(channel, text1, text2) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_service_set_channel_title(channel, text1, text2) - async def async_set_service_set_text_overlay(self, channel: int, group: int, text1: str, text2: str, text3: str, + async def async_set_service_set_text_overlay(self, group: int, text1: str, text2: str, text3: str, text4: str): """ Handles the service call from SERVICE_SET_TEXT_OVERLAY to set profile mode to day/night """ - await self.coordinator.client.async_set_service_set_text_overlay(channel, group, text1, text2, text3, text4) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_service_set_text_overlay(channel, group, text1, text2, text3, text4) - async def async_set_service_set_custom_overlay(self, channel: int, group: int, text1: str, text2: str): + async def async_set_service_set_custom_overlay(self, group: int, text1: str, text2: str): """ Handles the service call from SERVICE_SET_CUSTOM_OVERLAY to set profile mode to day/night """ - await self.coordinator.client.async_set_service_set_custom_overlay(channel, group, text1, text2) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_service_set_custom_overlay(channel, group, text1, text2) diff --git a/custom_components/dahua/client.py b/custom_components/dahua/client.py index b6c2ab2..667cdc3 100644 --- a/custom_components/dahua/client.py +++ b/custom_components/dahua/client.py @@ -54,11 +54,13 @@ def get_rtsp_stream_url(self, channel: int, subtype: int) -> str: ) return url - async def async_get_snapshot(self, channel: int) -> bytes: + async def async_get_snapshot(self, channel_number: int) -> bytes: """ Takes a snapshot of the camera and returns the binary jpeg data + NOTE: channel_number is not the channel_index. channel_number is the index + 1 + so channel index 0 is channel number 1 """ - url = "/cgi-bin/snapshot.cgi?channel={0}".format(channel) + url = "/cgi-bin/snapshot.cgi?channel={0}".format(channel_number) return await self.get_bytes(url) async def async_get_system_info(self) -> dict: @@ -147,7 +149,7 @@ async def async_get_machine_name(self) -> dict: url = "/cgi-bin/configManager.cgi?action=getConfig&name=General" return await self.get(url) - async def async_common_config(self, profile_mode) -> dict: + async def async_common_config(self, channel: int, profile_mode) -> dict: """ async_common_config will fetch the status of the IR light (InfraRed light) and motion detection status (if it is enabled or not) @@ -160,8 +162,8 @@ async def async_common_config(self, profile_mode) -> dict: table.Lighting[0][0].Mode=Auto table.Lighting[0][0].Sensitive=3 """ - url = "/cgi-bin/configManager.cgi?action=getConfig&name=MotionDetect&action=getConfig&name=Lighting[0][{0}]".format( - profile_mode + url = "/cgi-bin/configManager.cgi?action=getConfig&name=MotionDetect&action=getConfig&name=Lighting[{0}][{1}]".format( + channel, profile_mode ) return await self.get(url) @@ -204,15 +206,15 @@ async def async_set_ivs_rule(self, channel: int, index: int, enabled: bool): ) return await self.get(url, True) - async def async_set_lighting_v1(self, enabled: bool, brightness: int) -> dict: + 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 mode = "Manual" if not enabled: mode = "Off" - return await self.async_set_lighting_v1_mode(mode, brightness) + return await self.async_set_lighting_v1_mode(channel, mode, brightness) - async def async_set_lighting_v1_mode(self, mode: str, brightness: int) -> dict: + async def async_set_lighting_v1_mode(self, channel: int, mode: str, brightness: int) -> dict: """ async_set_lighting_v1_mode will set IR light (InfraRed light) mode and brightness Mode should be one of: Manual, Off, or Auto @@ -224,12 +226,12 @@ async def async_set_lighting_v1_mode(self, mode: str, brightness: int) -> dict: # Dahua api expects the first char to be capital mode = mode.capitalize() - url = "/cgi-bin/configManager.cgi?action=setConfig&Lighting[0][0].Mode={0}&Lighting[0][0].MiddleLight[0].Light={1}".format( - mode, brightness + url = "/cgi-bin/configManager.cgi?action=setConfig&Lighting[{channel}][0].Mode={mode}&Lighting[{channel}][0].MiddleLight[0].Light={brightness}".format( + channel=channel, mode=mode, brightness=brightness ) return await self.get(url) - async def async_set_video_profile_mode(self, mode: str): + async def async_set_video_profile_mode(self, channel: int, mode: str): """ async_set_video_profile_mode will set camera's profile mode to day or night Mode should be one of: Day or Night @@ -241,10 +243,8 @@ async def async_set_video_profile_mode(self, mode: str): # Default to "day", which is 0 mode = "0" - url = "/cgi-bin/configManager.cgi?action=setConfig&VideoInMode[0].Config[0]={0}".format(mode) - value = await self.get(url) - if "OK" not in value and "ok" not in value: - raise Exception("Could not set video profile mode") + url = "/cgi-bin/configManager.cgi?action=setConfig&VideoInMode[{0}].Config[0]={1}".format(channel, mode) + return await self.get(url, True) async def async_enable_channel_title(self, channel: int, enabled: bool, ): """ async_set_enable_channel_title will enable or disables the camera's channel title overlay """ @@ -313,7 +313,7 @@ async def async_set_service_set_custom_overlay(self, channel: int, group: int, t if "OK" not in value and "ok" not in value: raise Exception("Could not set text") - async def async_set_lighting_v2(self, enabled: bool, brightness: int, profile_mode: str) -> dict: + async def async_set_lighting_v2(self, channel: int, enabled: bool, brightness: int, profile_mode: str) -> dict: """ async_set_lighting_v2 will turn on or off the white light on the camera. If turning on, the brightness will be used. brightness is in the range of 0 to 100 inclusive where 100 is the brightest. @@ -326,8 +326,8 @@ async def async_set_lighting_v2(self, enabled: bool, brightness: int, profile_mo mode = "Manual" if not enabled: mode = "Off" - url = "/cgi-bin/configManager.cgi?action=setConfig&Lighting_V2[0][{0}][0].Mode={1}&Lighting_V2[0][{2}][0].MiddleLight[0].Light={3}".format( - profile_mode, mode, profile_mode, brightness + url = "/cgi-bin/configManager.cgi?action=setConfig&Lighting_V2[{channel}][{profile_mode}][0].Mode={mode}&Lighting_V2[{channel}][{profile_mode}][0].MiddleLight[0].Light={brightness}".format( + channel=channel, profile_mode=profile_mode, mode=mode, brightness=brightness ) _LOGGER.debug("Turning light on: %s", url) return await self.get(url) @@ -347,7 +347,7 @@ async def async_get_video_in_mode(self) -> dict: url = "/cgi-bin/configManager.cgi?action=getConfig&name=VideoInMode" return await self.get(url) - async def async_set_coaxial_control_state(self, dahua_type: int, enabled: bool) -> dict: + async def async_set_coaxial_control_state(self, channel: int, dahua_type: int, enabled: bool) -> dict: """ async_set_lighting_v2 will turn on or off the white light on the camera. @@ -361,12 +361,12 @@ async def async_set_coaxial_control_state(self, dahua_type: int, enabled: bool) if not enabled: io = "2" - url = "/cgi-bin/coaxialControlIO.cgi?action=control&channel=0&info[0].Type={0}&info[0].IO={1}".format( - dahua_type, io) + url = "/cgi-bin/coaxialControlIO.cgi?action=control&channel={channel}&info[0].Type={dahua_type}&info[0].IO={io}".format( + channel=channel, dahua_type=dahua_type, io=io) _LOGGER.debug("Setting coaxial control state to %s: %s", io, url) return await self.get(url) - async def async_set_disarming_linkage(self, enabled: bool) -> dict: + async def async_set_disarming_linkage(self, channel: int, enabled: bool) -> dict: """ async_set_disarming_linkage will set the camera's disarming linkage (Event -> Disarming in the UI) """ @@ -375,10 +375,10 @@ async def async_set_disarming_linkage(self, enabled: bool) -> dict: if enabled: value = "true" - url = "/cgi-bin/configManager.cgi?action=setConfig&DisableLinkage[0].Enable={0}".format(value) + url = "/cgi-bin/configManager.cgi?action=setConfig&DisableLinkage[{0}].Enable={1}".format(channel, value) return await self.get(url) - async def async_set_record_mode(self, mode: str) -> dict: + async def async_set_record_mode(self, channel: int, mode: str) -> dict: """ async_set_record_mode sets the record mode. mode should be one of: auto, manual, or off @@ -390,7 +390,7 @@ async def async_set_record_mode(self, mode: str) -> dict: mode = "1" elif mode.lower() == "off": mode = "2" - url = "/cgi-bin/configManager.cgi?action=setConfig&RecordMode[0].Mode={0}".format(mode) + url = "/cgi-bin/configManager.cgi?action=setConfig&RecordMode[{0}].Mode={1}".format(channel, mode) _LOGGER.debug("Setting record mode: %s", url) return await self.get(url) @@ -405,19 +405,20 @@ async def async_get_disarming_linkage(self) -> dict: url = "/cgi-bin/configManager.cgi?action=getConfig&name=DisableLinkage" return await self.get(url) - async def enable_motion_detection(self, enabled: bool) -> dict: + async def enable_motion_detection(self, channel: int, enabled: bool) -> dict: """ - enable_motion_detection will either enable or disable motion detection on the camera depending on the supplied value + enable_motion_detection will either enable/disable motion detection on the camera depending on the value """ - url = "/cgi-bin/configManager.cgi?action=setConfig&MotionDetect[0].Enable={0}&MotionDetect[0].DetectVersion=V3.0".format( - str(enabled).lower()) + url = "/cgi-bin/configManager.cgi?action=setConfig&MotionDetect[{channel}].Enable={enabled}&MotionDetect[{channel}].DetectVersion=V3.0".format( + channel=channel, enabled=str(enabled).lower()) response = await self.get(url) if "OK" in response: return response # Some older cameras do not support the above API, so try this one - url = "/cgi-bin/configManager.cgi?action=setConfig&MotionDetect[0].Enable={0}".format(str(enabled).lower()) + url = "/cgi-bin/configManager.cgi?action=setConfig&MotionDetect[{0}].Enable={1}".format(channel, + str(enabled).lower()) response = await self.get(url) return response diff --git a/custom_components/dahua/light.py b/custom_components/dahua/light.py index 752cb7c..8037645 100644 --- a/custom_components/dahua/light.py +++ b/custom_components/dahua/light.py @@ -81,14 +81,16 @@ async def async_turn_on(self, **kwargs): """Turn the light on with the current brightness""" hass_brightness = kwargs.get(ATTR_BRIGHTNESS) dahua_brightness = dahua_utils.hass_brightness_to_dahua_brightness(hass_brightness) - await self._coordinator.client.async_set_lighting_v1(True, dahua_brightness) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_lighting_v1(channel, True, dahua_brightness) await self.coordinator.async_refresh() async def async_turn_off(self, **kwargs): """Turn the light off""" hass_brightness = kwargs.get(ATTR_BRIGHTNESS) dahua_brightness = dahua_utils.hass_brightness_to_dahua_brightness(hass_brightness) - await self._coordinator.client.async_set_lighting_v1(False, dahua_brightness) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_lighting_v1(channel, False, dahua_brightness) await self.coordinator.async_refresh() @property @@ -143,16 +145,18 @@ async def async_turn_on(self, **kwargs): """Turn the light on with the current brightness""" hass_brightness = kwargs.get(ATTR_BRIGHTNESS) dahua_brightness = dahua_utils.hass_brightness_to_dahua_brightness(hass_brightness) + channel = self._coordinator.get_channel() profile_mode = self._coordinator.get_profile_mode() - await self._coordinator.client.async_set_lighting_v2(True, dahua_brightness, profile_mode) + await self._coordinator.client.async_set_lighting_v2(channel, True, dahua_brightness, profile_mode) await self._coordinator.async_refresh() async def async_turn_off(self, **kwargs): """Turn the light off""" hass_brightness = kwargs.get(ATTR_BRIGHTNESS) dahua_brightness = dahua_utils.hass_brightness_to_dahua_brightness(hass_brightness) + channel = self._coordinator.get_channel() profile_mode = self._coordinator.get_profile_mode() - await self._coordinator.client.async_set_lighting_v2(False, dahua_brightness, profile_mode) + await self._coordinator.client.async_set_lighting_v2(channel, False, dahua_brightness, profile_mode) await self._coordinator.async_refresh() @@ -165,11 +169,12 @@ class DahuaSecurityLight(DahuaBaseEntity, LightEntity): def __init__(self, coordinator: DahuaDataUpdateCoordinator, entry, name): super().__init__(coordinator, entry) self._name = name + self._coordinator = coordinator @property def name(self): """Return the name of the light.""" - return self.coordinator.get_device_name() + " " + self._name + return self._coordinator.get_device_name() + " " + self._name @property def unique_id(self): @@ -177,12 +182,12 @@ 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() + "_security" + return self._coordinator.get_serial_number() + "_security" @property def is_on(self): """Return true if the light is on""" - return self.coordinator.is_security_light_on() + return self._coordinator.is_security_light_on() @property def should_poll(self): @@ -191,13 +196,15 @@ def should_poll(self): async def async_turn_on(self, **kwargs): """Turn the light on""" - await self.coordinator.client.async_set_coaxial_control_state(SECURITY_LIGHT_TYPE, True) - await self.coordinator.async_refresh() + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_coaxial_control_state(channel, SECURITY_LIGHT_TYPE, True) + await self._coordinator.async_refresh() async def async_turn_off(self, **kwargs): """Turn the light off""" - await self.coordinator.client.async_set_coaxial_control_state(SECURITY_LIGHT_TYPE, False) - await self.coordinator.async_refresh() + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_coaxial_control_state(channel, SECURITY_LIGHT_TYPE, False) + await self._coordinator.async_refresh() @property def icon(self): diff --git a/custom_components/dahua/services.yaml b/custom_components/dahua/services.yaml index 3b806b4..c47628d 100644 --- a/custom_components/dahua/services.yaml +++ b/custom_components/dahua/services.yaml @@ -57,17 +57,6 @@ enable_channel_title: integration: dahua domain: camera fields: - channel: - name: Channel - description: "The camera channel index. Typically 0" - example: 0 - required: true - default: 0 - selector: - number: - mode: box - min: 0 - max: 100 enabled: name: Enabled description: "If the overlay is enabled or not" @@ -86,17 +75,6 @@ enable_time_overlay: integration: dahua domain: camera fields: - channel: - name: Channel - description: "The camera channel index. Typically 0" - example: 0 - required: true - default: 0 - selector: - number: - mode: box - min: 0 - max: 100 enabled: name: Enabled description: "If the overlay is enabled or not" @@ -115,17 +93,6 @@ enable_text_overlay: integration: dahua domain: camera fields: - channel: - name: Channel - description: "The camera channel index. Typically 0" - example: 0 - required: true - default: 0 - selector: - number: - mode: box - min: 0 - max: 100 group: name: Group description: "Multiple text overlay groups can exist. The default 1 should be used in most cases" @@ -155,17 +122,6 @@ enable_custom_overlay: integration: dahua domain: camera fields: - channel: - name: Channel - description: "The camera channel index. Typically 0" - example: 0 - required: true - default: 0 - selector: - number: - mode: box - min: 0 - max: 100 group: name: Group description: "Multiple custom text groups can exist. The default 0 should be used in most cases" @@ -194,17 +150,6 @@ set_custom_overlay: integration: dahua domain: camera fields: - channel: - name: Channel - description: "The camera channel index. Typically 0" - example: 0 - required: true - default: 0 - selector: - number: - mode: box - min: 0 - max: 100 group: name: Group description: "Multiple custom text groups can exist. The default 0 should be used in most cases" @@ -242,17 +187,6 @@ set_channel_title: integration: dahua domain: camera fields: - channel: - name: Channel - description: "The camera channel index. Typically 0" - example: 0 - required: true - default: 0 - selector: - number: - mode: box - min: 0 - max: 100 text1: name: Text 1 description: "The first title" @@ -279,17 +213,6 @@ set_text_overlay: integration: dahua domain: camera fields: - channel: - name: Channel - description: "The camera channel index. Typically 0" - example: 0 - required: true - default: 0 - selector: - number: - mode: box - min: 0 - max: 100 group: name: Group description: "Multiple custom text groups can exist. The default 1 should be used in most cases" @@ -361,17 +284,6 @@ enable_all_ivs_rules: integration: dahua domain: camera fields: - channel: - name: Channel - description: "The camera channel index. Typically 0" - example: 0 - required: true - default: 0 - selector: - number: - mode: box - min: 0 - max: 100 enabled: name: Enabled description: "If true all IVS rules are enabled. If false, all are disabled" @@ -389,17 +301,6 @@ enable_ivs_rule: integration: dahua domain: camera fields: - channel: - name: Channel - description: "The camera channel index. Typically 0" - example: 0 - required: true - default: 0 - selector: - number: - mode: box - min: 0 - max: 100 index: name: Index description: "The rule index. 0 is a hidden rule, so usually the first rule is rule 1" diff --git a/custom_components/dahua/switch.py b/custom_components/dahua/switch.py index 89a4e9d..917e713 100644 --- a/custom_components/dahua/switch.py +++ b/custom_components/dahua/switch.py @@ -34,12 +34,14 @@ class DahuaMotionDetectionBinarySwitch(DahuaBaseEntity, SwitchEntity): async def async_turn_on(self, **kwargs): # pylint: disable=unused-argument """Turn on/enable motion detection.""" - await self._coordinator.client.enable_motion_detection(True) + channel = self._coordinator.get_channel() + await self._coordinator.client.enable_motion_detection(channel, True) await self._coordinator.async_refresh() async def async_turn_off(self, **kwargs): # pylint: disable=unused-argument """Turn off/disable motion detection.""" - await self._coordinator.client.enable_motion_detection(False) + channel = self._coordinator.get_channel() + await self._coordinator.client.enable_motion_detection(channel, False) await self._coordinator.async_refresh() @property @@ -74,12 +76,14 @@ class DahuaDisarmingLinkageBinarySwitch(DahuaBaseEntity, SwitchEntity): async def async_turn_on(self, **kwargs): # pylint: disable=unused-argument """Turn on/enable linkage""" - await self._coordinator.client.async_set_disarming_linkage(True) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_disarming_linkage(channel, True) await self._coordinator.async_refresh() async def async_turn_off(self, **kwargs): # pylint: disable=unused-argument """Turn off/disable linkage""" - await self._coordinator.client.async_set_disarming_linkage(False) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_disarming_linkage(channel, False) await self._coordinator.async_refresh() @property @@ -115,12 +119,14 @@ class DahuaSirenBinarySwitch(DahuaBaseEntity, SwitchEntity): async def async_turn_on(self, **kwargs): # pylint: disable=unused-argument """Turn on/enable the camera's siren""" - await self._coordinator.client.async_set_coaxial_control_state(SIREN_TYPE, True) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_coaxial_control_state(channel, SIREN_TYPE, True) await self._coordinator.async_refresh() async def async_turn_off(self, **kwargs): # pylint: disable=unused-argument """Turn off/disable camera siren""" - await self._coordinator.client.async_set_coaxial_control_state(SIREN_TYPE, False) + channel = self._coordinator.get_channel() + await self._coordinator.client.async_set_coaxial_control_state(channel, SIREN_TYPE, False) await self._coordinator.async_refresh() @property