pergolafabio / Hikvision-Addons

Home Assistant: Hikvision Doorbell
178 stars 40 forks source link

DS-K1T502DBFWX-C - Not supported #177

Open seederp2p opened 8 months ago

seederp2p commented 8 months ago

What happened?

I'm trying to add my DS-K1T502DBFWX-C without success.

Add-on

Hikvision Doorbell

What version of the add-on are you running?

3.0.12

Installation type

Home Assistant add-on

Relevant log output

2024-01-05 18:59:28.255 | DEBUG    | config:load_mqtt_config:129 - Loading MQTT configuration from supervisor
2024-01-05 18:59:28.255 | DEBUG    | config:mqtt_config_from_supervisor:36 - Requesting MQTT service configuration to supervisor
2024-01-05 18:59:28.273 | INFO     | sdk.utils:loadSDK:44 - Using OS: Linux with architecture: x86_64
loop[2] find 2 mac and 0 ip
Traceback (most recent call last):
  File "/app/main.py", line 96, in <module>
    asyncio.run(main())
  File "/usr/local/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/local/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "/app/main.py", line 47, in main
    doorbell.authenticate()
  File "/app/doorbell.py", line 71, in authenticate
    self._type = DeviceType(self._device_info.wDevType)
  File "/usr/local/lib/python3.10/enum.py", line 385, in __call__
    return cls.__new__(cls, value)
  File "/usr/local/lib/python3.10/enum.py", line 710, in __new__
    raise ve_exc
ValueError: 884 is not a valid DeviceType

Relevant configuration

No response

Anything else?

No response

pergolafabio commented 8 months ago

gonna close for now, too bad you couldnt use the addon , would be much better

seederp2p commented 8 months ago

nice :-) i hope the script doesnt hammer your device with that 1 sec, i hope it keeps stable

Yep. I'll monitor that :)

seederp2p commented 3 weeks ago

Hi!

For your information, there's a different approach possible! :)

You can go to Configuration -> Network Service -> HTTP -> HTTP Listening and configure an endpoint to receive the event payloads such as:

{
    "ipAddress": "whatever",
    "portNo": 80,
    "protocol": "HTTP",
    "dateTime": "2024-08-26T12:32:28+01:00",
    "activePostCount": 1,
    "eventType": "AccessControllerEvent",
    "eventState": "active",
    "eventDescription": "Access Controller Event",
    "AccessControllerEvent": {
        "deviceName": "DS-K1T502DBFWX-C",
        "majorEventType": 5,
        "subEventType": 21,
        "reportChannel": 5,
        "cardReaderKind": 1,
        "doorNo": 1,
        "serialNo": 257,
        "currentEvent": true,
        "frontSerialNo": 256,
        "hasRecord": false
    }
}

However I found out that this is not going to support calls to HTTPS service as it looks like the device won't support nothing higher than TLS 1.0. However you can create a "proxy service" inside your network with simple HTTP port 80 that can forward that to your HA Webhook. Example:

<?php
// Define the path to the file where the payload will be saved for debugging
$file = 'payload.txt';

// URL of the Home Assistant webhook (hidden for security)
$webhook_url = 'YOUR_WEBHOOK_URL_HERE';

// Check if the request method is POST
if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    // Capture the payload from the event_log field
    $event_log = isset($_POST['event_log']) ? $_POST['event_log'] : null;

    // If the event_log field exists, process it
    if ($event_log) {

        // Convert the event_log to an array if it's in JSON format
        if (is_string($event_log)) {
            $event_log = json_decode($event_log, true);
        }

        // Initialize a variable for the event name
        $event_name = "Irrelevant Event";

        // Check if the major and minor fields exist and process the event
        if (isset($event_log['AccessControllerEvent']['majorEventType']) && $event_log['AccessControllerEvent']['majorEventType'] === 5) {
            $minor = $event_log['AccessControllerEvent']['subEventType'];
            if ($minor === 22) {
                $event_name = "Door Closed";
            } elseif ($minor === 38) {
                $event_name = "Digital Authentication";
            }
        }

        // Rebuild the payload with the event name
        $new_payload = array("last_event" => $event_name);

        // Optional: Save the received payload and the (relevant) payload to be sent locally for debugging
        $debug_info = "Received Payload:\n" . json_encode($event_log, JSON_PRETTY_PRINT) . "\n\n";

        // Check if the event is relevant before sending it to Home Assistant
        if ($event_name !== "Irrelevant Event") {
            // Add the payload sent to the log
            $debug_info .= "Payload Sent:\n" . json_encode($new_payload, JSON_PRETTY_PRINT) . "\n\n";

            // Send the new payload to the Home Assistant webhook using cURL
            $ch = curl_init($webhook_url);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($new_payload));
            curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

            // Execute the request and capture the response
            $response = curl_exec($ch);
            $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

            curl_close($ch);

            // Check if the request was successful
            if ($http_code == 200) {
                echo "Payload successfully sent to Home Assistant.";
            } else {
                echo "Failed to send payload to Home Assistant. HTTP Code: $http_code";
            }
        } else {
            // If the event is not relevant, only log the information
            $debug_info .= "No payload sent (Irrelevant Event).\n\n";
            echo "Irrelevant event. No payload sent.";
        }

        // Write all debugging information to the file
        file_put_contents($file, $debug_info . str_repeat("-", 20) . "\n", FILE_APPEND);

    } else {
        echo "event_log field not found in the request.";
    }
} else {
    echo "Unsupported request method.";
}
?>

