diff --git a/custom_components/hikvision_next/binary_sensor.py b/custom_components/hikvision_next/binary_sensor.py index d455651..fff1d13 100644 --- a/custom_components/hikvision_next/binary_sensor.py +++ b/custom_components/hikvision_next/binary_sensor.py @@ -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) diff --git a/custom_components/hikvision_next/coordinator.py b/custom_components/hikvision_next/coordinator.py index 61c394a..47e6890 100644 --- a/custom_components/hikvision_next/coordinator.py +++ b/custom_components/hikvision_next/coordinator.py @@ -41,7 +41,7 @@ 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) @@ -49,7 +49,7 @@ async def _async_update_data(self): 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) diff --git a/custom_components/hikvision_next/diagnostics.py b/custom_components/hikvision_next/diagnostics.py index aa78de2..2c653e4 100644 --- a/custom_components/hikvision_next/diagnostics.py +++ b/custom_components/hikvision_next/diagnostics.py @@ -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 diff --git a/custom_components/hikvision_next/isapi.py b/custom_components/hikvision_next/isapi.py index a758cb9..ab8f68b 100644 --- a/custom_components/hikvision_next/isapi.py +++ b/custom_components/hikvision_next/isapi.py @@ -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 @@ -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 @@ -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)) @@ -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()) @@ -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, @@ -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, @@ -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, @@ -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 @@ -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") @@ -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: diff --git a/custom_components/hikvision_next/switch.py b/custom_components/hikvision_next/switch.py index 287ae29..03a659e 100644 --- a/custom_components/hikvision_next/switch.py +++ b/custom_components/hikvision_next/switch.py @@ -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 diff --git a/tests/fixtures/devices/DS-2CD2386G2-IU.json b/tests/fixtures/devices/DS-2CD2386G2-IU.json index 634fa69..db8aeaa 100644 --- a/tests/fixtures/devices/DS-2CD2386G2-IU.json +++ b/tests/fixtures/devices/DS-2CD2386G2-IU.json @@ -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" + } + } } } } diff --git a/tests/test_init.py b/tests/test_init.py index da4a140..b0bbc6c 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -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"