diff --git a/README.md b/README.md
index c7768db..98df81d 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,7 @@ The Home Assistant integration for Hikvision NVRs and IP cameras. Receives and s
- Image entities for the latest snapshots
- Tracking HDD and NAS status
- Tracking Notifications Host settings for diagnostic purposes
+- Remote reboot device
- Basic and digest authentication support
### Supported events
@@ -34,9 +35,18 @@ Events must be set to alert the surveillance center in Linkage Action for Home A
### Blueprints
-Take Multiple Snapshots On Detection Event
+#### Take Multiple Snapshots On Detection Event
+
+Creates automation that allows to take snapshots from selected cameras when an event sensor is triggered.
[](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https://github.com/maciej-or/hikvision_next/blob/main/blueprints/take_pictures_on_motion_detection.yaml)
+
+#### Display Sensor State On Hikvision Video
+
+Creates an automation that allows to display text overlay on a selected video stream with the state of a selected sensor. Refreshes every 15 minutes.
+
+[](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https://github.com/maciej-or/hikvision_next/blob/main/blueprints/display_sensor_state_on_hikvision_video.yaml)
+
## Preview
### IP Camera device view
diff --git a/blueprints/display_sensor_state_on_hikvision_video.yaml b/blueprints/display_sensor_state_on_hikvision_video.yaml
new file mode 100644
index 0000000..d6e02db
--- /dev/null
+++ b/blueprints/display_sensor_state_on_hikvision_video.yaml
@@ -0,0 +1,81 @@
+blueprint:
+ name: Display Sensor State On Hikvision Video
+ description: |
+ Sets an overlay text on a Hikvision camera video.
+ domain: automation
+ input:
+ config_entry_id:
+ name: "Device"
+ description: "The Hikvision device."
+ selector:
+ config_entry:
+ integration: hikvision_next
+ camera_no:
+ name: "Camera"
+ description: "The camera number."
+ default: 1
+ selector:
+ number:
+ min: 1
+ max: 32
+ step: 1
+ mode: box
+ sensor_entity:
+ name: Sensor
+ description: "The sensor entity to display on the video."
+ selector:
+ entity:
+ domain: sensor
+ position_x:
+ name: "X"
+ description: "The X position of the overlay text."
+ default: 16
+ selector:
+ number:
+ min: 0
+ max: 1920
+ step: 1
+ position_y:
+ name: "Y"
+ description: "The Y position of the overlay text."
+ default: 570
+ selector:
+ number:
+ min: 0
+ max: 1080
+ step: 1
+ enabled:
+ name: "State"
+ description: "Enable or disable the overlay text."
+ default: true
+ selector:
+ boolean: {}
+mode: single
+variables:
+ entity_id: !input sensor_entity
+ camera_no: !input camera_no
+ position_x: !input position_x
+ position_y: !input position_y
+ enabled: !input enabled
+trigger:
+ - platform: time_pattern
+ minutes: /15
+action:
+ - service: hikvision_next.isapi_request
+ data:
+ method: PUT
+ config_entry_id: !input config_entry_id
+ path: "/System/Video/inputs/channels/{{camera_no}}/overlays/text"
+ payload: >
+
+
+
+ 1
+ {{ enabled | lower }}
+ {{ position_x }}
+ {{ position_y }}
+ {{ states(entity_id, with_unit=True) }}
+
+
+
+ response_variable: response
diff --git a/custom_components/hikvision_next/__init__.py b/custom_components/hikvision_next/__init__.py
index f7da136..04cd1bf 100644
--- a/custom_components/hikvision_next/__init__.py
+++ b/custom_components/hikvision_next/__init__.py
@@ -5,6 +5,7 @@
import asyncio
from contextlib import suppress
import logging
+import traceback
from httpx import TimeoutException
@@ -18,6 +19,7 @@
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.httpx_client import get_async_client
+from homeassistant.helpers.typing import ConfigType
from .const import (
ALARM_SERVER_PATH,
@@ -31,6 +33,7 @@
from .coordinator import EventsCoordinator, SecondaryCoordinator
from .isapi import ISAPI
from .notifications import EventNotificationsView
+from .services import setup_services
_LOGGER = logging.getLogger(__name__)
@@ -43,6 +46,14 @@
]
+async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
+ """Set up the Hikvision component."""
+
+ setup_services(hass)
+
+ return True
+
+
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up integration from a config entry."""
hass.data.setdefault(DOMAIN, {})
@@ -59,12 +70,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
device_info = isapi.hass_device_info()
device_registry = dr.async_get(hass)
device_registry.async_get_or_create(config_entry_id=entry.entry_id, **device_info)
- except (asyncio.TimeoutError, TimeoutException) as ex:
+ except (TimeoutError, TimeoutException) as ex:
raise ConfigEntryNotReady(f"Timeout while connecting to {host}. Cannot initialize {DOMAIN}") from ex
except Exception as ex: # pylint: disable=broad-except
- raise ConfigEntryNotReady(
- f"Unknown error connecting to {host}. Cannot initialize {DOMAIN}. Error is {ex}"
- ) from ex
+ msg = f"Cannot initialize {DOMAIN} {host}. Error: {ex}\n"
+ _LOGGER.error(msg + traceback.format_exc())
+ raise ConfigEntryNotReady(msg) from ex
coordinators = {}
diff --git a/custom_components/hikvision_next/config_flow.py b/custom_components/hikvision_next/config_flow.py
index 46d984c..33d9330 100755
--- a/custom_components/hikvision_next/config_flow.py
+++ b/custom_components/hikvision_next/config_flow.py
@@ -14,6 +14,7 @@
from homeassistant.config_entries import ConfigEntry, ConfigFlow
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.data_entry_flow import FlowResult
+from homeassistant.helpers.httpx_client import get_async_client
from .const import DATA_ALARM_SERVER_HOST, DATA_SET_ALARM_SERVER, DOMAIN
from .isapi import ISAPI
@@ -62,7 +63,8 @@ async def async_step_user(self, user_input: dict[str, Any] | None = None) -> Flo
CONF_HOST: host,
}
- isapi = ISAPI(host, username, password)
+ session = get_async_client(self.hass)
+ isapi = ISAPI(host, username, password, session)
await isapi.get_device_info()
if self._reauth_entry:
diff --git a/custom_components/hikvision_next/const.py b/custom_components/hikvision_next/const.py
index cb4dca9..3227e10 100644
--- a/custom_components/hikvision_next/const.py
+++ b/custom_components/hikvision_next/const.py
@@ -18,6 +18,11 @@
CONNECTION_TYPE_DIRECT = "Direct"
CONNECTION_TYPE_PROXIED = "Proxied"
+ATTR_CONFIG_ENTRY_ID = "config_entry_id"
+ACTION_REBOOT = "reboot"
+ACTION_ISAPI_REQUEST = "isapi_request"
+ACTION_UPDATE_SNAPSHOT = "update_snapshot"
+
HIKVISION_EVENT = f"{DOMAIN}_event"
EVENT_BASIC: Final = "basic"
EVENT_IO: Final = "io"
diff --git a/custom_components/hikvision_next/coordinator.py b/custom_components/hikvision_next/coordinator.py
index 5996d0f..72dc854 100644
--- a/custom_components/hikvision_next/coordinator.py
+++ b/custom_components/hikvision_next/coordinator.py
@@ -45,8 +45,8 @@ async def _async_update_data(self):
if event.disabled:
continue
try:
- entity_id = ENTITY_ID_FORMAT.format(event.unique_id)
- data[entity_id] = await self.isapi.get_event_enabled_state(event)
+ _id = ENTITY_ID_FORMAT.format(event.unique_id)
+ data[_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}")
@@ -55,18 +55,18 @@ async def _async_update_data(self):
if event.disabled:
continue
try:
- entity_id = ENTITY_ID_FORMAT.format(event.unique_id)
- data[entity_id] = await self.isapi.get_event_enabled_state(event)
+ _id = ENTITY_ID_FORMAT.format(event.unique_id)
+ data[_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 output port(s) status
for i in range(1, self.isapi.device_info.output_ports + 1):
try:
- entity_id = ENTITY_ID_FORMAT.format(
+ _id = ENTITY_ID_FORMAT.format(
f"{slugify(self.isapi.device_info.serial_no.lower())}_{i}_alarm_output"
)
- data[entity_id] = await self.isapi.get_port_status("output", i)
+ data[_id] = await self.isapi.get_port_status("output", i)
except Exception as ex: # pylint: disable=broad-except
self.isapi.handle_exception(ex, f"Cannot fetch state for alarm output {i}")
diff --git a/custom_components/hikvision_next/image.py b/custom_components/hikvision_next/image.py
index 719ef7d..0272d30 100644
--- a/custom_components/hikvision_next/image.py
+++ b/custom_components/hikvision_next/image.py
@@ -15,7 +15,7 @@
from homeassistant.helpers.template import Template
from homeassistant.util import slugify
-from .const import DATA_ISAPI, DOMAIN
+from .const import ACTION_UPDATE_SNAPSHOT, DATA_ISAPI, DOMAIN
from .isapi import ISAPI, CameraStreamInfo
_LOGGER = logging.getLogger(__name__)
@@ -37,7 +37,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e
platform = entity_platform.async_get_current_platform()
platform.async_register_entity_service(
- "update_snapshot",
+ ACTION_UPDATE_SNAPSHOT,
{vol.Required(CONF_FILENAME): cv.template},
"update_snapshot_filename",
)
diff --git a/custom_components/hikvision_next/isapi.py b/custom_components/hikvision_next/isapi.py
index 58081d6..6fa1d63 100644
--- a/custom_components/hikvision_next/isapi.py
+++ b/custom_components/hikvision_next/isapi.py
@@ -8,12 +8,12 @@
import datetime
from functools import reduce
from http import HTTPStatus
-import httpx
import json
import logging
from typing import Any, Optional
from urllib.parse import quote, urlparse
+import httpx
from httpx import HTTPStatusError, TimeoutException
import xmltodict
@@ -21,7 +21,6 @@
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.util import slugify
-from .isapi_client import ISAPI_Client
from .const import (
CONNECTION_TYPE_DIRECT,
@@ -35,6 +34,7 @@
MUTEX_ALTERNATE_IDS,
STREAM_TYPE,
)
+from .isapi_client import ISAPI_Client
Node = dict[str, Any]
@@ -410,6 +410,11 @@ def get_event(event_trigger: dict):
if not event_type:
return None
+ if event_type.lower() == EVENT_PIR:
+ is_supported = str_to_bool(deep_get(system_capabilities, "WLAlarmCap.isSupportPIR", False))
+ if not is_supported:
+ return None
+
channel = event_trigger.get("videoInputChannelID", event_trigger.get("dynVideoInputChannelID", 0))
io_port = event_trigger.get("inputIOPortID", event_trigger.get("dynInputIOPortID", 0))
notifications = notification_list.get("EventTriggerNotification", [])
@@ -435,6 +440,8 @@ def get_event(event_trigger: dict):
supported_events = deep_get(event_notification, "EventTriggerList.EventTrigger", [])
else:
supported_events = deep_get(event_triggers, "EventTriggerList.EventTrigger", [])
+ if not isinstance(supported_events, list):
+ supported_events = [supported_events]
for event_trigger in supported_events:
if event := get_event(event_trigger):
@@ -772,6 +779,10 @@ async def set_alarm_server(self, base_url: str, path: str) -> None:
xml = xmltodict.unparse(data)
await self.request(PUT, "Event/notification/httpHosts", present="xml", data=xml)
+ async def reboot(self):
+ """Reboot device."""
+ await self.request(PUT, "System/reboot", present="xml")
+
async def request(
self,
method: str,
diff --git a/custom_components/hikvision_next/isapi_client.py b/custom_components/hikvision_next/isapi_client.py
index 6e373f2..53e3dc3 100644
--- a/custom_components/hikvision_next/isapi_client.py
+++ b/custom_components/hikvision_next/isapi_client.py
@@ -1,10 +1,13 @@
import httpx
+import logging
from typing import Any, AsyncIterator, List, Union
from urllib.parse import urljoin
import json
import xmltodict
from dataclasses import dataclass
+_LOGGER = logging.getLogger(__name__)
+
def response_parser(response, present="dict"):
"""Parse Hikvision results."""
@@ -42,16 +45,21 @@ async def _detect_auth_method(self):
if not self.session:
self.session = httpx.AsyncClient(timeout=self.timeout)
- url = urljoin(self.host, self.isapi_prefix + "/System/status")
- for method in [
- httpx.BasicAuth(self.username, self.password),
- httpx.DigestAuth(self.username, self.password),
- ]:
- response = await self.session.get(url, auth=method)
- if response.status_code == 200:
- self._auth_method = method
+ url = urljoin(self.host, self.isapi_prefix + "/System/deviceInfo")
+ _LOGGER.debug("--- [WWW-Authenticate detection] %s", self.host)
+ response = await self.session.get(url)
+ if response.status_code == 401:
+ www_authenticate = response.headers.get("WWW-Authenticate", "")
+ _LOGGER.debug("WWW-Authenticate header: %s", www_authenticate)
+ if "Basic" in www_authenticate:
+ self._auth_method = httpx.BasicAuth(self.username, self.password)
+ elif "Digest" in www_authenticate:
+ self._auth_method = httpx.DigestAuth(self.username, self.password)
if not self._auth_method:
+ _LOGGER.error("Authentication method not detected, %s", response.status_code)
+ if response.headers:
+ _LOGGER.error("response.headers %s", response.headers)
response.raise_for_status()
def get_url(self, relative_url: str) -> str:
diff --git a/custom_components/hikvision_next/manifest.json b/custom_components/hikvision_next/manifest.json
index 42cc16c..ea88d2d 100644
--- a/custom_components/hikvision_next/manifest.json
+++ b/custom_components/hikvision_next/manifest.json
@@ -13,5 +13,5 @@
"xmltodict==0.13.0",
"requests-toolbelt==1.0.0"
],
- "version": "1.0.18"
+ "version": "1.0.19"
}
diff --git a/custom_components/hikvision_next/services.py b/custom_components/hikvision_next/services.py
new file mode 100644
index 0000000..8b5aa09
--- /dev/null
+++ b/custom_components/hikvision_next/services.py
@@ -0,0 +1,73 @@
+"Integration actions."
+
+from httpx import HTTPStatusError
+import voluptuous as vol
+
+from homeassistant.core import (
+ HomeAssistant,
+ ServiceCall,
+ ServiceResponse,
+ SupportsResponse,
+)
+from homeassistant.exceptions import HomeAssistantError
+
+from .const import (
+ ACTION_ISAPI_REQUEST,
+ ACTION_REBOOT,
+ ATTR_CONFIG_ENTRY_ID,
+ DATA_ISAPI,
+ DOMAIN,
+)
+
+ACTION_ISAPI_REQUEST_SCHEMA = vol.Schema(
+ {
+ vol.Required(ATTR_CONFIG_ENTRY_ID): str,
+ vol.Required("method"): str,
+ vol.Required("path"): str,
+ vol.Optional("payload"): str,
+ }
+)
+
+
+def setup_services(hass: HomeAssistant) -> None:
+ """Set up the services for the Hikvision component."""
+
+ async def handle_reboot(call: ServiceCall):
+ """Handle the reboot action call."""
+ entries = hass.data[DOMAIN]
+ entry_id = call.data.get(ATTR_CONFIG_ENTRY_ID)
+ isapi = entries[entry_id][DATA_ISAPI]
+ try:
+ await isapi.reboot()
+ except HTTPStatusError as ex:
+ raise HomeAssistantError(ex.response.content) from ex
+
+ async def handle_isapi_request(call: ServiceCall) -> ServiceResponse:
+ """Handle the custom ISAPI request action call."""
+ entries = hass.data[DOMAIN]
+ entry_id = call.data.get(ATTR_CONFIG_ENTRY_ID)
+ isapi = entries[entry_id][DATA_ISAPI]
+ method = call.data.get("method", "POST")
+ path = call.data["path"].strip("/")
+ payload = call.data.get("payload")
+ try:
+ response = await isapi.request(method, path, present="xml", data=payload)
+ except HTTPStatusError as ex:
+ if isinstance(ex.response.content, bytes):
+ response = ex.response.content.decode("utf-8")
+ else:
+ response = ex.response.content
+ return {"data": response.replace("\r", "")}
+
+ hass.services.async_register(
+ DOMAIN,
+ ACTION_REBOOT,
+ handle_reboot,
+ )
+ hass.services.async_register(
+ DOMAIN,
+ ACTION_ISAPI_REQUEST,
+ handle_isapi_request,
+ schema=ACTION_ISAPI_REQUEST_SCHEMA,
+ supports_response=SupportsResponse.ONLY,
+ )
diff --git a/custom_components/hikvision_next/services.yaml b/custom_components/hikvision_next/services.yaml
index e54d6cf..21d4e36 100644
--- a/custom_components/hikvision_next/services.yaml
+++ b/custom_components/hikvision_next/services.yaml
@@ -1,6 +1,6 @@
update_snapshot:
name: Update snapshot
- description: Update the snapshot file in an image entity.
+ description: Update the snapshot file in an image entity. Used by take_pictures_on_motion_detection blueprint.
target:
entity:
domain: image
@@ -13,3 +13,52 @@ update_snapshot:
example: "/media/hikvision_next/snapshots/2024-08/07/2024-08-07__11-54-19__camera01.jpg"
selector:
text:
+
+reboot:
+ name: Reboot
+ description: Reboot the device.
+ fields:
+ config_entry_id:
+ required: true
+ name: "Device"
+ description: "Permission needed - Remote: Shutdown/Reboot"
+ selector:
+ config_entry:
+ integration: hikvision_next
+
+isapi_request:
+ name: ISAPI request
+ description: "Send a custom ISAPI request to the device."
+ fields:
+ config_entry_id:
+ required: true
+ name: "Device"
+ description: "The Hikvision device."
+ selector:
+ config_entry:
+ integration: hikvision_next
+ method:
+ required: true
+ name: "HTTP method"
+ description: "WARNING: You do POST and PUT at your own risk!"
+ default: GET
+ selector:
+ select:
+ mode: dropdown
+ options:
+ - GET
+ - POST
+ - PUT
+ path:
+ required: true
+ name: "ISAPI path"
+ description: "The ISAPI endpoint to request. Example: /System/deviceInfo"
+ selector:
+ text:
+ payload:
+ required: false
+ name: "Request payload"
+ description: "XML data"
+ selector:
+ text:
+ multiline: true
diff --git a/custom_components/hikvision_next/switch.py b/custom_components/hikvision_next/switch.py
index c135ce0..8ce2baa 100644
--- a/custom_components/hikvision_next/switch.py
+++ b/custom_components/hikvision_next/switch.py
@@ -13,10 +13,10 @@
from .const import (
DOMAIN,
+ EVENT_IO,
EVENTS_COORDINATOR,
HOLIDAY_MODE,
SECONDARY_COORDINATOR,
- EVENT_IO,
)
from .isapi import EventInfo
@@ -73,7 +73,7 @@ def __init__(self, device_id: int, event: EventInfo, coordinator) -> None:
@property
def is_on(self) -> bool | None:
"""Return True if the binary sensor is on."""
- return self.coordinator.data.get(self.entity_id)
+ return self.coordinator.data.get(self.unique_id)
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on."""
@@ -93,6 +93,7 @@ async def async_turn_off(self, **kwargs: Any) -> None:
finally:
await self.coordinator.async_request_refresh()
+
class NVROutputSwitch(CoordinatorEntity, SwitchEntity):
"""Detection events switch."""
@@ -114,7 +115,7 @@ def __init__(self, coordinator, port_no: int) -> None:
@property
def is_on(self) -> bool | None:
"""Turn on."""
- return self.coordinator.data.get(self.entity_id) == "active"
+ return self.coordinator.data.get(self.unique_id) == "active"
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on."""
diff --git a/tests/conftest.py b/tests/conftest.py
index e35069e..67c51cb 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -76,7 +76,7 @@ def mock_device_endpoints(model):
def mock_isapi():
"""Mock ISAPI instance."""
- respx.get(f"{TEST_HOST}/ISAPI/System/status").respond(status_code=200)
+ respx.get(f"{TEST_HOST}/ISAPI/System/deviceInfo").respond(status_code=200)
isapi = ISAPI(**TEST_CLIENT)
return isapi
diff --git a/tests/fixtures/devices/DS-2CD2532F-IWS.json b/tests/fixtures/devices/DS-2CD2532F-IWS.json
new file mode 100644
index 0000000..43bfbd9
--- /dev/null
+++ b/tests/fixtures/devices/DS-2CD2532F-IWS.json
@@ -0,0 +1,1222 @@
+{
+ "data": {
+ "ISAPI": {
+ "System/deviceInfo": {
+ "response": {
+ "DeviceInfo": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "deviceName": "SCD14 #TUIN",
+ "deviceID": "c8e3b8e9-59b0-00b0-0000-e0b0f0b0f0a0",
+ "deviceDescription": "IPCamera",
+ "deviceLocation": "hangzhou",
+ "systemContact": "Hikvision.China",
+ "model": "DS-2CD2532F-IWS",
+ "serialNumber": "DS-2CD2532F-IWS00000000CCCH000000000",
+ "macAddress": "16:33:90:0d:9b:e8",
+ "firmwareVersion": "V5.2.0",
+ "firmwareReleasedDate": "build 140721",
+ "encoderVersion": "V5.0",
+ "encoderReleasedDate": "build 140714",
+ "bootVersion": "V1.3.4",
+ "bootReleasedDate": "100316",
+ "hardwareVersion": "0x0",
+ "deviceType": "IPCamera",
+ "telecontrolID": "2",
+ "supportBeep": "false",
+ "supportVideoLoss": "false"
+ }
+ }
+ },
+ "System/capabilities": {
+ "response": {
+ "DeviceCap": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "SysCap": {
+ "NetworkCap": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "isSupportWireless": "true",
+ "isSupportPPPoE": "true",
+ "isSupportBond": "false",
+ "isSupport802_1x": "false",
+ "isSupportNtp": "true",
+ "isSupportFtp": "true",
+ "isSupportUpnp": "true",
+ "isSupportDdns": "true",
+ "isSupportHttps": "false",
+ "SnmpCap": {
+ "isSupport": "true"
+ },
+ "isSupportIPFilter": "true",
+ "isSupportEZVIZ": "true",
+ "isSupportEhome": "false"
+ },
+ "IOCap": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "IOInputPortNums": "1",
+ "IOOutputPortNums": "1",
+ "isSupportStrobeLamp": "false"
+ },
+ "VideoCap": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "videoInputPortNums": "0",
+ "videoOutputPortNums": "0"
+ },
+ "AudioCap": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "audioInputNums": "1",
+ "audioOutputNums": "1"
+ }
+ },
+ "voicetalkNums": "1",
+ "isSupportSnapshot": "true",
+ "SecurityCap": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "supportUserNums": "32",
+ "userBondIpNums": "0",
+ "userBondMacNums": "0",
+ "isSupCertificate": "true"
+ },
+ "EventCap": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "isSupportHDFull": "true",
+ "isSupportHDError": "true",
+ "isSupportNicBroken": "true",
+ "isSupportIpConflict": "true",
+ "isSupportIllAccess": "true",
+ "isSupportViException": "false",
+ "isSupportViMismatch": "false",
+ "isSupportRecordException": "false",
+ "isSupportTriggerFocus": "false"
+ },
+ "SmartCap": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "isSupportROI": "true",
+ "isSupportFaceDetect": "false",
+ "isSupportIntelliTrace": "false",
+ "isSupportFieldDetection": "true",
+ "isSupportDefocusDetection": "false",
+ "isSupportAudioDetection": "false",
+ "isSupportSceneChangeDetection": "false",
+ "isSupportLineDetection": "true"
+ },
+ "WLAlarmCap": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "isSupportPIR": "false",
+ "isSupportWLSensors": "false",
+ "isSupportCallHelp": "false"
+ }
+ }
+ }
+ },
+ "System/IO/inputs/1/status": {
+ "response": {
+ "IOPortStatus": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "ioPortID": "1",
+ "ioPortType": "input",
+ "ioState": "inactive"
+ }
+ }
+ },
+ "System/IO/outputs/1/status": {
+ "response": {
+ "IOPortStatus": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "ioPortID": "1",
+ "ioPortType": "output",
+ "ioState": "active"
+ }
+ }
+ },
+ "System/Holidays": {
+ "status_code": 403
+ },
+ "System/Video/inputs/channels": {
+ "response": {
+ "VideoInputChannelList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "VideoInputChannel": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "1",
+ "inputPort": "1",
+ "name": "SCD14",
+ "videoFormat": "PAL"
+ }
+ }
+ }
+ },
+ "ContentMgmt/InputProxy/channels": {
+ "status_code": 403
+ },
+ "ContentMgmt/Storage": {
+ "response": {
+ "storage": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "hddList": {
+ "@version": "1.0",
+ "@xmlns": "http://www.hikvision.com/ver10/XMLSchema",
+ "@size": "8",
+ "hdd": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "1",
+ "hddName": "hdde",
+ "hddPath": null,
+ "hddType": "SATA",
+ "status": "ok",
+ "capacity": "30223",
+ "freeSpace": "0",
+ "property": "RW"
+ }
+ },
+ "nasList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "supportMountType": {
+ "@opt": "NFS,SMB/CIFS"
+ },
+ "authentication": {
+ "@opt": "SMB/CIFS"
+ }
+ },
+ "workMode": "quota"
+ }
+ }
+ },
+ "Security/adminAccesses": {
+ "response": {
+ "AdminAccessProtocolList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "AdminAccessProtocol": [
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "1",
+ "enabled": "true",
+ "protocol": "HTTP",
+ "portNo": "80"
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "2",
+ "enabled": "true",
+ "protocol": "HTTPS",
+ "portNo": "443"
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "3",
+ "enabled": "true",
+ "protocol": "DEV_MANAGE",
+ "portNo": "8000"
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "4",
+ "enabled": "true",
+ "protocol": "RTSP",
+ "portNo": "554"
+ }
+ ]
+ }
+ }
+ },
+ "Event/triggers": {
+ "response": {
+ "EventNotification": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTriggerList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTrigger": [
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "IO-1",
+ "eventType": "IO",
+ "eventDescription": "IO Event trigger Information",
+ "inputIOPortID": "1",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema"
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "VMD-1",
+ "eventType": "VMD",
+ "eventDescription": "VMD Event trigger Information",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTriggerNotification": [
+ {
+ "id": "IO-1",
+ "notificationMethod": "IO",
+ "notificationRecurrence": "beginning",
+ "outputIOPortID": "1"
+ },
+ {
+ "id": "record-1",
+ "notificationMethod": "record",
+ "videoInputID": "1",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "beep",
+ "notificationMethod": "beep",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "center",
+ "notificationMethod": "center",
+ "notificationRecurrence": "beginning"
+ }
+ ]
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "videoloss-1",
+ "eventType": "videoloss",
+ "eventDescription": "Videoloss Event trigger Information",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema"
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "tamper-1",
+ "eventType": "tamperdetection",
+ "eventDescription": "shelteralarm Event trigger Information",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema"
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "diskfull",
+ "eventType": "diskfull",
+ "eventDescription": "exception Information",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema"
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "diskerror",
+ "eventType": "diskerror",
+ "eventDescription": "exception Information",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTriggerNotification": {
+ "id": "beep",
+ "notificationMethod": "beep",
+ "notificationRecurrence": "beginning"
+ }
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "nicbroken",
+ "eventType": "nicbroken",
+ "eventDescription": "exception Information",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema"
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "ipconflict",
+ "eventType": "ipconflict",
+ "eventDescription": "exception Information",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema"
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "illaccess",
+ "eventType": "illaccess",
+ "eventDescription": "exception Information",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema"
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "Linedetection-1",
+ "eventType": "linedetection",
+ "eventDescription": "Linedetection Event trigger Information",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTriggerNotification": [
+ {
+ "id": "IO-1",
+ "notificationMethod": "IO",
+ "notificationRecurrence": "beginning",
+ "outputIOPortID": "1"
+ },
+ {
+ "id": "record-1",
+ "notificationMethod": "record",
+ "videoInputID": "1",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "center",
+ "notificationMethod": "center",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "FTP",
+ "notificationMethod": "FTP",
+ "notificationRecurrence": "beginning"
+ }
+ ]
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "fielddetection-1",
+ "eventType": "fielddetection",
+ "eventDescription": "fielddetection Event trigger Information",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema"
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "videomismatch",
+ "eventType": "videomismatch",
+ "eventDescription": "exception Information",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTriggerNotification": {
+ "id": "beep",
+ "notificationMethod": "beep",
+ "notificationRecurrence": "beginning"
+ }
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "badvideo",
+ "eventType": "badvideo",
+ "eventDescription": "exception Information",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema"
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "PIR",
+ "eventType": "PIR",
+ "eventDescription": "PIR Event trigger Information",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTriggerNotification": [
+ {
+ "id": "email",
+ "notificationMethod": "email",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "record-1",
+ "notificationMethod": "record",
+ "videoInputID": "1",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "beep",
+ "notificationMethod": "beep",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "center",
+ "notificationMethod": "center",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "FTP",
+ "notificationMethod": "FTP",
+ "notificationRecurrence": "beginning"
+ }
+ ]
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "callhelp",
+ "eventType": "callhelp",
+ "eventDescription": "exception Information",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTriggerNotification": [
+ {
+ "id": "email",
+ "notificationMethod": "email",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "record-1",
+ "notificationMethod": "record",
+ "videoInputID": "1",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "beep",
+ "notificationMethod": "beep",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "center",
+ "notificationMethod": "center",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "FTP",
+ "notificationMethod": "FTP",
+ "notificationRecurrence": "beginning"
+ }
+ ]
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "facedetection",
+ "eventType": "facedetection",
+ "eventDescription": "exception Information",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema"
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "WLSensor-1",
+ "eventType": "WLSensor",
+ "eventDescription": "WLSensor Event trigger Information",
+ "WLSensorID": "1",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTriggerNotification": [
+ {
+ "id": "email",
+ "notificationMethod": "email",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "record-1",
+ "notificationMethod": "record",
+ "videoInputID": "1",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "beep",
+ "notificationMethod": "beep",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "center",
+ "notificationMethod": "center",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "FTP",
+ "notificationMethod": "FTP",
+ "notificationRecurrence": "beginning"
+ }
+ ]
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "WLSensor-2",
+ "eventType": "WLSensor",
+ "eventDescription": "WLSensor Event trigger Information",
+ "WLSensorID": "2",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTriggerNotification": [
+ {
+ "id": "email",
+ "notificationMethod": "email",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "record-1",
+ "notificationMethod": "record",
+ "videoInputID": "1",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "beep",
+ "notificationMethod": "beep",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "center",
+ "notificationMethod": "center",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "FTP",
+ "notificationMethod": "FTP",
+ "notificationRecurrence": "beginning"
+ }
+ ]
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "WLSensor-3",
+ "eventType": "WLSensor",
+ "eventDescription": "WLSensor Event trigger Information",
+ "WLSensorID": "3",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTriggerNotification": [
+ {
+ "id": "email",
+ "notificationMethod": "email",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "record-1",
+ "notificationMethod": "record",
+ "videoInputID": "1",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "beep",
+ "notificationMethod": "beep",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "center",
+ "notificationMethod": "center",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "FTP",
+ "notificationMethod": "FTP",
+ "notificationRecurrence": "beginning"
+ }
+ ]
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "WLSensor-4",
+ "eventType": "WLSensor",
+ "eventDescription": "WLSensor Event trigger Information",
+ "WLSensorID": "4",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTriggerNotification": [
+ {
+ "id": "email",
+ "notificationMethod": "email",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "record-1",
+ "notificationMethod": "record",
+ "videoInputID": "1",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "beep",
+ "notificationMethod": "beep",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "center",
+ "notificationMethod": "center",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "FTP",
+ "notificationMethod": "FTP",
+ "notificationRecurrence": "beginning"
+ }
+ ]
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "WLSensor-5",
+ "eventType": "WLSensor",
+ "eventDescription": "WLSensor Event trigger Information",
+ "WLSensorID": "5",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTriggerNotification": [
+ {
+ "id": "email",
+ "notificationMethod": "email",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "record-1",
+ "notificationMethod": "record",
+ "videoInputID": "1",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "beep",
+ "notificationMethod": "beep",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "center",
+ "notificationMethod": "center",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "FTP",
+ "notificationMethod": "FTP",
+ "notificationRecurrence": "beginning"
+ }
+ ]
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "WLSensor-6",
+ "eventType": "WLSensor",
+ "eventDescription": "WLSensor Event trigger Information",
+ "WLSensorID": "6",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTriggerNotification": [
+ {
+ "id": "email",
+ "notificationMethod": "email",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "record-1",
+ "notificationMethod": "record",
+ "videoInputID": "1",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "beep",
+ "notificationMethod": "beep",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "center",
+ "notificationMethod": "center",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "FTP",
+ "notificationMethod": "FTP",
+ "notificationRecurrence": "beginning"
+ }
+ ]
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "WLSensor-7",
+ "eventType": "WLSensor",
+ "eventDescription": "WLSensor Event trigger Information",
+ "WLSensorID": "7",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTriggerNotification": [
+ {
+ "id": "email",
+ "notificationMethod": "email",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "record-1",
+ "notificationMethod": "record",
+ "videoInputID": "1",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "beep",
+ "notificationMethod": "beep",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "center",
+ "notificationMethod": "center",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "FTP",
+ "notificationMethod": "FTP",
+ "notificationRecurrence": "beginning"
+ }
+ ]
+ }
+ },
+ {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "WLSensor-8",
+ "eventType": "WLSensor",
+ "eventDescription": "WLSensor Event trigger Information",
+ "WLSensorID": "8",
+ "videoInputChannelID": "1",
+ "dynVideoInputChannelID": "1",
+ "EventTriggerNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "EventTriggerNotification": [
+ {
+ "id": "email",
+ "notificationMethod": "email",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "record-1",
+ "notificationMethod": "record",
+ "videoInputID": "1",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "beep",
+ "notificationMethod": "beep",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "center",
+ "notificationMethod": "center",
+ "notificationRecurrence": "beginning"
+ },
+ {
+ "id": "FTP",
+ "notificationMethod": "FTP",
+ "notificationRecurrence": "beginning"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ }
+ },
+ "Event/triggers/scenechangedetection-1": {
+ "status_code": 403
+ },
+ "Event/notification/httpHosts": {
+ "response": {
+ "HttpHostNotificationList": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "HttpHostNotification": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "1",
+ "url": "/api/hikvision",
+ "protocolType": "HTTP",
+ "parameterFormatType": "XML",
+ "addressingFormatType": "ipaddress",
+ "ipAddress": "1.0.0.248",
+ "portNo": "8123",
+ "userName": null,
+ "httpAuthenticationMethod": "none"
+ }
+ }
+ }
+ },
+ "Streaming/channels/101": {
+ "response": {
+ "StreamingChannel": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "101",
+ "channelName": "SCD14",
+ "enabled": "true",
+ "Transport": {
+ "maxPacketSize": "1000",
+ "ControlProtocolList": {
+ "ControlProtocol": [
+ {
+ "streamingTransport": "RTSP"
+ },
+ {
+ "streamingTransport": "HTTP"
+ },
+ {
+ "streamingTransport": "SHTTP"
+ }
+ ]
+ },
+ "Unicast": {
+ "enabled": "true",
+ "rtpTransportType": "RTP/TCP"
+ },
+ "Multicast": {
+ "enabled": "true",
+ "destIPAddress": "0.0.0.0",
+ "videoDestPortNo": "8600",
+ "audioDestPortNo": "8600"
+ },
+ "Security": {
+ "enabled": "true"
+ }
+ },
+ "Video": {
+ "enabled": "true",
+ "videoInputChannelID": "1",
+ "videoCodecType": "H.264",
+ "videoScanType": "progressive",
+ "videoResolutionWidth": "1920",
+ "videoResolutionHeight": "1080",
+ "videoQualityControlType": "CBR",
+ "constantBitRate": "4096",
+ "fixedQuality": "60",
+ "maxFrameRate": "2500",
+ "keyFrameInterval": "2000",
+ "snapShotImageType": "JPEG",
+ "H264Profile": "Main",
+ "GovLength": "50",
+ "SVC": {
+ "enabled": "false"
+ },
+ "PacketType": [
+ "PS",
+ "RTP"
+ ],
+ "smoothing": "50"
+ },
+ "Audio": {
+ "enabled": "true",
+ "audioInputChannelID": "1",
+ "audioCompressionType": "G.711ulaw"
+ }
+ }
+ }
+ },
+ "Streaming/channels/102": {
+ "response": {
+ "StreamingChannel": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "102",
+ "channelName": "SCD14",
+ "enabled": "true",
+ "Transport": {
+ "maxPacketSize": "1000",
+ "ControlProtocolList": {
+ "ControlProtocol": [
+ {
+ "streamingTransport": "RTSP"
+ },
+ {
+ "streamingTransport": "HTTP"
+ },
+ {
+ "streamingTransport": "SHTTP"
+ }
+ ]
+ },
+ "Unicast": {
+ "enabled": "true",
+ "rtpTransportType": "RTP/TCP"
+ },
+ "Multicast": {
+ "enabled": "true",
+ "destIPAddress": "0.0.0.0",
+ "videoDestPortNo": "8600",
+ "audioDestPortNo": "8600"
+ },
+ "Security": {
+ "enabled": "true"
+ }
+ },
+ "Video": {
+ "enabled": "true",
+ "videoInputChannelID": "1",
+ "videoCodecType": "MJPEG",
+ "videoScanType": "progressive",
+ "videoResolutionWidth": "640",
+ "videoResolutionHeight": "480",
+ "videoQualityControlType": "VBR",
+ "constantBitRate": "256",
+ "fixedQuality": "60",
+ "vbrUpperCap": "256",
+ "vbrLowerCap": "32",
+ "maxFrameRate": "1200",
+ "keyFrameInterval": "4166",
+ "snapShotImageType": "JPEG",
+ "H264Profile": "Main",
+ "GovLength": "50",
+ "SVC": {
+ "enabled": "false"
+ },
+ "PacketType": [
+ "PS",
+ "RTP"
+ ],
+ "smoothing": "50"
+ },
+ "Audio": {
+ "enabled": "true",
+ "audioInputChannelID": "1",
+ "audioCompressionType": "G.711ulaw"
+ }
+ }
+ }
+ },
+ "Streaming/channels/103": {
+ "status_code": 500
+ },
+ "Streaming/channels/104": {
+ "status_code": 500
+ },
+ "System/IO/inputs/1": {
+ "response": {
+ "IOInputPort": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "1",
+ "enabled": "true",
+ "triggering": "high",
+ "name": null
+ }
+ }
+ },
+ "System/Video/inputs/channels/1/motionDetection": {
+ "response": {
+ "MotionDetection": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "enabled": "true",
+ "enableHighlight": "false",
+ "samplingInterval": "2",
+ "startTriggerTime": "500",
+ "endTriggerTime": "500",
+ "regionType": "grid",
+ "Grid": {
+ "rowGranularity": "18",
+ "columnGranularity": "22"
+ },
+ "MotionDetectionLayout": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "sensitivityLevel": "20",
+ "layout": {
+ "gridMap": "fffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffcfffffc"
+ }
+ }
+ }
+ }
+ },
+ "System/Video/inputs/channels/1/videoLoss": {
+ "response": {
+ "VideoLoss": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "enabled": "false"
+ }
+ }
+ },
+ "System/Video/inputs/channels/1/tamperDetection": {
+ "response": {
+ "TamperDetection": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "enabled": "false",
+ "normalizedScreenSize": {
+ "normalizedScreenWidth": "704",
+ "normalizedScreenHeight": "576"
+ },
+ "TamperDetectionRegionList": {
+ "TamperDetectionRegion": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "1",
+ "enabled": "false",
+ "sensitivityLevel": "0",
+ "RegionCoordinatesList": null
+ }
+ }
+ }
+ }
+ },
+ "Smart/LineDetection/1": {
+ "response": {
+ "LineDetection": {
+ "id": "1",
+ "enabled": "true",
+ "normalizedScreenSize": {
+ "normalizedScreenWidth": "1000",
+ "normalizedScreenHeight": "1000"
+ },
+ "LineItemList": {
+ "@size": "1",
+ "LineItem": {
+ "id": "1",
+ "enabled": "false",
+ "sensitivityLevel": "50",
+ "directionSensitivity": "any",
+ "CoordinatesList": {
+ "Coordinates": [
+ {
+ "positionX": "231",
+ "positionY": "835"
+ },
+ {
+ "positionX": "740",
+ "positionY": "832"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "Smart/FieldDetection/1": {
+ "response": {
+ "FieldDetection": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "1",
+ "enabled": "false",
+ "startTriggerTime": "500",
+ "endTriggerTime": "500",
+ "normalizedScreenSize": {
+ "normalizedScreenWidth": "1000",
+ "normalizedScreenHeight": "1000"
+ },
+ "FieldDetectionRegionList": {
+ "@size": "1",
+ "FieldDetectionRegion": {
+ "@version": "2.0",
+ "@xmlns": "http://www.hikvision.com/ver20/XMLSchema",
+ "id": "1",
+ "enabled": "false",
+ "sensitivityLevel": "50",
+ "timeThreshold": "0",
+ "objectOccupation": "1"
+ }
+ }
+ }
+ }
+ },
+ "WLAlarm/PIR": {
+ "status_code": 403
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/test_init.py b/tests/test_init.py
index d31666d..c032df6 100644
--- a/tests/test_init.py
+++ b/tests/test_init.py
@@ -15,6 +15,7 @@
"DS-2CD2386G2-IU",
"DS-2CD2146G2-ISU",
"DS-2CD2443G0-IW",
+ "DS-2CD2532F-IWS",
"DS-7616NI-K2",
"DS-7616NI-Q2",
"DS-7732NI-M4",
diff --git a/tests/test_pir.py b/tests/test_pir.py
index b235762..a4100d6 100644
--- a/tests/test_pir.py
+++ b/tests/test_pir.py
@@ -4,12 +4,13 @@
import pytest
from http import HTTPStatus
from homeassistant.core import HomeAssistant
+from custom_components.hikvision_next.const import DOMAIN, EVENT_PIR
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from pytest_homeassistant_custom_component.common import MockConfigEntry
import homeassistant.helpers.entity_registry as er
from custom_components.hikvision_next.notifications import EventNotificationsView
from tests.test_notifications import mock_event_notification
-from tests.conftest import TEST_CONFIG
+from tests.conftest import TEST_HOST
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TURN_OFF,
@@ -62,7 +63,7 @@ async def test_pir_switch(hass: HomeAssistant, init_integration: MockConfigEntry
assert (switch := hass.states.get(entity_id))
assert switch.state == STATE_ON
- url = f"{TEST_CONFIG['host']}/ISAPI/WLAlarm/PIR"
+ url = f"{TEST_HOST}/ISAPI/WLAlarm/PIR"
endpoint = respx.put(url)
# switch to off
@@ -73,3 +74,31 @@ async def test_pir_switch(hass: HomeAssistant, init_integration: MockConfigEntry
blocking=True,
)
assert endpoint.called
+
+
+@pytest.mark.parametrize("init_integration", ["DS-2CD2443G0-IW", "DS-2CD2532F-IWS", "DS-2CD2386G2-IU"], indirect=True)
+async def test_pir_support_detection(
+ hass: HomeAssistant,
+ init_integration: MockConfigEntry,
+) -> None:
+ """Test PIR entities creation """
+
+ device_data = {
+ "DS-2CD2443G0-IW": {
+ "isSupportPIR": True,
+ },
+ "DS-2CD2532F-IWS": {
+ "isSupportPIR": False,
+ },
+ "DS-2CD2386G2-IU": {
+ "isSupportPIR": False,
+ }
+ }
+
+ entry = init_integration
+ isapi = hass.data[DOMAIN][entry.entry_id]["isapi"]
+ data = device_data[init_integration.title]
+ pir_events = [
+ s for s in isapi.supported_events if (s.event_id == EVENT_PIR)
+ ]
+ assert (len(pir_events) == 1) == data["isSupportPIR"]
diff --git a/tests/test_services.py b/tests/test_services.py
new file mode 100644
index 0000000..bb51b98
--- /dev/null
+++ b/tests/test_services.py
@@ -0,0 +1,32 @@
+"""Tests for actions."""
+
+import pytest
+import respx
+from homeassistant.core import HomeAssistant
+from pytest_homeassistant_custom_component.common import MockConfigEntry
+from custom_components.hikvision_next.const import (
+ ACTION_REBOOT,
+ ATTR_CONFIG_ENTRY_ID,
+ DOMAIN,
+)
+from tests.conftest import TEST_HOST
+
+
+@respx.mock
+@pytest.mark.parametrize("init_integration", ["DS-7608NXI-I2"], indirect=True)
+async def test_reboot_action(hass: HomeAssistant, init_integration: MockConfigEntry) -> None:
+ """Test sending reboot request on reboot action."""
+
+ mock_config_entry = init_integration
+
+ url = f"{TEST_HOST}/ISAPI/System/reboot"
+ endpoint = respx.put(url).respond()
+
+ await hass.services.async_call(
+ DOMAIN,
+ ACTION_REBOOT,
+ {ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id},
+ blocking=True,
+ )
+
+ assert endpoint.called
diff --git a/tests/test_switch.py b/tests/test_switch.py
index 19f4eae..e3499e9 100644
--- a/tests/test_switch.py
+++ b/tests/test_switch.py
@@ -4,7 +4,7 @@
import pytest
import httpx
from homeassistant.core import HomeAssistant
-from tests.conftest import TEST_CONFIG
+from tests.conftest import TEST_HOST
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from pytest_homeassistant_custom_component.common import MockConfigEntry
import homeassistant.helpers.entity_registry as er
@@ -64,7 +64,7 @@ def update_side_effect(request, route):
raise AssertionError("Request content does not match expected payload")
return httpx.Response(200)
- url = f"{TEST_CONFIG['host']}/ISAPI/ContentMgmt/InputProxy/channels/1/video/videoLoss"
+ url = f"{TEST_HOST}/ISAPI/ContentMgmt/InputProxy/channels/1/video/videoLoss"
endpoint = respx.put(url).mock(side_effect=update_side_effect)
# do not call if already on