In this little script I'm just using the events for finger print and door closing.

then create something like this in your HA:

template:
  - trigger:
      - platform: webhook
        webhook_id: hikvision
        local_only: true
    sensor:
      - name: "your-door"
        state: "{{ trigger.json.last_event }}"
pergolafabio commented 3 weeks ago

ah, thats cool indeed,

=> Configuration -> Network Service -> HTTP -> HTTP Listening

is that something you can configure on the Hikvison device? seems i dont have that on my DS-KD8003 ... Where can you find it? can you make a screenshot?

This is maybe an idea to implement in the addon, to support thos devices

seederp2p commented 3 weeks ago

ah, thats cool indeed,

=> Configuration -> Network Service -> HTTP -> HTTP Listening

is that something you can configure on the Hikvison device? seems i dont have that on my DS-KD8003 ... Where can you find it? can you make a screenshot?

This is maybe an idea to implement in the addon, to support thos devices

Yep!

Take a look:

image

However, as I said, it looks like it doesn't support nothing higher than TLS 1.0, so we should send this to a "proxy" listener in HTTP that maybe can then forward to the HA webhook (or to a listener in your integration).

Also, If you want to open the door on this device you can do that by: curl -i --digest -u 'admin:whatever' -X PUT -d '<RemoteControlDoor><cmd>open</cmd></RemoteControlDoor>' http://you.r.i.p/ISAPI/AccessControl/RemoteControl/door/1

pergolafabio commented 3 weeks ago

yeah, that door open command is indeed also part of the addon i dont have that http listener setting :-( , not easy to develop if i cant test :-(

seederp2p commented 3 weeks ago

yeah, that door open command is indeed also part of the addon i dont have that http listener setting :-( , not easy to develop if i cant test :-(

Basically the Device start sending payloads whenever ANY event occurs.

Something like:

{
    "ipAddress": "whatever",
    "portNo": 80,
    "protocol": "HTTP",
    "dateTime": "2024-08-26T12:32:28+01:00",
    "activePostCount": 1,
    "eventType": "AccessControllerEvent",
    "eventState": "active",
    "eventDescription": "Access Controller Event",
    "AccessControllerEvent": {
        "deviceName": "DS-K1T502DBFWX-C",
        "majorEventType": 5,
        "subEventType": 21,
        "reportChannel": 5,
        "cardReaderKind": 1,
        "doorNo": 1,
        "serialNo": 257,
        "currentEvent": true,
        "frontSerialNo": 256,
        "hasRecord": false
    }
}

The code numbers are the same as in the ISAPI documentation...

So for instance, a Fingerprint read is major event 5 and minor (or subevent) 38.

Door locked is major 5 and minor (or subevent) 22

pergolafabio commented 3 weeks ago

should be easy, i have the issue open again , gonna make a feature reques from it ... maybe in future when i have some time :-)

seederp2p commented 3 weeks ago

should be easy, i have the issue open again , gonna make a feature reques from it ... maybe in future when i have some time :-)

sure!

Please let me know if you need any data from the device or a beta tester ;)

seederp2p commented 3 weeks ago

FYI, when you restart the hikvision device it won't start using the HTTP Listener automatically (!!!!).

In fact after you reboot, when you access your /ISAPI/Event/notification/httpHosts/1 you'll see:

<HttpHostNotification xmlns="http://www.isapi.org/ver20/XMLSchema" version="2.0">
<id>1</id>
<url>/HA/proxy.php</url>
<protocolType>HTTP</protocolType>
<parameterFormatType/>
<addressingFormatType>ipaddress</addressingFormatType>
<ipAddress>whatever</ipAddress>
<portNo>80</portNo>
<httpAuthenticationMethod/>
<SubscribeEvent>
<heartbeat>30</heartbeat>
<eventMode>all</eventMode>
</SubscribeEvent>
</HttpHostNotification>

Unless you save again your configuration OR you send the proper payload to the ISAPI it won't send any alerts!

After you save again you'll see this:

<HttpHostNotification xmlns="http://www.isapi.org/ver20/XMLSchema" version="2.0">
<id>1</id>
<url>/HA/proxy.php</url>
<protocolType>HTTP</protocolType>
<parameterFormatType/>
<addressingFormatType>ipaddress</addressingFormatType>
<ipAddress>whatever</ipAddress>
<portNo>80</portNo>
<httpAuthenticationMethod/>
<SubscribeEvent>
<heartbeat>30</heartbeat>
<eventMode>all</eventMode>
<EventList>
<Event>
<type>AccessControllerEvent</type>
<minorAlarm/>
<minorException/>
<minorOperation/>
<minorEvent/>
<pictureURLType>binary</pictureURLType>
</Event>
</EventList>
</SubscribeEvent>
</HttpHostNotification>

Notice the EventList that is now present!

You can also achieve the same result by running something like:

curl -i --digest -u 'admin:whatever' -X PUT -d '<HttpHostNotification xmlns="http://www.isapi.org/ver20/XMLSchema" version="2.0">
<id>1</id>
<url>/HA/proxy.php</url>
<protocolType>HTTP</protocolType>
<parameterFormatType/>
<addressingFormatType>ipaddress</addressingFormatType>
<ipAddress>whatever</ipAddress>
<portNo>80</portNo>
<httpAuthenticationMethod/>
<SubscribeEvent>
<heartbeat>30</heartbeat>
<eventMode>all</eventMode>
<EventList>
<Event>
<type>AccessControllerEvent</type>
<minorAlarm/>
<minorException/>
<minorOperation/>
<minorEvent/>
<pictureURLType>binary</pictureURLType>
</Event>
</EventList>
</SubscribeEvent>
</HttpHostNotification>' \
http://whatever/ISAPI/Event/notification/httpHosts/1

Please remind that it will spit all the missed events, so you should be careful. Maybe handlind event dates so you won't trigger any action based on a past event...

pergolafabio commented 3 weeks ago

hmm, maybe thats a firmware bug ?

seederp2p commented 3 weeks ago

hmm, maybe thats a firmware bug ?

Very likely... :) but we can't rely on hikvision to fix this ... ahaha

