Skip to content

Commit

Permalink
fix for scene change event entities for newer ipc firmware (#185)
Browse files Browse the repository at this point in the history
  • Loading branch information
maciej-or authored Jul 12, 2024
1 parent 32cecb3 commit 84f3372
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 17 deletions.
4 changes: 2 additions & 2 deletions custom_components/hikvision_next/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e

# Camera Events
for camera in isapi.cameras:
for event in camera.supported_events:
for event in camera.events_info:
entities.append(EventBinarySensor(isapi, camera.id, event))

# NVR Events
if isapi.device_info.is_nvr:
for event in isapi.device_info.supported_events:
for event in isapi.device_info.events_info:
entities.append(EventBinarySensor(isapi, 0, event))

async_add_entities(entities)
Expand Down
4 changes: 2 additions & 2 deletions custom_components/hikvision_next/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ async def _async_update_data(self):

# Get camera event status
for camera in self.isapi.cameras:
for event in camera.supported_events:
for event in camera.events_info:
try:
entity_id = ENTITY_ID_FORMAT.format(event.unique_id)
data[entity_id] = await self.isapi.get_event_enabled_state(event)
except Exception as ex: # pylint: disable=broad-except
self.isapi.handle_exception(ex, f"Cannot fetch state for {event.id}")

# Get NVR event status
for event in self.isapi.device_info.supported_events:
for event in self.isapi.device_info.events_info:
try:
entity_id = ENTITY_ID_FORMAT.format(event.unique_id)
data[entity_id] = await self.isapi.get_event_enabled_state(event)
Expand Down
2 changes: 1 addition & 1 deletion custom_components/hikvision_next/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ async def _async_get_diagnostics(

# event states
for camera in isapi.cameras:
for event in camera.supported_events:
for event in camera.events_info:
responses[event.url] = await get_isapi_data(isapi, event.url)

info["ISAPI"] = responses
Expand Down
30 changes: 21 additions & 9 deletions custom_components/hikvision_next/isapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ class HikDeviceInfo:
output_ports: int = 0
rtsp_port: int = 554
storage: list[StorageInfo] = field(default_factory=list)
supported_events: list[EventInfo] = field(default_factory=list)
events_info: list[EventInfo] = field(default_factory=list)


@dataclass
Expand All @@ -162,7 +162,7 @@ class AnalogCamera:
input_port: int
connection_type: str
streams: list[CameraStreamInfo] = field(default_factory=list)
supported_events: list[EventInfo] = field(default_factory=list)
events_info: list[EventInfo] = field(default_factory=list)


@dataclass
Expand Down Expand Up @@ -209,7 +209,7 @@ async def get_hardware_info(self):
capabilities = (await self.request(GET, "System/capabilities")).get("DeviceCap", {})

# Get all supported events to reduce isapi queries
self.supported_events = await self.get_supported_events_info()
self.supported_events = await self.get_supported_events(capabilities)

# Set DeviceInfo
self.device_info.support_analog_cameras = int(deep_get(capabilities, "SysCap.VideoCap.videoInputPortNums", 0))
Expand All @@ -226,7 +226,7 @@ async def get_hardware_info(self):

with suppress(Exception):
self.device_info.storage = await self.get_storage_devices()
self.device_info.supported_events = await self.get_device_event_capabilities(
self.device_info.events_info = await self.get_device_event_capabilities(
self.supported_events, self.device_info.serial_no, 0
)
self.device_info.support_alarm_server = bool(await self.get_alarm_server())
Expand Down Expand Up @@ -254,7 +254,7 @@ async def get_cameras(self):
connection_type=CONNECTION_TYPE_DIRECT,
ip_addr=self.device_info.ip_address,
streams=await self.get_camera_streams(1),
supported_events=await self.get_device_event_capabilities(
events_info=await self.get_device_event_capabilities(
self.supported_events,
self.device_info.serial_no,
1,
Expand Down Expand Up @@ -299,7 +299,7 @@ async def get_cameras(self):
ip_addr=source.get("ipAddress"),
ip_port=source.get("managePortNo"),
streams=await self.get_camera_streams(camera_id),
supported_events=await self.get_device_event_capabilities(
events_info=await self.get_device_event_capabilities(
self.supported_events,
self.device_info.serial_no,
camera_id,
Expand Down Expand Up @@ -332,7 +332,7 @@ async def get_cameras(self):
input_port=int(analog_camera.get("inputPort")),
connection_type=CONNECTION_TYPE_DIRECT,
streams=await self.get_camera_streams(camera_id),
supported_events=await self.get_device_event_capabilities(
events_info=await self.get_device_event_capabilities(
self.supported_events,
self.device_info.serial_no,
camera_id,
Expand Down Expand Up @@ -361,7 +361,7 @@ async def get_device_event_capabilities(
device_id: int,
connection_type: str = CONNECTION_TYPE_DIRECT,
) -> list[EventInfo]:
"""Get events support by device (device id: NVR = 0, camera > 0)."""
"""Get events info supported by integration (device id: NVR = 0, camera > 0)."""
events = []

if device_id == 0: # NVR
Expand Down Expand Up @@ -391,7 +391,7 @@ async def get_device_event_capabilities(
events.append(event_info)
return events

async def get_supported_events_info(self):
async def get_supported_events(self, system_capabilities: dict) -> list[SupportedEventsInfo]:
"""Get list of all supported events available."""
events = []
event_triggers = await self.request(GET, "Event/triggers")
Expand Down Expand Up @@ -430,6 +430,18 @@ async def get_supported_events_info(self):
)
)

# some single cameras do not have scenechangedetection in Event/triggers
if not self.device_info.is_nvr and not [e for e in events if e.event_id == "scenechangedetection"]:
is_supported = str_to_bool(deep_get(system_capabilities, "SmartCap.isSupportSceneChangeDetection", False))
if is_supported:
events.append(
SupportedEventsInfo(
channel_id=1,
io_port_id=0,
event_id="scenechangedetection",
)
)

return events

def get_event_url(self, event: SupportedEventsInfo, connection_type: str) -> str:
Expand Down
4 changes: 2 additions & 2 deletions custom_components/hikvision_next/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e

# Camera supported events
for camera in events_coordinator.isapi.cameras:
for event in camera.supported_events:
for event in camera.events_info:
entities.append(EventSwitch(camera.id, event, events_coordinator))

# NVR supported events
if events_coordinator.isapi.device_info.is_nvr:
for event in events_coordinator.isapi.device_info.supported_events:
for event in events_coordinator.isapi.device_info.events_info:
entities.append(EventSwitch(0, event, events_coordinator))

# Output port switch
Expand Down
11 changes: 11 additions & 0 deletions tests/fixtures/devices/DS-2CD2386G2-IU.json
Original file line number Diff line number Diff line change
Expand Up @@ -1623,6 +1623,17 @@
"isSupportTargetMultiSelect": "true"
}
}
},
"Smart/SceneChangeDetection/1": {
"response": {
"SceneChangeDetection": {
"@version": "2.0",
"@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
"id": "1",
"enabled": "true",
"sensitivityLevel": "50"
}
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ async def test_async_setup_entry_ipc(hass: HomeAssistant, init_integration: Mock
isapi = hass.data[DOMAIN][entry.entry_id]["isapi"]
assert isapi.host == TEST_CONFIG["host"]
assert len(isapi.cameras) == 1
assert len(isapi.supported_events) == 11
assert len(isapi.supported_events) == 12

device_info = isapi.device_info
assert device_info.device_type == "IPCamera"
Expand Down

0 comments on commit 84f3372

Please sign in to comment.