diff --git a/custom_components/dahua/__init__.py b/custom_components/dahua/__init__.py index 984e55e..bb912c2 100755 --- a/custom_components/dahua/__init__.py +++ b/custom_components/dahua/__init__.py @@ -362,6 +362,15 @@ async def _async_update_data(self): except ClientError: _LOGGER.debug("Cam does not support profile mode. Will use mode 0") self._supports_profile_mode = False + if not self._supports_profile_mode: + # The legacy Lighting[0][2] probe is empty on some cams (e.g. white-light/dual + # illuminator models). Fall back to the VideoInMode API, which is the canonical + # source for the active day/night profile. + try: + mode_data = await self.client.async_get_video_in_mode() + self._supports_profile_mode = "table.VideoInMode[0].Config[0]" in mode_data + except ClientError: + self._supports_profile_mode = False _LOGGER.debug("Device supports profile mode=%s", self._supports_profile_mode) else: # Start the event listeners for doorbells (VTO) @@ -766,11 +775,28 @@ def get_infrared_brightness(self) -> int: bri = self.data.get("table.Lighting[{0}][0].MiddleLight[0].Light".format(self._channel)) return dahua_utils.dahua_brightness_to_hass_brightness(bri) + def get_illuminator_index(self, profile_mode) -> int: + """ + Return the Lighting_V2 array index of the white-light illuminator for the given profile. + + On single-illuminator cameras the white light is at index 0, but on dual-illuminator + models (e.g. the -IL series) index 0 is the InfraredLight and the white light lives at a + later index. We locate it by LightType so the right light is read/controlled regardless of + layout. Falls back to 0 when no WhiteLight entry is present (legacy behaviour). + """ + for index in range(0, 3): + light_type = self.data.get( + "table.Lighting_V2[{0}][{1}][{2}].LightType".format(self._channel, profile_mode, index)) + if light_type == "WhiteLight": + return index + return 0 + def is_illuminator_on(self) -> bool: """Return true if the illuminator light is on""" # profile_mode 0=day, 1=night, 2=scene - profile_mode = self.get_profile_mode() - return self.data.get("table.Lighting_V2[{0}][{1}][0].Mode".format(self._channel, profile_mode), "") == "Manual" + profile_mode = self.get_profile_mode() + index = self.get_illuminator_index(profile_mode) + return self.data.get("table.Lighting_V2[{0}][{1}][{2}].Mode".format(self._channel, profile_mode, index), "") == "Manual" def is_flood_light_on(self) -> bool: @@ -789,8 +815,10 @@ def is_ring_light_on(self) -> bool: def get_illuminator_brightness(self) -> int: """Return the brightness of the illuminator light, as reported by the camera itself, between 0..255 inclusive""" - - bri = self.data.get("table.Lighting_V2[{0}][0][0].MiddleLight[0].Light".format(self._channel)) + # profile_mode 0=day, 1=night, 2=scene + profile_mode = self.get_profile_mode() + index = self.get_illuminator_index(profile_mode) + bri = self.data.get("table.Lighting_V2[{0}][{1}][{2}].MiddleLight[0].Light".format(self._channel, profile_mode, index)) return dahua_utils.dahua_brightness_to_hass_brightness(bri) def is_security_light_on(self) -> bool: diff --git a/custom_components/dahua/camera.py b/custom_components/dahua/camera.py index ced874a..8cc4c9a 100755 --- a/custom_components/dahua/camera.py +++ b/custom_components/dahua/camera.py @@ -335,6 +335,10 @@ async def async_set_video_profile_mode(self, mode: str): await self._coordinator.client.async_set_night_switch_mode(channel, mode) else: await self._coordinator.client.async_set_video_profile_mode(channel, mode) + # Refresh immediately so dependent entities (e.g. the illuminator light, whose state is + # derived from the active day/night profile) update right away instead of waiting for the + # next ~30s poll. The camera reflects the new profile in its config immediately. + await self._coordinator.async_refresh() async def async_adjustfocus(self, focus: str, zoom: str): """ Handles the service call from SERVICE_SET_INFRARED_MODE to set zoom and focus """ diff --git a/custom_components/dahua/client.py b/custom_components/dahua/client.py index 7d573c8..138b23e 100644 --- a/custom_components/dahua/client.py +++ b/custom_components/dahua/client.py @@ -495,21 +495,23 @@ 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, channel: int, enabled: bool, brightness: int, profile_mode: str) -> dict: + async def async_set_lighting_v2(self, channel: int, enabled: bool, brightness: int, profile_mode: str, index: int = 0) -> 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. NOTE: this is not the same as the infrared (IR) light. This is the white visible light on the camera profile_mode: 0=day, 1=night, 2=scene + index: the Lighting_V2 light index. White light is index 0 on single-illuminator cams but a + later index on dual-illuminator (-IL) models, so the caller passes the resolved index. """ # on = Manual, off = Off mode = "Manual" if not enabled: mode = "Off" - 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 + url = "/cgi-bin/configManager.cgi?action=setConfig&Lighting_V2[{channel}][{profile_mode}][{index}].Mode={mode}&Lighting_V2[{channel}][{profile_mode}][{index}].MiddleLight[0].Light={brightness}".format( + channel=channel, profile_mode=profile_mode, index=index, mode=mode, brightness=brightness ) _LOGGER.debug("Turning light on: %s", url) return await self.get(url) diff --git a/custom_components/dahua/light.py b/custom_components/dahua/light.py index a844366..c3c99d4 100755 --- a/custom_components/dahua/light.py +++ b/custom_components/dahua/light.py @@ -166,7 +166,8 @@ async def async_turn_on(self, **kwargs): 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(channel, True, dahua_brightness, profile_mode) + index = self._coordinator.get_illuminator_index(profile_mode) + await self._coordinator.client.async_set_lighting_v2(channel, True, dahua_brightness, profile_mode, index) await self._coordinator.async_refresh() async def async_turn_off(self, **kwargs): @@ -175,7 +176,8 @@ async def async_turn_off(self, **kwargs): 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(channel, False, dahua_brightness, profile_mode) + index = self._coordinator.get_illuminator_index(profile_mode) + await self._coordinator.client.async_set_lighting_v2(channel, False, dahua_brightness, profile_mode, index) await self._coordinator.async_refresh()