Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions custom_components/dahua/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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:

Expand All @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions custom_components/dahua/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 """
Expand Down
8 changes: 5 additions & 3 deletions custom_components/dahua/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 4 additions & 2 deletions custom_components/dahua/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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()


Expand Down