I don't even have a proper contact to escalate this... so we can overcome by creating a mechanism that PUT de needed XML payload so the listener is active again (for instance after a reboot).

Then you just need to handle the dates between the payloads that hikvision send vs the current time of the system.

My example:

<?php
// Define the path to the file where the payload will be saved for debugging
$file = 'payload.txt';

// Home Assistant webhook URL
$webhook_url = 'YOUR_HIDDEN_URL_HERE';

// Check if the request method is POST
if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    // Capture the payload from the event_log field
    $event_log = isset($_POST['event_log']) ? $_POST['event_log'] : null;

    // If the event_log field exists, process it
    if ($event_log) {

        // Convert event_log to an array if it's in JSON format
        if (is_string($event_log)) {
            $event_log = json_decode($event_log, true);
        }

        // Check if the dateTime field exists in the payload
        if (isset($event_log['dateTime'])) {

            // Convert the payload dateTime and the current system time to timestamps
            $payload_time = strtotime($event_log['dateTime']);
            $current_time = time();

            // Calculate the difference in seconds between the payload time and the current time
            $time_difference = abs($current_time - $payload_time);

            // If the difference is greater than 10 seconds, consider the event as irrelevant
            if ($time_difference > 10) {
                // Capture the current system date and time
                $system_time = date('Y-m-d H:i:s');

                // Message indicating that the event is outside the time tolerance, including the system date and time
                $message = "Payload is outside the time tolerance (system date: $system_time).";

                echo $message;

                // Log the debug information in the file
                $debug_info = "$message\nPayload Received:\n" . json_encode($event_log, JSON_PRETTY_PRINT) . "\n\n";
                file_put_contents($file, $debug_info . str_repeat("-", 20) . "\n", FILE_APPEND);
                exit;
            }
        } else {
            echo "dateTime field not found in the payload.";
            exit;
        }

        // Initialize the variable for the event name
        $event_name = "Irrelevant Event";

        // Check if the major and minor fields exist and process the event
        if ($event_log['AccessControllerEvent']['majorEventType'] === 5) {
            $minor = $event_log['AccessControllerEvent']['subEventType'];
            if ($minor === 22) {
                $event_name = "Door Closure";
            } elseif ($minor === 38) {
                $event_name = "Digital Authentication";
            }
        }

        // Reconstruct the payload with the event name
        $new_payload = array("last_event" => $event_name);

        // Optionally: Save the received payload and the (if relevant) payload to be sent locally for debugging
        $debug_info = "Payload Received:\n" . json_encode($event_log, JSON_PRETTY_PRINT) . "\n\n";

        // Check if the event is relevant before sending it to Home Assistant
        if ($event_name !== "Irrelevant Event") {
            // Add the sent payload to the log
            $debug_info .= "Payload Sent:\n" . json_encode($new_payload, JSON_PRETTY_PRINT) . "\n\n";

            // Send the new payload to the Home Assistant webhook using cURL
            $ch = curl_init($webhook_url);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($new_payload));
            curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

            // Execute the request and capture the response
            $response = curl_exec($ch);
            $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

            curl_close($ch);

            // Check if the request was successful
            if ($http_code == 200) {
                echo "Payload successfully sent to Home Assistant.";
            } else {
                echo "Failed to send payload to Home Assistant. HTTP Code: $http_code";
            }
        } else {
            // If the event is not relevant, just log the information
            $debug_info .= "No payload sent (Irrelevant Event).\n\n";
            echo "Irrelevant event. No payload sent.";
        }

        // Log all the debug information in the file
        file_put_contents($file, $debug_info . str_repeat("-", 20) . "\n", FILE_APPEND);

    } else {
        echo "event_log field not found in the request.";
    }
} else {
    echo "Request method not supported.";
}
?>