hikvision-sdk 0.0.2
Python wrapper for Hikvision HCNetSDK
License: MIT
Requires Python: >=3.9
Keywords: hikvision, camera, nvr, dvr, sdk, hcnetsdk, security

# Hikvision SDK

[![Latest Release](https://img.shields.io/badge/release-latest-blue)](https://gitlab.com/pierre.mariette/hikvision-sdk/-/releases/permalink/latest)

Python wrapper for the Hikvision HCNetSDK, providing easy access to Hikvision cameras and NVRs.

## Features

- **Device Connection** - Connect to Hikvision cameras and NVRs via TCP/UDP
- **Recording Search & Download** - Search and download recorded files by name or time range
- **Real-time Preview** - Live video streaming with callback support
- **JPEG Capture** - Capture snapshots from live channels
- **PTZ Control** - Pan, tilt, zoom with speed control, presets, cruise routes, and patterns
- **Alarm Handling** - Subscribe to device alarms and events (motion, VCA, etc.)
- **Device Configuration** - Query device information and settings
- **Playback Control** - Remote playback of recorded files with pause/resume

## Installation

### 1. Install the Python package

Download and install from [GitLab releases](https://gitlab.com/pierre.mariette/hikvision-sdk/-/releases/permalink/latest):

```bash
wget -q --content-disposition -P /tmp "https://gitlab.com/pierre.mariette/hikvision-sdk/-/releases/permalink/latest/downloads/hikvision_sdk-latest-py3-none-any.whl"
pip install /tmp/hikvision_sdk-*.whl
```

### 2. Install the Hikvision SDK libraries

The SDK libraries must be installed separately.

1. Download the SDK from [Hikvision](https://www.hikvision.com/en/support/download/sdk/)
2. Extract and copy the libraries:

```bash
# Recommended: user-local installation
mkdir -p ~/.local/lib/hikvision
cp -r /path/to/EN-HCNetSDKV6.x.x/lib/* ~/.local/lib/hikvision/

# Or system-wide (requires sudo)
sudo mkdir -p /usr/local/lib/hikvision
sudo cp -r /path/to/EN-HCNetSDKV6.x.x/lib/* /usr/local/lib/hikvision/
```

Alternatively, set the `HIKVISION_SDK_PATH` environment variable:

```bash
export HIKVISION_SDK_PATH=/path/to/sdk/lib
```

## Requirements

- Python 3.9+
- Linux x86_64 (the Hikvision SDK only supports this platform)
- Hikvision HCNetSDK libraries (installed separately)

## Quick Start

```python
from hikvision_sdk import HCNetSDK

with HCNetSDK() as sdk:
    # Login to device
    with sdk.login("192.168.1.100", 8000, "admin", "password") as device:
        print(f"Connected to: {device.serial_number}")

        # Search for recorded files
        from datetime import datetime, timedelta

        end_time = datetime.now()
        start_time = end_time - timedelta(days=1)

        for file in device.find_files(
            channel=device.start_channel,
            start_time=start_time,
            end_time=end_time
        ):
            print(f"{file.filename} - {file.start_time} ({file.file_size} bytes)")

        # Download a file
        device.download_file(file.filename, "/tmp/recording.mp4")

        # Capture JPEG
        device.capture_jpeg(device.start_channel, "/tmp/snapshot.jpg")
```

## API Reference

### HCNetSDK

Main SDK class for initialization and device login.

```python
from hikvision_sdk import HCNetSDK

# Initialize with auto-detected library path
sdk = HCNetSDK()

# Or specify custom library path
sdk = HCNetSDK(lib_path="/path/to/sdk/lib")

# Initialize SDK (required before login)
sdk.init(log_level=3, log_dir="/tmp/hcnetsdk_logs")

# Get SDK version
version = sdk.get_sdk_version()  # e.g., "6.1.9.4"

# Login to device
device = sdk.login(
    ip="192.168.1.100",
    port=8000,
    username="admin",
    password="password",
    async_login=False  # Set True for non-blocking login
)

# Cleanup when done
sdk.cleanup()

# Context manager (recommended)
with HCNetSDK() as sdk:
    with sdk.login("192.168.1.100", 8000, "admin", "password") as device:
        # ... use device ...
        pass
```

### Device

Represents a connected camera/NVR.

#### Properties

```python
device.user_id         # Device handle (int)
device.ip              # Device IP address
device.port            # Device port
device.serial_number   # Device serial number
device.channel_count   # Number of analog channels
device.start_channel   # First channel number (usually 1 or 33)
device.ip_channel_count # Number of IP channels (for NVRs)
```

#### File Search & Download

```python
from datetime import datetime, timedelta

# Search for recordings
end = datetime.now()
start = end - timedelta(hours=2)

for file in device.find_files(
    channel=device.start_channel,
    start_time=start,
    end_time=end,
    file_type=FILE_TYPE_ALL,      # Optional: filter by type
    stream_type=STREAM_TYPE_ALL,  # Optional: main/sub stream
    locked=LOCK_STATUS_ALL        # Optional: locked status
):
    print(f"{file.filename}: {file.start_time} - {file.end_time}")
    print(f"  Size: {file.file_size} bytes, Locked: {file.is_locked}")

# Download by filename
device.download_file(
    filename=file.filename,
    save_path="/tmp/recording.mp4",
    progress_callback=lambda p: print(f"Progress: {p}%")
)

# Download by time range
device.download_by_time(
    channel=device.start_channel,
    start_time=start,
    end_time=end,
    save_path="/tmp/recording.mp4",
    progress_callback=lambda p: print(f"Progress: {p}%")
)
```

#### Real-time Preview

```python
from hikvision_sdk import NET_DVR_SYSHEAD, NET_DVR_STREAMDATA

def on_preview_data(handle, data_type, data):
    if data_type == NET_DVR_SYSHEAD:
        print(f"Received system header: {len(data)} bytes")
    elif data_type == NET_DVR_STREAMDATA:
        print(f"Received video data: {len(data)} bytes")

# Start preview with callback
handle = device.start_preview(
    channel=device.start_channel,
    stream_type=STREAM_TYPE_MAIN,  # 0=main, 1=sub, 2=third
    link_mode=LINK_MODE_TCP,       # 0=TCP, 1=UDP
    callback=on_preview_data,
    blocked=True
)

# Change callback after starting
device.set_preview_callback(handle, new_callback)

# Stop preview
device.stop_preview(handle)
```

#### JPEG Capture

```python
# Capture snapshot
device.capture_jpeg(
    channel=device.start_channel,
    save_path="/tmp/snapshot.jpg",
    quality=0,  # 0=best, 1=better, 2=normal
    size=0      # 0=CIF, 1=QCIF, 2=D1
)
```

#### Playback Control

```python
# Start playback by filename
def on_playback_data(handle, data_type, data):
    print(f"Playback data: {len(data)} bytes")

handle = device.playback_by_name(
    filename=file.filename,
    callback=on_playback_data
)

# Control playback
device.playback_control(handle, 1)  # 1=pause
device.playback_control(handle, 2)  # 2=resume
device.playback_control(handle, 3)  # 3=fast forward
device.playback_control(handle, 4)  # 4=slow

# Get playback position (0-100%)
pos = device.get_playback_pos(handle)

# Capture frame during playback
device.playback_capture(handle, "/tmp/frame.bmp")

# Stop playback
device.stop_playback(handle)
```

#### PTZ Control

```python
from hikvision_sdk import (
    PTZ_UP, PTZ_DOWN, PTZ_LEFT, PTZ_RIGHT,
    PTZ_ZOOM_IN, PTZ_ZOOM_OUT,
    PTZ_FOCUS_NEAR, PTZ_FOCUS_FAR,
    PTZ_PRESET_SET, PTZ_PRESET_GOTO, PTZ_PRESET_CLEAR,
    PTZ_CRUISE_RUN, PTZ_CRUISE_STOP,
    PTZ_TRACK_START_RECORD, PTZ_TRACK_STOP_RECORD, PTZ_TRACK_RUN
)
import time

# Basic movement (start/stop)
device.ptz_control(device.start_channel, PTZ_LEFT)
time.sleep(1)
device.ptz_control(device.start_channel, PTZ_LEFT, stop=True)

# Movement with speed (1-7)
device.ptz_control_with_speed(device.start_channel, PTZ_UP, speed=5)
time.sleep(1)
device.ptz_control_with_speed(device.start_channel, PTZ_UP, speed=5, stop=True)

# Zoom control
device.ptz_control(device.start_channel, PTZ_ZOOM_IN)
time.sleep(0.5)
device.ptz_control(device.start_channel, PTZ_ZOOM_IN, stop=True)

# Preset management
device.ptz_preset(device.start_channel, PTZ_PRESET_SET, preset_index=1)   # Save
device.ptz_preset(device.start_channel, PTZ_PRESET_GOTO, preset_index=1)  # Go to
device.ptz_preset(device.start_channel, PTZ_PRESET_CLEAR, preset_index=1) # Clear

# Cruise routes
device.ptz_cruise(device.start_channel, PTZ_CRUISE_RUN, cruise_route=1)   # Start
device.ptz_cruise(device.start_channel, PTZ_CRUISE_STOP, cruise_route=1)  # Stop

# Pattern/track recording
device.ptz_track(device.start_channel, PTZ_TRACK_START_RECORD)  # Start recording
# ... move camera manually ...
device.ptz_track(device.start_channel, PTZ_TRACK_STOP_RECORD)   # Stop recording
device.ptz_track(device.start_channel, PTZ_TRACK_RUN)           # Replay pattern
```

#### Alarm Subscription

```python
from hikvision_sdk import COMM_ALARM_V30, COMM_ALARM_RULE

def on_alarm(command, alarmer_info, alarm_data):
    print(f"Alarm received: command={command}")
    print(f"  Device IP: {alarmer_info.get('ip')}")
    print(f"  Serial: {alarmer_info.get('serial')}")
    print(f"  Data length: {len(alarm_data)} bytes")

# Subscribe to alarms
alarm_handle = device.setup_alarm(
    callback=on_alarm,
    level=0  # 0=all, 1=high priority, 2=medium
)

# ... wait for alarms ...

# Unsubscribe
device.close_alarm(alarm_handle)
```

#### Device Configuration

```python
# Get detailed device configuration
config = device.get_device_config()

print(config)
# Output:
# Device: My Camera
#   Serial: DS-2CD2143G2-I20210101AACH123456789
#   Type: IP Camera (31)
#   Software: 5.7.1.0 (2023-06-15)
#   Hardware: 1.0.0
#   Channels: 1 (start: 1)
#   IP Channels: 0
#   Disks: 1
#   Alarm In/Out: 1/1

# Access individual fields
print(config.device_name)
print(config.serial_number)
print(config.software_version)
print(config.channel_count)
print(config.ip_channel_count)
print(config.disk_count)
```

### FileInfo

Represents a recorded file returned by `find_files()`.

```python
file.filename     # Remote filename (str)
file.start_time   # Recording start time (datetime or None)
file.end_time     # Recording end time (datetime or None)
file.file_size    # File size in bytes (int)
file.is_locked    # Whether file is locked (bool)
file.file_type    # File type code (int)
file.stream_type  # Stream type code (int)
```

### DeviceConfig

Device configuration returned by `get_device_config()`.

```python
config.device_name          # Device name (str)
config.device_id            # Device ID (int)
config.serial_number        # Serial number (str)
config.software_version     # Software version (str)
config.software_build_date  # Build date (str)
config.hardware_version     # Hardware version (str)
config.dsp_version          # DSP version (str)
config.dsp_build_date       # DSP build date (str)
config.panel_version        # Panel version (str)
config.device_type          # Device type code (int)
config.device_type_name     # Device type name (str)
config.channel_count        # Analog channel count (int)
config.start_channel        # Start channel number (int)
config.ip_channel_count     # IP channel count (int)
config.alarm_in_count       # Alarm input count (int)
config.alarm_out_count      # Alarm output count (int)
config.disk_count           # Disk count (int)
config.audio_count          # Audio channel count (int)
config.recycle_record       # Recycle recording enabled (bool)
```

### Constants

#### Stream Types

```python
from hikvision_sdk import (
    STREAM_TYPE_MAIN,   # 0 - Main stream (high quality)
    STREAM_TYPE_SUB,    # 1 - Sub stream (lower quality)
    STREAM_TYPE_THIRD,  # 2 - Third stream
    STREAM_TYPE_ALL,    # 0xFF - All streams
)
```

#### Link Modes

```python
from hikvision_sdk import (
    LINK_MODE_TCP,       # 0 - TCP
    LINK_MODE_UDP,       # 1 - UDP
    LINK_MODE_MULTICAST, # 2 - Multicast
    LINK_MODE_RTP,       # 3 - RTP
    LINK_MODE_RTP_RTSP,  # 4 - RTP over RTSP
    LINK_MODE_RTSP_HTTP, # 5 - RTSP over HTTP
)
```

#### File Types

```python
from hikvision_sdk import (
    FILE_TYPE_ALL,     # 0xFF - All files
    FILE_TYPE_TIMING,  # 0 - Scheduled recording
    FILE_TYPE_MOTION,  # 1 - Motion detection
    FILE_TYPE_ALARM,   # 2 - Alarm triggered
    FILE_TYPE_MANUAL,  # 6 - Manual recording
    FILE_TYPE_VCA,     # 7 - Video content analysis
)
```

#### Lock Status

```python
from hikvision_sdk import (
    LOCK_STATUS_ALL,      # 0xFF - All files
    LOCK_STATUS_UNLOCKED, # 0 - Unlocked files
    LOCK_STATUS_LOCKED,   # 1 - Locked files
)
```

#### Data Types (for callbacks)

```python
from hikvision_sdk import (
    NET_DVR_SYSHEAD,         # 1 - System header
    NET_DVR_STREAMDATA,      # 2 - Stream data (video/audio)
    NET_DVR_AUDIOSTREAMDATA, # 3 - Audio only
)
```

#### PTZ Commands

```python
from hikvision_sdk import (
    # Movement
    PTZ_UP, PTZ_DOWN, PTZ_LEFT, PTZ_RIGHT,
    PTZ_UP_LEFT, PTZ_UP_RIGHT, PTZ_DOWN_LEFT, PTZ_DOWN_RIGHT,

    # Zoom/Focus/Iris
    PTZ_ZOOM_IN, PTZ_ZOOM_OUT,
    PTZ_FOCUS_NEAR, PTZ_FOCUS_FAR,
    PTZ_IRIS_OPEN, PTZ_IRIS_CLOSE,

    # Auto
    PTZ_AUTO_PAN,

    # Presets
    PTZ_PRESET_SET,    # Save preset
    PTZ_PRESET_CLEAR,  # Clear preset
    PTZ_PRESET_GOTO,   # Go to preset

    # Cruise
    PTZ_CRUISE_RUN, PTZ_CRUISE_STOP,
    PTZ_CRUISE_FILL_PRESET, PTZ_CRUISE_SET_SPEED,
    PTZ_CRUISE_SET_DWELL, PTZ_CRUISE_CLEAR,

    # Track/Pattern
    PTZ_TRACK_START_RECORD, PTZ_TRACK_STOP_RECORD, PTZ_TRACK_RUN,
)
```

#### Alarm Types

```python
from hikvision_sdk import (
    COMM_ALARM,        # Basic alarm
    COMM_ALARM_V30,    # Alarm V30
    COMM_ALARM_V40,    # Alarm V40
    COMM_ALARM_RULE,   # Behavior analysis
    COMM_VCA_ALARM,    # VCA alarm
)
```

### Exceptions

```python
from hikvision_sdk import (
    HCNetSDKError,          # Base exception (includes error_code)
    SDKNotInitializedError, # SDK.init() not called
    SDKInitError,           # SDK initialization failed
    LoginError,             # Login failed
    LogoutError,            # Logout failed
    PreviewError,           # Preview start/stop failed
    FileSearchError,        # File search failed
    FileDownloadError,      # Download failed
    CaptureError,           # JPEG capture failed
    LibraryLoadError,       # SDK library not found
)

# All exceptions include error_code and human-readable message
try:
    device = sdk.login("192.168.1.100", 8000, "admin", "wrong")
except LoginError as e:
    print(f"Login failed: {e}")
    print(f"Error code: {e.error_code}")
```

### Utility Functions

```python
from hikvision_sdk import get_error_message

# Convert error code to message
msg = get_error_message(1)  # "Username or password error"
```

## Error Codes

Common error codes and their meanings:

| Code | Constant | Description |
|------|----------|-------------|
| 0 | `NET_DVR_NOERROR` | No error |
| 1 | `NET_DVR_PASSWORD_ERROR` | Username or password error |
| 2 | `NET_DVR_NOENOUGHPRI` | Not enough privilege |
| 3 | `NET_DVR_NOINIT` | SDK not initialized |
| 7 | `NET_DVR_NETWORK_FAIL_CONNECT` | Network connection failed |
| 10 | `NET_DVR_NETWORK_RECV_TIMEOUT` | Network receive timeout |
| 17 | `NET_DVR_PARAMETER_ERROR` | Parameter error |
| 23 | `NET_DVR_NOSUPPORT` | Not supported |
| 44 | `NET_DVR_DEV_OFFLINE` | Device offline |
| 47 | `NET_DVR_USER_LOCKED` | User locked |

## License

MIT License - see [LICENSE](LICENSE) file for details.