HA Received the Alarm Server Notification but no binary_sensor is seen
Hello,
I have a HIK camera - DS-2CD1643G2-LIZSU.
I have enabled motion detection. The camera has the capability to detect whether it's a vehicle or a person.
I correctly receive the motion notification from the camera but an exception is raised by Home Assistant.
In the Home Assistant log, here is what I have:
2025-01-14 23:12:25.208 DEBUG (MainThread) [custom_components.hikvision_next.notifications] --- Incoming event notification ---
2025-01-14 23:12:25.208 DEBUG (MainThread) [custom_components.hikvision_next.notifications] Source: 192.169.1.30
2025-01-14 23:12:25.208 DEBUG (MainThread) [custom_components.hikvision_next.notifications] request headers: <CIMultiDictProxy('Content-Type': 'multipart/form-data; boundary=boundary', 'Host': '192.169.1.1:8123', 'Connection': 'close', 'Content-Length': '907')>
2025-01-14 23:12:25.209 DEBUG (MainThread) [custom_components.hikvision_next.notifications] part headers: {'Content-Disposition': 'form-data; name="MoveDetection.xml"; filename="MoveDetection.xml"', 'Content-Type': 'application/xml', 'Content-Length': '737'}
2025-01-14 23:12:25.209 DEBUG (MainThread) [custom_components.hikvision_next.notifications] alert info: <?xml version="1.0" encoding="UTF-8"?>
<EventNotificationAlert version="2.0" xmlns="http://www.hikvision.com/ver20/XMLSchema">
<ipAddress>192.169.1.30</ipAddress>
<portNo>8123</portNo>
<protocol>HTTP</protocol>
<macAddress>5c:34:5b:e4:de:14</macAddress>
<channelID>1</channelID>
<dateTime>2025-01-14T23:12:25+01:00</dateTime>
<activePostCount>1</activePostCount>
<eventType>VMD</eventType>
<eventState>active</eventState>
<eventDescription>Motion alarm</eventDescription>
<channelName>Camera 01</channelName>
<targetType>human</targetType>
<targetInfo>
<targetID>1</targetID>
<targetRect>
<X>0.245</X>
<Y>0.018</Y>
<width>0.268</width>
<height>0.583</height>
</targetRect>
</targetInfo>
</EventNotificationAlert>
2025-01-14 23:12:25.210 DEBUG (MainThread) [custom_components.hikvision_next.notifications] **Alert: AlertInfo(channel_id=1, io_port_id=0, event_id='motiondetection', device_serial_no=None, mac='5c:34:5b:e4:de:14', region_id=0, detection_target=None)
2025-01-14 23:12:25.210 DEBUG (MainThread) [custom_components.hikvision_next.notifications] UNIQUE_ID: binary_sensor.ds_2cd1643g2_lizsu20240906aawrfn2343166_1_motiondetection**
2025-01-14 23:12:25.210 WARNING (MainThread) [custom_components.hikvision_next.notifications] Cannot process incoming event Entity not found None
2025-01-14 23:13:02.612 DEBUG (MainThread) [custom_components.hikvision_next.isapi.isapi] --- [GET] http://192.169.1.30/ISAPI/System/IO/outputs/1/status
2025-01-14 23:13:02.612 DEBUG (MainThread) [custom_components.hikvision_next.isapi.isapi]
{'IOPortStatus': {'@version': '2.0', '@xmlns': 'http://www.hikvision.com/ver20/XMLSchema', 'ioPortID': '1', 'ioPortType': 'output', 'ioState': 'inactive'}}
2025-01-14 23:13:02.617 DEBUG (MainThread) [custom_components.hikvision_next.isapi.isapi] --- [GET] http://192.169.1.30/ISAPI/ContentMgmt/Storage
2025-01-14 23:13:02.617 DEBUG (MainThread) [custom_components.hikvision_next.isapi.isapi]
{'storage': {'@version': '2.0', '@xmlns': 'http://www.hikvision.com/ver20/XMLSchema', 'hddList': {'@version': '1.0', '@xmlns': 'http://www.hikvision.com/ver10/XMLSchema', '@size': '8'}, 'workMode': 'quota'}}
2025-01-14 23:13:02.617 DEBUG (MainThread) [custom_components.hikvision_next.coordinator] Finished fetching hikvision_next data in 0.018 seconds (success: True)
As we can see, it is trying to update the sensor binary_sensor.ds_2cd1643g2_lizsu20240906aawrfn2343166_1_motiondetection.
This sensor is not recognized by the system. In fact, I don’t have any sensors from HIK that are being detected.
Is there any way to update the code in order to create the sensor ?
Sincerely,
Make sure if you have enabled Notify Surveillance Center https://github.com/maciej-or/hikvision_next/issues/257
please send diagnostic data if the problem persists https://github.com/maciej-or/hikvision_next/wiki/How-to-get-Diagnostic-Data
Thank you for your answer.
Notification Center is activated as HA get the notifications.
The problem is on HA which does not generate the Motion sensor.
please send diagnostic data https://github.com/maciej-or/hikvision_next/wiki/How-to-get-Diagnostic-Data
Hello @maciej-or ,
Thank you for your answer. Please find attached the diagnostic data. config_entry-hikvision_next-01JHKB62HJR4WXYJ5D3XB0GV0P.json
If it can help, as you see no sensor for movement has been detected.
When HA gets the camera event : AlertInfo(channel_id=1, io_port_id=0, event_id='motiondetection', device_serial_no=None, mac='5c:34:5b:e4:de:14', region_id=0, detection_target=None
It tries to update the sensor : UNIQUE_ID: binary_sensor.ds_2cd1643g2_lizsu20240906aawrfn2343166_1_motiondetection which is not known by HA system.
I made some searches in your code.
In ISAPI setup, two requests gets an error :
"Event/triggers": { "status_code": 500 },
and
"Event/triggers/scenechangedetection-1": { "status_code": 403 },
In the log:
2025-01-17 17:18:22.741 INFO (MainThread) [custom_components.hikvision_next.isapi.isapi] --- [GET] http://192.169.1.30/ISAPI/Event/triggers/scenechangedetection-1 Client error '403 Forbidden' for url 'http://192.169.1.30/ISAPI/Event/triggers/scenechangedetection-1'
I made some search via ISAPI commands:
/ISAPI/Event/capabilities
gives: <EventCap xmlns="http://www.hikvision.com/ver20/XMLSchema" version="2.0"> <isSupportHDFull>false</isSupportHDFull> <isSupportHDError>true</isSupportHDError> <isSupportNicBroken>true</isSupportNicBroken> <isSupportIpConflict>true</isSupportIpConflict> <isSupportIllAccess>true</isSupportIllAccess> <isSupportViException>false</isSupportViException> <isSupportViMismatch>false</isSupportViMismatch> <isSupportRecordException>false</isSupportRecordException> <isSupportTriggerFocus>false</isSupportTriggerFocus> <isSupportMotionDetection>true</isSupportMotionDetection> <isSupportVideoLoss>false</isSupportVideoLoss> <isSupportTamperDetection>true</isSupportTamperDetection> </EventCap>
so <isSupportMotionDetection>true</isSupportMotionDetection>
I Get configuration capability of alarm linkage action
Request URL: GET /ISAPI/Event/triggersCap
it returns: <EventTriggersCap xmlns="http://www.hikvision.com/ver20/XMLSchema" version="2.0"> <DiskerrorTriggerCap> <isSupportCenter>true</isSupportCenter> <isSupportIO>true</isSupportIO> <isSupportEmail>true</isSupportEmail> </DiskerrorTriggerCap> <StorageDetectionTriggerCap> <isSupportCenter>true</isSupportCenter> <isSupportIO>true</isSupportIO> <isSupportEmail>true</isSupportEmail> </StorageDetectionTriggerCap> <NicbrokenTriggerCap> <isSupportIO>true</isSupportIO> </NicbrokenTriggerCap> <IpconflictTriggerCap> <isSupportIO>true</isSupportIO> </IpconflictTriggerCap> <IllaccesTriggerCap> <isSupportCenter>true</isSupportCenter> <isSupportIO>true</isSupportIO> <isSupportEmail>true</isSupportEmail> </IllaccesTriggerCap> <IOTriggerCap> <isSupportCenter>true</isSupportCenter> <isSupportRecord>true</isSupportRecord> <isSupportBeep>true</isSupportBeep> <isSupportIO>true</isSupportIO> <isSupportFTP>true</isSupportFTP> <isSupportEmail>true</isSupportEmail> </IOTriggerCap> <MotionDetectionTriggerCap> <isSupportCenter>true</isSupportCenter> <isSupportRecord>true</isSupportRecord> <isSupportBeep>true</isSupportBeep> <isSupportIO>true</isSupportIO> <isSupportFTP>true</isSupportFTP> <isSupportEmail>true</isSupportEmail> </MotionDetectionTriggerCap> <TamperDetectionTriggerCap> <isSupportCenter>true</isSupportCenter> <isSupportIO>true</isSupportIO> <isSupportEmail>true</isSupportEmail> </TamperDetectionTriggerCap> <isSupportAudioAction>true</isSupportAudioAction> <isSupportAudioTrigger>true</isSupportAudioTrigger> </EventTriggersCap>
And I have a strange behavior on
ISAPI Event/triggers
It returns <ResponseStatus xmlns="http://www.hikvision.com/ver20/XMLSchema" version="2.0"> <requestURL/> <statusCode>3</statusCode> <statusString>Device Error</statusString> <subStatusCode>deviceError</subStatusCode> <description> deviceError. isapi_get_event_triggerinfo error: diskfull. </description> </ResponseStatus>
Why deviceError. isapi_get_event_triggerinfo error: diskfull. ?
I try to remove my Sd Card --> idem. I Format it --> idem
I think that the error in ISAPI scan is in these lines:
# Get events from Event/triggers
event_triggers = await self.request(GET, "Event/triggers")
event_notification = event_triggers.get("EventNotification")
if event_notification:
available_events = deep_get(event_notification, "EventTriggerList.EventTrigger", [])
else:
available_events = deep_get(event_triggers, "EventTriggerList.EventTrigger", [])
for event_trigger in available_events:
if event := create_event_info(event_trigger):
events.append(event)
# some devices do not have scenechangedetection in Event/triggers
if not [e for e in events if e.id == "scenechangedetection"]:
is_supported = str_to_bool(deep_get(system_capabilities, "SmartCap.isSupportSceneChangeDetection", False))
if is_supported:
event_trigger = await self.request(GET, "Event/triggers/scenechangedetection-1")
event_trigger = deep_get(event_trigger, "EventTrigger", {})
if event := create_event_info(event_trigger):
events.append(event)
# multichannel camera needs to fetch events for each channel
if self.capabilities.is_multi_channel:
channels_capabilities = await self.request(GET, "Event/channels/capabilities")
channel_events = deep_get(channels_capabilities, "ChannelEventCapList.ChannelEventCap", [])
for event_cap in channel_events:
event_types = deep_get(event_cap, "eventType").get("@opt", "").split(",")
channel_id = int(event_cap.get("channelID"))
for event_type in event_types:
event_id = event_type.lower()
if event_id in EVENTS_ALTERNATE_ID:
event_id = EVENTS_ALTERNATE_ID[event_id]
if event_id not in EVENTS:
continue
if not [e for e in events if (e.id == event_id and e.channel_id == channel_id)]:
event_trigger = await self.request(GET, f"Event/triggers/{event_id}-{channel_id}")
event_trigger = deep_get(event_trigger, "EventTrigger", {})
if event := create_event_info(event_trigger):
events.append(event)
return events
I know that
1° Event/triggers --> Return error (see previous post) 2° scenechangedetection is not know by my ISAPI request on the camera 3° if self.capabilities.is_multi_channel: I don't think that the camera is multichannel... but if a do GET Event/channels/capabilities
I get this:
<ChannelEventCapList xmlns="http://www.hikvision.com/ver20/XMLSchema" version="2.0"> <ChannelEventCap> <eventType opt="motionDetection,VMD,tamperDetection,Shelteralarm,storageDetection,diskerror,nicbroken,ipconflict,illaccess,IO"/> <shieldEventType opt=""/> <channelID>1</channelID> </ChannelEventCap> </ChannelEventCapList>
...
so maybe that my camera is not multichannel but your code need to go in to load the <channelID>1</channelID> in all case.
If you want to access to camera to make tests.
82.64.108.28 Port 6060
user: maciejor password: hikvision2025 (you have to change it at the first connection)
Nothing sensible, you can connect. Will be reset after.
Thanks, direct access explained a lot. Indeed "Event/triggers" should provide information about all events supported by the cam. Apparently Hikvision enginers decided to break backward compatibility and get any event details in separate requests like /ISAPI/Event/triggers/VMD-1 for motion detection and /ISAPI/Event/triggers/tamper-1 Will grab details tmw and add support for that. I'm afraid more devices will work this way.
I couldn't find any mention of this update in firmware changelogs, but they likely implemented it without any announcements. I read that they've released a new SDK, so they might migrate to that as well.
You'll be more effective at fixing the bug. I tried editing isapi.py to directly create the sensor from /ISAPI/Event/triggers/VMD, but since I'm not familiar with your code, I couldn't get it to work in a few tentatives.
Saw that Event/triggers/VMD works (without the channel ID).... It may be more robust for multichannel models (iterate on channels ?) Maybe the good way is to get Event/channels/capabilities and iterate with Event/triggers/EVENT_TYPE
with EVENT_TYPE : motionDetection,VMD,tamperDetection,Shelteralarm,storageDetection,diskerror,nicbroken,ipconflict (from Event/channels/capabilities)
EventNotification changed to EventTriggerNotification and EventTriggerList to EventTriggerNotificationList
I am sure that a new step for this kind of sensor configuration should not be so hard. Maybe just detect the 500 error and switching the case...
I tried few minutes something like that:
# LUCKY TRY
event_triggers = await self.request(GET, "Event/triggers/VMD")
event_notification = event_triggers.get("EventTriggerNotification")
if event_notification:
available_events = deep_get(event_notification, "EventTriggerNotificationList.EventTriggerNotification", [])
else:
available_events = deep_get(event_triggers, "EventTriggerNotificationList.EventTriggerNotification", [])
for event_trigger in available_events:
if event := create_event_info(event_trigger):
events.append(event)
event_triggers = await self.request(GET, "Event/triggers/VMD")
event_notification = event_triggers.get("EventNotification")
if event_notification:
available_events = deep_get(event_notification, "EventTriggerList.EventTrigger", [])
else:
available_events = deep_get(event_triggers, "EventTriggerList.EventTrigger", [])
for event_trigger in available_events:
if event := create_event_info(event_trigger):
events.append(event)
### END OF LUCKY TRY
Unlucky ! I have to dig into your structure to get it working. You should get it fast... If you need some help. I can looking deeply in deep_get and create_event_info which are still black box for me.
I'm sure you already have this, but it might be useful for anyone looking to explore the ISAPI protocol for HIK. Attached is the latest version of the API I found (note that Event/triggers isn't documented in this one).
Thanks a lot!
Your insights are right, approach for multichannel cams could work. BTW this PDF is from 2019. I also have ones from 2020 and 2022. I wonder what the latest versions look like. Anyway I'm going to add this way to get supported events.
`async def parse_event_request(self, request: web.Request) -> str: """ Извлекает XML-содержимое из входящего запроса, который может быть обычным текстовым или multipart/form-data.
- Если запрос содержит XML напрямую (например, Content-Type: application/xml), то данные декодируются как XML.
- Если Content-Type равен multipart/form-data, то перебираются все части:
- Сначала ищется часть, где Content-Type содержит 'xml', и используется её содержимое.
- Если такой части нет, ищется часть с 'json', затем пытаемся преобразовать JSON в XML.
- Если ни XML, ни JSON не найдены, выбрасывается исключение.
"""
# Считываем все данные из запроса
data = await request.read()
# Получаем заголовок Content-Type из запроса
content_type_header = request.headers.get(CONTENT_TYPE)
if not content_type_header:
raise ValueError("Missing Content-Type header")
# Приводим заголовок к нижнему регистру и убираем лишние пробелы для унификации
content_type_header = content_type_header.strip().lower()
_LOGGER.debug("Request headers: %s", request.headers)
xml = None # Здесь будет храниться извлечённое XML-содержимое
# Если Content-Type содержит 'xml', значит данные уже в виде XML
if "xml" in content_type_header:
try:
# Декодируем данные в строку, используя UTF-8 (при необходимости ошибки заменяются)
xml = data.decode("utf-8", errors="replace")
_LOGGER.debug("Direct XML decoded: %s", xml)
except Exception as e:
_LOGGER.error("Error decoding XML: %s", e)
raise ValueError("Error decoding XML") from e
# Если Content-Type указывает на multipart/form-data, нужно разобрать каждую часть запроса
elif "multipart/form-data" in content_type_header:
try:
# Инициализируем декодер для multipart-данных
decoder = MultipartDecoder(data, content_type_header)
except Exception as e:
_LOGGER.error("Error decoding multipart/form-data: %s", e)
raise ValueError(f"Error decoding multipart/form-data: {content_type_header}") from e
# Перебираем все части запроса, пытаясь найти ту, что содержит XML
for part in decoder.parts:
# Получаем заголовок Content-Type для данной части и приводим его к нижнему регистру
part_ct = part.headers.get(b"Content-Type", b"").decode("ascii", errors="replace").lower()
_LOGGER.debug("Multipart part Content-Type: %s", part_ct)
if "xml" in part_ct:
try:
# Если в части найден XML, декодируем её текст
xml = part.text
_LOGGER.debug("XML part found: %s", xml)
except Exception as e:
_LOGGER.error("Error decoding XML part: %s", e)
continue # Если не удалось декодировать, переходим к следующей части
break # XML найден, можно завершить перебор частей
# Если XML не найден, пытаемся найти часть с JSON и преобразовать её в XML
if not xml:
for part in decoder.parts:
part_ct = part.headers.get(b"Content-Type", b"").decode("ascii", errors="replace").lower()
if "json" in part_ct:
try:
json_text = part.text
_LOGGER.debug("JSON part found: %s", json_text)
import json
# Пытаемся загрузить текст как JSON-объект
json_data = json.loads(json_text)
# Преобразуем JSON в XML с использованием вспомогательной функции
xml = json_to_xml(json_data, root_tag="AlertInfo")
_LOGGER.debug("Converted JSON to XML: %s", xml)
except Exception as e:
_LOGGER.error("Error converting JSON to XML: %s", e)
# Если преобразование не удалось, используем исходный JSON как запасной вариант
xml = json_text
break
else:
# Если Content-Type не соответствует ни XML, ни multipart, выбрасываем исключение
raise ValueError(f"Unexpected event Content-Type {content_type_header}")
# Если после всех попыток xml так и не был извлечён, выбрасываем исключение с описанием ошибки
if not xml:
raise ValueError(f"Could not extract XML from event with Content-Type {content_type_header}")
return xml
def json_to_xml(json_obj: dict, root_tag: str = "root") -> str: """ Преобразует объект JSON (словарь) в строку XML с указанным корневым тегом.
Использует рекурсивную функцию build_element для обхода вложенных структур:
- Если данные являются словарем, для каждого ключа создаётся подэлемент.
- Если данные являются списком, для каждого элемента списка создаётся тег 'item'.
- В противном случае данные преобразуются в строку и устанавливаются как текст элемента.
"""
import xml.etree.ElementTree as ET
def build_element(elem: ET.Element, data: Any):
# Если данные представляют собой словарь, обрабатываем каждый ключ-значение
if isinstance(data, dict):
for key, val in data.items():
# Создаем подэлемент, нормализуя имя тега (удаляем пробелы)
child = ET.SubElement(elem, key.strip().replace(" ", "_"))
build_element(child, val)
# Если данные представляют собой список, создаем элемент "item" для каждого элемента списка
elif isinstance(data, list):
for item in data:
item_elem = ET.SubElement(elem, "item")
build_element(item_elem, item)
else:
# Если данные – простой тип (число, строка и т.д.), устанавливаем его как текст элемента
elem.text = str(data)
# Создаем корневой элемент XML с заданным тегом
root = ET.Element(root_tag)
# Рекурсивно строим дерево XML из JSON-объекта
build_element(root, json_obj)
# Преобразуем XML-дерево в строку (без байтовых данных)
xml_string = ET.tostring(root, encoding="unicode")
return xml_string
` С такой ошибкой я сталкиваюсь, выше решение от ИИ, не знаю как внедрить Cannot process incoming event Unexpected event Content-Type multipart/form-data; boundary=boundary
Hello
Can you get this in english ? I am not sure that your problem is relative to the discussion above.
By the way @maciej-or do you plan to provide a new delivery with the update ? (Sorry to ask, I know that we all have a lot to do).
Otherwise I will try to dive in your code.
@apande if you use AI to generate code let it to translate comments and the whole post, we speak english here if you haven't noticed, furthermore your post is not related to the topic of the thread
@GeoCodeTout feel free to contribute, if you send a PR I will review the code and help if needed. I shared you the ISAPI docs.
Have the same issue on my fresh DS-2CD2347G3-LIY camera. Firmware Version: V5.8.13 build 250725
The fix is extra simple here in isapi/isapi.py:
# multichannel camera needs to fetch events for each channel
if self.capabilities.is_multi_channel or not event_triggers:
And now I have 5 new controls and some triggers:
Can use at least fielddetection / Intrusion (Field Detection) now for motion detection, but unfortunately /ISAPI/Event/triggers/motiondetection-1 or /ISAPI/Event/triggers/motiondetection call returns
Client error '403 Forbidden' for url 'http://192.168.1.64/ISAPI/Event/triggers/motiondetection-1'
although motionDetection is supported by my camera and "Motion alarm" with targetType=human is sent.
Log file with alerts after the fix above + "dev" branch changes: home-assistant_2025-09-17T03-53-08.079Z.log