shaykhnazar/hikvision-isapi 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

shaykhnazar/hikvision-isapi

最新稳定版本:v1.5.4

Composer 安装命令:

composer require shaykhnazar/hikvision-isapi

包简介

Laravel package for Hikvision ISAPI Face Recognition Terminals integration

README 文档

README

Latest Version License: MIT PHP Version Laravel Version

A clean, modern Laravel package for integrating with Hikvision ISAPI Face Recognition Terminals and access control devices. Supports multi-device management, universal device providers (config, database, API), multi-tenant architectures, and real-time event webhooks.

Features

  • Real-Time Event Webhooks: Configure devices to push events to your API endpoints (v1.4.0+)
  • Multi-Device Support: Manage unlimited Hikvision terminals simultaneously (v1.2.0+)
  • Universal Device Providers: Load devices from config, database, API, or custom sources (v1.3.0+)
  • Multi-Tenant Ready: Tenant-scoped device loading with runtime registration (v1.3.0+)
  • Device Management: Get device info, status, and capabilities
  • Person Management: Add, update, search, and delete persons
  • Card Management: Handle access cards with full CRUD operations
  • Face Recognition: Upload and manage face images with advanced search (v1.1.0+)
  • Fingerprint Support: Register and manage fingerprints
  • Access Control: Control doors remotely
  • Event Handling: Search and subscribe to access events
  • Event Notifications: HTTP webhooks for real-time event delivery (v1.4.0+)
  • Clean Architecture: SOLID principles, dependency injection, contracts
  • Type Safety: PHP 8.2+ features (enums, readonly properties)
  • Performance: Built-in caching with configurable TTL (v1.3.0+)
  • XML/JSON Support: Automatic format detection and conversion (v1.4.0+)
  • Laravel 12: Full Laravel 12.x support with service provider and facade

Table of Contents

Requirements

  • PHP ^8.2
  • Laravel ^12.0
  • Guzzle HTTP ^7.8
  • ext-curl
  • ext-json

Installation

Install the package via Composer:

composer require shaykhnazar/hikvision-isapi

Publish the configuration file:

php artisan vendor:publish --tag=hikvision-config

Configuration

Add these environment variables to your .env file:

HIKVISION_IP=192.168.1.100
HIKVISION_PORT=80
HIKVISION_USERNAME=admin
HIKVISION_PASSWORD=your_password
HIKVISION_PROTOCOL=http
HIKVISION_TIMEOUT=30
HIKVISION_VERIFY_SSL=false
HIKVISION_FORMAT=json

Configuration File

The published config/hikvision.php supports multiple devices:

return [
    'default' => env('HIKVISION_DEFAULT_DEVICE', 'primary'),

    'devices' => [
        'primary' => [
            'ip' => env('HIKVISION_IP', '192.168.1.100'),
            'port' => env('HIKVISION_PORT', 80),
            'username' => env('HIKVISION_USERNAME', 'admin'),
            'password' => env('HIKVISION_PASSWORD'),
            'protocol' => env('HIKVISION_PROTOCOL', 'http'),
            'timeout' => env('HIKVISION_TIMEOUT', 30),
            'verify_ssl' => env('HIKVISION_VERIFY_SSL', false),
        ],
    ],

    'format' => env('HIKVISION_FORMAT', 'json'),

    'logging' => [
        'enabled' => env('HIKVISION_LOGGING', true),
        'channel' => env('HIKVISION_LOG_CHANNEL', 'stack'),
    ],
];

Quick Start

Check Device Status

use Shaykhnazar\HikvisionIsapi\Services\DeviceService;

$deviceService = app(DeviceService::class);

if ($deviceService->isOnline()) {
    $info = $deviceService->getInfo();
    echo "Device Model: " . $info['DeviceInfo']['model'];
}

Add a Person

use Shaykhnazar\HikvisionIsapi\Services\PersonService;
use Shaykhnazar\HikvisionIsapi\DTOs\Person;
use Shaykhnazar\HikvisionIsapi\Enums\UserType;

$personService = app(PersonService::class);

$person = new Person(
    employeeNo: 'EMP001',
    name: 'John Doe',
    userType: UserType::NORMAL,
    validEnabled: true,
    beginTime: now()->toISOString(),
    endTime: now()->addYear()->toISOString(),
    doorRight: '1',
    rightPlan: [
        ['doorNo' => 1, 'planTemplateNo' => '1']
    ]
);

$response = $personService->add($person);

Upload Face Image

use Shaykhnazar\HikvisionIsapi\Services\FaceService;

$faceService = app(FaceService::class);

// Read image file
$imageData = file_get_contents('path/to/photo.jpg');
$imageBase64 = base64_encode($imageData);

// Upload face (person must exist first)
$response = $faceService->uploadFace('EMP001', $imageBase64, 1);

Search and Manage Face Data

use Shaykhnazar\HikvisionIsapi\Services\FaceService;

$faceService = app(FaceService::class);

// Search face data with pagination
$results = $faceService->searchFace(
    page: 0,
    maxResults: 30,
    faceLibType: 'blackFD',
    fdid: 1,
    fpid: '31903791410044'
);

// Upload face data record with image file
$imageContent = file_get_contents('face.jpg');
$response = $faceService->uploadFaceDataRecord(
    fdid: 1,
    fpid: '31903791410044',
    imageContent: $imageContent,
    faceLibType: 'blackFD'
);

// Delete face search data
$faceService->deleteFaceSearch(fdid: 1, faceLibType: 'blackFD');

Search Persons

$personService = app(PersonService::class);

// Get total count
$total = $personService->count();

// Search with pagination (30 persons per page)
$persons = $personService->search(page: 0, maxResults: 30);

foreach ($persons as $person) {
    echo "{$person->employeeNo}: {$person->name}\n";
}

Add Card

use Shaykhnazar\HikvisionIsapi\Services\CardService;
use Shaykhnazar\HikvisionIsapi\DTOs\Card;

$cardService = app(CardService::class);

$card = new Card(
    employeeNo: 'EMP001',
    cardNo: '1234567890',
    cardType: 'normal'
);

$response = $cardService->add($card);

Control Doors

use Shaykhnazar\HikvisionIsapi\Services\AccessControlService;

$accessControl = app(AccessControlService::class);

// Open door
$accessControl->openDoor(1);

// Get door status
$status = $accessControl->getDoorStatus(1);

// Close door
$accessControl->closeDoor(1);

Search Events

use Shaykhnazar\HikvisionIsapi\Services\EventService;

$eventService = app(EventService::class);

$events = $eventService->search([
    'major' => 5,  // Access control
    'minor' => 75, // Face recognition
    'startTime' => now()->subDay()->toISOString(),
    'endTime' => now()->toISOString(),
], page: 0, maxResults: 50);

Event Webhooks & Notifications (v1.4.0+)

Configure Hikvision devices to automatically push events (access granted, face recognized, etc.) to your application's webhook endpoint in real-time. This eliminates the need for polling and provides instant event notifications.

Configure Webhook Endpoint

The EventNotificationService allows you to configure HTTP event notifications on Hikvision devices:

use Shaykhnazar\HikvisionIsapi\Services\EventNotificationService;

$eventNotificationService = app(EventNotificationService::class);

Simple Webhook Setup

The easiest way to set up a webhook is using the configureWebhook() method:

// Configure device to send all events to your webhook endpoint
$webhookUrl = 'https://your-api.com/api/webhooks/hikvision/events';

$response = $eventNotificationService->configureWebhook(
    webhookUrl: $webhookUrl,
    hostId: 1 // Host ID (1-8), default is 1
);

// Enable the webhook
$eventNotificationService->enableHttpHost(1);

Advanced Webhook Configuration

For more control over webhook configuration, use configureHttpHost():

$response = $eventNotificationService->configureHttpHost(
    url: 'https://your-api.com/api/webhooks/hikvision/events',
    id: 1,                      // Host ID (1-8)
    protocol: 'HTTPS',          // HTTP or HTTPS
    port: 443,                  // Port number
    httpAuthType: 'basic',      // none, basic, or digest
    username: 'webhook_user',   // Optional: for authentication
    password: 'webhook_pass',   // Optional: for authentication
    eventTypes: [               // Optional: specific event types (empty = all events)
        'AccessControllerEvent',
        'VideoMotion',
        'linedetection'
    ]
);

Managing Webhook Hosts

// Get webhook configuration
$config = $eventNotificationService->getHttpHost(1);

// Get all webhook configurations (supports up to 8 hosts)
$allConfigs = $eventNotificationService->getAllHttpHosts();

// Enable webhook
$eventNotificationService->enableHttpHost(1);

// Disable webhook
$eventNotificationService->disableHttpHost(1);

// Remove webhook configuration
$eventNotificationService->removeHttpHost(1);

// Get notification capabilities
$capabilities = $eventNotificationService->getCapabilities();

Testing Webhooks

Test if your webhook endpoint is reachable from the device:

// Send a test event to configured webhook
$response = $eventNotificationService->testHttpNotification(1);

Receiving Webhook Events

Create a controller to receive webhook events from Hikvision devices:

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

class HikvisionWebhookController extends Controller
{
    /**
     * Receive events from Hikvision devices
     */
    public function handleEvent(Request $request)
    {
        // Hikvision sends events as XML
        $xmlData = $request->getContent();

        // Parse XML to array
        $xml = simplexml_load_string($xmlData);
        $event = json_decode(json_encode($xml), true);

        // Log the event
        Log::info('Hikvision Event Received', $event);

        // Process event based on type
        $eventType = $event['eventType'] ?? null;

        match($eventType) {
            'AccessControllerEvent' => $this->handleAccessControl($event),
            'faceDetection' => $this->handleFaceDetection($event),
            'VideoMotion' => $this->handleMotionDetection($event),
            default => Log::warning('Unknown event type', ['type' => $eventType])
        };

        // Return 200 OK to acknowledge receipt
        return response('OK', 200);
    }

    protected function handleAccessControl(array $event)
    {
        // Example: Handle access control event
        $employeeNo = $event['employeeNoString'] ?? null;
        $cardNo = $event['cardNo'] ?? null;
        $doorNo = $event['doorNo'] ?? null;

        Log::info('Access Control Event', [
            'employee' => $employeeNo,
            'card' => $cardNo,
            'door' => $doorNo,
            'time' => $event['dateTime'] ?? now()
        ]);

        // Your business logic here
        // - Record attendance
        // - Send notifications
        // - Update access logs
    }

    protected function handleFaceDetection(array $event)
    {
        // Handle face detection event
        Log::info('Face Detection Event', $event);
    }

    protected function handleMotionDetection(array $event)
    {
        // Handle motion detection event
        Log::info('Motion Detection Event', $event);
    }
}

Route setup:

// routes/api.php
Route::post('webhooks/hikvision/events', [HikvisionWebhookController::class, 'handleEvent'])
    ->name('hikvision.webhook');

Multi-Device Webhook Setup

Configure webhooks for multiple devices:

use Shaykhnazar\HikvisionIsapi\Facades\Hikvision;
use Shaykhnazar\HikvisionIsapi\Services\EventNotificationService;

$devices = ['entrance', 'exit', 'canteen'];
$webhookUrl = 'https://your-api.com/api/webhooks/hikvision/events';

foreach ($devices as $deviceName) {
    $client = Hikvision::device($deviceName);
    $eventService = new EventNotificationService($client);

    // Configure webhook with device name in URL for identification
    $deviceWebhookUrl = $webhookUrl . '?device=' . $deviceName;

    $eventService->configureWebhook($deviceWebhookUrl);
    $eventService->enableHttpHost(1);

    Log::info("Webhook configured for device: {$deviceName}");
}

Webhook Security

Protect your webhook endpoint:

// Add authentication to webhook configuration
$eventNotificationService->configureHttpHost(
    url: 'https://your-api.com/api/webhooks/hikvision/events',
    id: 1,
    protocol: 'HTTPS',
    port: 443,
    httpAuthType: 'basic',
    username: env('HIKVISION_WEBHOOK_USER'),
    password: env('HIKVISION_WEBHOOK_PASSWORD')
);

// In your controller, validate the credentials
public function handleEvent(Request $request)
{
    // Laravel automatically handles basic auth with middleware
    // Or manually validate:
    if ($request->getUser() !== env('HIKVISION_WEBHOOK_USER') ||
        $request->getPassword() !== env('HIKVISION_WEBHOOK_PASSWORD')) {
        return response('Unauthorized', 401);
    }

    // Process event...
}

Webhook Event Examples

Access Control Event (Face Recognition):

<EventNotificationAlert>
    <eventType>AccessControllerEvent</eventType>
    <eventState>active</eventState>
    <eventDescription>Access Control Event</eventDescription>
    <dateTime>2025-10-30T14:30:00Z</dateTime>
    <ActivePost>
        <eventType>AccessControllerEvent</eventType>
        <employeeNoString>EMP001</employeeNoString>
        <name>John Doe</name>
        <cardNo>1234567890</cardNo>
        <doorNo>1</doorNo>
        <swipeResult>success</swipeResult>
    </ActivePost>
</EventNotificationAlert>

Motion Detection Event:

<EventNotificationAlert>
    <eventType>VideoMotion</eventType>
    <eventState>active</eventState>
    <dateTime>2025-10-30T14:35:00Z</dateTime>
</EventNotificationAlert>

Using the Facade

use Shaykhnazar\HikvisionIsapi\Facades\HikvisionIsapi;

// Get device info
$info = HikvisionIsapi::get('/ISAPI/System/deviceInfo');

// Custom endpoint
$response = HikvisionIsapi::post('/ISAPI/CustomEndpoint', [
    'key' => 'value'
]);

Services Overview

DeviceService

$deviceService = app(DeviceService::class);

$deviceService->getInfo();           // Get device information
$deviceService->getCapabilities();   // Get device capabilities
$deviceService->getStatus();         // Get device status
$deviceService->isOnline();          // Check if device is online

PersonService

$personService = app(PersonService::class);

$personService->add(Person $person);              // Add person
$personService->update(Person $person);           // Update person
$personService->apply(Person $person);            // Apply person changes
$personService->search(int $page, int $maxResults); // Search persons
$personService->delete(array $employeeNos);       // Delete persons
$personService->count();                          // Count persons
$personService->getCapabilities();                // Get capabilities

CardService

$cardService = app(CardService::class);

$cardService->add(Card $card);                    // Add card
$cardService->update(Card $card);                 // Update card
$cardService->search(...);                        // Search cards
$cardService->delete(array $employeeNos);         // Delete cards
$cardService->deleteAll();                        // Delete all cards
$cardService->count(?string $employeeNo);         // Count cards
$cardService->batchAdd(array $cards);             // Batch add cards

FaceService

$faceService = app(FaceService::class);

$faceService->uploadFace(string $employeeNo, string $imageBase64, int $fdid);
$faceService->deleteFace(int $fdid, int $fpid);
$faceService->searchFace(int $page, int $maxResults, string $faceLibType, ?int $fdid, ?string $fpid);
$faceService->deleteFaceSearch(int $fdid, string $faceLibType);
$faceService->uploadFaceDataRecord(int $fdid, string $fpid, string $imageContent, string $faceLibType);
$faceService->getLibraries();
$faceService->createLibrary(array $data);
$faceService->getCapabilities();

FingerprintService

$fingerprintService = app(FingerprintService::class);

$fingerprintService->add(string $employeeNo, int $fingerprintId, string $data);
$fingerprintService->search(...);
$fingerprintService->capture(int $timeout);
$fingerprintService->delete(array $employeeNos);
$fingerprintService->getCapabilities();

AccessControlService

$accessControl = app(AccessControlService::class);

$accessControl->openDoor(int $doorNo);
$accessControl->closeDoor(int $doorNo);
$accessControl->getDoorStatus(int $doorNo);

EventService

$eventService = app(EventService::class);

$eventService->search(array $conditions, int $page, int $maxResults);
$eventService->count(array $conditions);
$eventService->subscribe(array $eventTypes, int $heartbeat);

EventNotificationService

$eventNotificationService = app(EventNotificationService::class);

// Simple webhook setup
$eventNotificationService->configureWebhook(string $webhookUrl, int $hostId);

// Advanced configuration
$eventNotificationService->configureHttpHost(string $url, int $id, string $protocol, int $port, string $httpAuthType, ?string $username, ?string $password, array $eventTypes);

// Manage webhooks
$eventNotificationService->getHttpHost(int $id);
$eventNotificationService->getAllHttpHosts();
$eventNotificationService->enableHttpHost(int $id);
$eventNotificationService->disableHttpHost(int $id);
$eventNotificationService->removeHttpHost(int $id);

// Testing
$eventNotificationService->testHttpNotification(int $id);

// Capabilities
$eventNotificationService->getCapabilities();

DTOs (Data Transfer Objects)

Person DTO

use Shaykhnazar\HikvisionIsapi\DTOs\Person;
use Shaykhnazar\HikvisionIsapi\Enums\UserType;

$person = new Person(
    employeeNo: 'EMP001',
    name: 'John Doe',
    userType: UserType::NORMAL,
    validEnabled: true,
    beginTime: '2025-01-01T00:00:00',
    endTime: '2025-12-31T23:59:59',
    doorRight: '1',
    rightPlan: [
        ['doorNo' => 1, 'planTemplateNo' => '1']
    ],
    email: 'john@example.com',
    phoneNumber: '+1234567890',
    organizationId: 1,
    belongGroup: null
);

// Convert to array for API
$array = $person->toArray();

// Create from API response
$person = Person::fromArray($response);

Card DTO

use Shaykhnazar\HikvisionIsapi\DTOs\Card;

$card = new Card(
    employeeNo: 'EMP001',
    cardNo: '1234567890',
    cardType: 'normal',
    enabled: true
);

Face DTO

use Shaykhnazar\HikvisionIsapi\DTOs\Face;

$face = new Face(
    employeeNo: 'EMP001',
    faceData: base64_encode($imageData),
    faceLibId: 1,
    faceLibType: 'blackFD'
);

Enums

UserType

use Shaykhnazar\HikvisionIsapi\Enums\UserType;

UserType::NORMAL    // Normal user
UserType::VISITOR   // Visitor
UserType::BLOCKLIST // Blocklist user

// Get label
$label = UserType::NORMAL->label(); // "Normal User"

EventType

use Shaykhnazar\HikvisionIsapi\Enums\EventType;

EventType::ACCESS_GRANTED  // 0x01
EventType::ACCESS_DENIED   // 0x02
EventType::FACE_RECOGNIZED // 0x4b
EventType::CARD_SWIPED     // 0x05

// Get description
$desc = EventType::ACCESS_GRANTED->description(); // "Access Granted"

Error Handling

All exceptions extend HikvisionException:

use Shaykhnazar\HikvisionIsapi\Exceptions\HikvisionException;
use Shaykhnazar\HikvisionIsapi\Exceptions\AuthenticationException;
use Shaykhnazar\HikvisionIsapi\Exceptions\DeviceNotFoundException;

try {
    $response = $personService->add($person);
} catch (AuthenticationException $e) {
    // Handle authentication errors
    Log::error('Authentication failed', ['error' => $e->getMessage()]);
} catch (DeviceNotFoundException $e) {
    // Handle device not found
    Log::error('Device not found', ['error' => $e->getMessage()]);
} catch (HikvisionException $e) {
    // Handle other Hikvision errors
    Log::error('Hikvision error', ['error' => $e->getMessage()]);
}

Multi-Device Support

The package supports managing multiple Hikvision devices simultaneously. This is useful when you have multiple terminals at different locations (entrance, exit, canteen, etc.).

Device Sources Supported:

  • ✅ Config files (default)
  • ✅ Database tables
  • ✅ Custom callbacks (API, Redis, cache, etc.)
  • ✅ Runtime registration

Configuration from Config Files (Default)

Configure multiple devices in config/hikvision.php:

return [
    'default' => env('HIKVISION_DEFAULT_DEVICE', 'primary'),

    'devices' => [
        'primary' => [
            'ip' => env('HIKVISION_IP', '192.168.1.100'),
            'port' => env('HIKVISION_PORT', 80),
            'username' => env('HIKVISION_USERNAME', 'admin'),
            'password' => env('HIKVISION_PASSWORD'),
            'protocol' => env('HIKVISION_PROTOCOL', 'http'),
            'timeout' => env('HIKVISION_TIMEOUT', 30),
            'verify_ssl' => env('HIKVISION_VERIFY_SSL', false),
        ],

        'entrance' => [
            'ip' => env('HIKVISION_ENTRANCE_IP', '192.168.1.101'),
            'port' => env('HIKVISION_ENTRANCE_PORT', 80),
            'username' => env('HIKVISION_ENTRANCE_USERNAME', 'admin'),
            'password' => env('HIKVISION_ENTRANCE_PASSWORD'),
            'protocol' => env('HIKVISION_ENTRANCE_PROTOCOL', 'http'),
            'timeout' => env('HIKVISION_ENTRANCE_TIMEOUT', 30),
            'verify_ssl' => env('HIKVISION_ENTRANCE_VERIFY_SSL', false),
        ],

        'exit' => [
            'ip' => env('HIKVISION_EXIT_IP', '192.168.1.102'),
            'port' => env('HIKVISION_EXIT_PORT', 80),
            'username' => env('HIKVISION_EXIT_USERNAME', 'admin'),
            'password' => env('HIKVISION_EXIT_PASSWORD'),
            'protocol' => env('HIKVISION_EXIT_PROTOCOL', 'http'),
            'timeout' => env('HIKVISION_EXIT_TIMEOUT', 30),
            'verify_ssl' => env('HIKVISION_EXIT_VERIFY_SSL', false),
        ],

        'canteen' => [
            'ip' => env('HIKVISION_CANTEEN_IP', '192.168.1.103'),
            'port' => env('HIKVISION_CANTEEN_PORT', 80),
            'username' => env('HIKVISION_CANTEEN_USERNAME', 'admin'),
            'password' => env('HIKVISION_CANTEEN_PASSWORD'),
            'protocol' => env('HIKVISION_CANTEEN_PROTOCOL', 'http'),
            'timeout' => env('HIKVISION_CANTEEN_TIMEOUT', 30),
            'verify_ssl' => env('HIKVISION_CANTEEN_VERIFY_SSL', false),
        ],
    ],
];

Environment Variables

Add device-specific environment variables to .env:

# Primary Device (Default)
HIKVISION_DEFAULT_DEVICE=primary
HIKVISION_IP=192.168.1.100
HIKVISION_PORT=80
HIKVISION_USERNAME=admin
HIKVISION_PASSWORD=your_password

# Entrance Device
HIKVISION_ENTRANCE_IP=192.168.1.101
HIKVISION_ENTRANCE_PORT=80
HIKVISION_ENTRANCE_USERNAME=admin
HIKVISION_ENTRANCE_PASSWORD=entrance_password

# Exit Device
HIKVISION_EXIT_IP=192.168.1.102
HIKVISION_EXIT_PORT=80
HIKVISION_EXIT_USERNAME=admin
HIKVISION_EXIT_PASSWORD=exit_password

# Canteen Device
HIKVISION_CANTEEN_IP=192.168.1.103
HIKVISION_CANTEEN_PORT=80
HIKVISION_CANTEEN_USERNAME=admin
HIKVISION_CANTEEN_PASSWORD=canteen_password

Using Multiple Devices with Facade

use Shaykhnazar\HikvisionIsapi\Facades\Hikvision;

// Use default device
$defaultClient = Hikvision::default();
$info = $defaultClient->get('/ISAPI/System/deviceInfo');

// Use specific devices
$entranceClient = Hikvision::device('entrance');
$exitClient = Hikvision::device('exit');
$canteenClient = Hikvision::device('canteen');

// Get device information from each terminal
$entranceInfo = $entranceClient->get('/ISAPI/System/deviceInfo');
$exitInfo = $exitClient->get('/ISAPI/System/deviceInfo');
$canteenInfo = $canteenClient->get('/ISAPI/System/deviceInfo');

// List all available devices
$devices = Hikvision::availableDevices();
// Returns: ['primary', 'entrance', 'exit', 'canteen']

// Check if device exists
if (Hikvision::hasDevice('entrance')) {
    // Work with entrance device
}

Using Multiple Devices with Services

use Shaykhnazar\HikvisionIsapi\Facades\Hikvision;
use Shaykhnazar\HikvisionIsapi\Services\PersonService;
use Shaykhnazar\HikvisionIsapi\DTOs\Person;
use Shaykhnazar\HikvisionIsapi\Enums\UserType;

// Create person DTO
$person = new Person(
    employeeNo: 'EMP001',
    name: 'John Doe',
    userType: UserType::NORMAL,
    validEnabled: true,
    beginTime: now()->toISOString(),
    endTime: now()->addYear()->toISOString()
);

// Get device-specific clients
$entranceClient = Hikvision::device('entrance');
$exitClient = Hikvision::device('exit');
$canteenClient = Hikvision::device('canteen');

// Create person service for each device
$entrancePersonService = new PersonService($entranceClient);
$exitPersonService = new PersonService($exitClient);
$canteenPersonService = new PersonService($canteenClient);

// Add person to all devices
$entrancePersonService->add($person);
$exitPersonService->add($person);
$canteenPersonService->add($person);

Syncing Employee to Multiple Devices

use Shaykhnazar\HikvisionIsapi\Facades\Hikvision;
use Shaykhnazar\HikvisionIsapi\Services\PersonService;
use Shaykhnazar\HikvisionIsapi\Services\FaceService;
use Shaykhnazar\HikvisionIsapi\DTOs\Person;

class EmployeeSyncService
{
    public function syncToAllDevices(Person $person, string $faceImagePath): array
    {
        $results = [];
        $devices = Hikvision::availableDevices();

        foreach ($devices as $deviceName) {
            try {
                // Get device client
                $client = Hikvision::device($deviceName);

                // Create services for this device
                $personService = new PersonService($client);
                $faceService = new FaceService($client);

                // Add person
                $personService->add($person);

                // Upload face
                $imageData = file_get_contents($faceImagePath);
                $imageBase64 = base64_encode($imageData);
                $faceService->uploadFace($person->employeeNo, $imageBase64, 1);

                $results[$deviceName] = [
                    'success' => true,
                    'message' => 'Synced successfully',
                ];
            } catch (\Exception $e) {
                $results[$deviceName] = [
                    'success' => false,
                    'error' => $e->getMessage(),
                ];
            }
        }

        return $results;
    }
}

Device Manager Methods

The DeviceManager provides the following methods:

use Shaykhnazar\HikvisionIsapi\Facades\Hikvision;

// Get client for specific device (or default if null)
$client = Hikvision::device('entrance');
$client = Hikvision::device(); // Uses default device

// Get default device client
$defaultClient = Hikvision::default();

// Get all available device names
$devices = Hikvision::availableDevices();
// Returns: ['primary', 'entrance', 'exit', 'canteen']

// Check if device exists in configuration
$exists = Hikvision::hasDevice('entrance'); // true or false

// Reload devices from provider (useful for database-driven configs)
Hikvision::reload(); // Clears cache and reloads from source

// Clear cached clients (useful for testing)
Hikvision::clearClients();

// Register device at runtime (v1.3.0+)
Hikvision::registerDevice('temp_device', [
    'ip' => '192.168.1.150',
    'port' => 80,
    'username' => 'admin',
    'password' => 'password',
    'protocol' => 'http',
    'timeout' => 30,
    'verify_ssl' => false,
]);

// Switch device provider at runtime (v1.3.0+)
use Shaykhnazar\HikvisionIsapi\Client\Providers\DatabaseDeviceProvider;
$dbProvider = new DatabaseDeviceProvider(table: 'terminals');
Hikvision::setProvider($dbProvider);

Backward Compatibility

The package maintains 100% backward compatibility. If you're using the default device setup, your existing code will work without any changes:

use Shaykhnazar\HikvisionIsapi\Services\PersonService;

// This still works - uses default device
$personService = app(PersonService::class);
$person = $personService->add($newPerson);

Loading Devices from Database

For applications that store terminal configurations in database (multi-tenant, dynamic terminals, etc.), use the DatabaseDeviceProvider:

Step 1: Create Terminals Table

// Migration: create_terminals_table.php
Schema::create('terminals', function (Blueprint $table) {
    $table->id();
    $table->string('name')->unique(); // Device identifier
    $table->string('ip');
    $table->integer('port')->default(80);
    $table->string('username');
    $table->string('password');
    $table->string('protocol')->default('http');
    $table->integer('timeout')->default(30);
    $table->boolean('verify_ssl')->default(false);
    $table->boolean('is_active')->default(true);
    $table->timestamps();
});

Step 2: Register Database Provider

In your AppServiceProvider or custom service provider:

use Shaykhnazar\HikvisionIsapi\Client\Providers\DatabaseDeviceProvider;

public function register(): void
{
    // Bind custom device provider
    $this->app->singleton('hikvision.device.provider', function ($app) {
        return new DatabaseDeviceProvider(
            table: 'terminals',
            nameColumn: 'name',
            configColumns: [
                'ip' => 'ip',
                'port' => 'port',
                'username' => 'username',
                'password' => 'password',
                'protocol' => 'protocol',
                'timeout' => 'timeout',
                'verify_ssl' => 'verify_ssl',
            ],
            defaultDevice: 'primary',
            whereConditions: ['is_active' => true], // Only load active terminals
            cache: true, // Enable caching
            cacheTtl: 3600 // Cache for 1 hour
        );
    });
}

Step 3: Use Terminals from Database

use Shaykhnazar\HikvisionIsapi\Facades\Hikvision;
use Shaykhnazar\HikvisionIsapi\Services\PersonService;

// Get all available terminals from database
$terminals = Hikvision::availableDevices();
// Returns: ['entrance', 'exit', 'canteen', 'office'] - loaded from DB

// Use specific terminal
$entranceClient = Hikvision::device('entrance');
$personService = new PersonService($entranceClient);

// Add person to entrance terminal
$personService->add($person);

Step 4: Reload Devices When Database Changes

use Shaykhnazar\HikvisionIsapi\Facades\Hikvision;

// After adding/updating terminals in database
Hikvision::reload(); // Clears cache and reloads from DB

Using Eloquent Models with CallbackProvider

For more complex scenarios with Eloquent relationships:

use Shaykhnazar\HikvisionIsapi\Client\Providers\CallbackDeviceProvider;
use App\Models\Terminal;

// In your service provider
$this->app->singleton('hikvision.device.provider', function ($app) {
    return CallbackDeviceProvider::fromEloquent(
        query: Terminal::where('status', 'active')
                      ->where('company_id', auth()->user()->company_id),
        nameColumn: 'slug',
        configMap: [
            'ip' => 'ip_address',
            'port' => 'port',
            'username' => 'username',
            'password' => 'password',
            'protocol' => 'protocol',
            'timeout' => 'connection_timeout',
            'verify_ssl' => 'ssl_enabled',
        ]
    );
});

Multi-Tenant Support Example

For multi-tenant applications where each tenant has their own terminals:

use Shaykhnazar\HikvisionIsapi\Client\Providers\CallbackDeviceProvider;
use App\Models\Terminal;

// In AppServiceProvider
$this->app->singleton('hikvision.device.provider', function ($app) {
    return new CallbackDeviceProvider(
        deviceNamesCallback: function () {
            // Only load terminals for current tenant
            $tenantId = auth()->user()->tenant_id;
            return Terminal::where('tenant_id', $tenantId)
                          ->where('is_active', true)
                          ->pluck('name')
                          ->toArray();
        },
        deviceConfigCallback: function (string $deviceName) {
            $tenantId = auth()->user()->tenant_id;
            $terminal = Terminal::where('tenant_id', $tenantId)
                               ->where('name', $deviceName)
                               ->first();

            if (!$terminal) {
                return null;
            }

            return [
                'ip' => $terminal->ip,
                'port' => $terminal->port,
                'username' => $terminal->username,
                'password' => decrypt($terminal->password), // Decrypt if encrypted
                'protocol' => $terminal->protocol,
                'timeout' => $terminal->timeout,
                'verify_ssl' => $terminal->verify_ssl,
            ];
        },
        defaultDevice: 'primary'
    );
});

Runtime Device Registration

Register devices dynamically at runtime:

use Shaykhnazar\HikvisionIsapi\Facades\Hikvision;

// Register a temporary device
Hikvision::registerDevice('temp_device', [
    'ip' => '192.168.1.150',
    'port' => 80,
    'username' => 'admin',
    'password' => 'password',
    'protocol' => 'http',
    'timeout' => 30,
    'verify_ssl' => false,
]);

// Use the temporary device
$client = Hikvision::device('temp_device');

Switching Providers at Runtime

Change device provider dynamically:

use Shaykhnazar\HikvisionIsapi\Facades\Hikvision;
use Shaykhnazar\HikvisionIsapi\Client\Providers\DatabaseDeviceProvider;
use Shaykhnazar\HikvisionIsapi\Client\Providers\ConfigDeviceProvider;

// Switch to database provider
$dbProvider = new DatabaseDeviceProvider(table: 'terminals');
Hikvision::setProvider($dbProvider);

// Now all devices are loaded from database
$devices = Hikvision::availableDevices();

// Switch back to config provider
$configProvider = new ConfigDeviceProvider(config('hikvision'));
Hikvision::setProvider($configProvider);

Advanced Usage

Batch Operations

$cardService = app(CardService::class);

$cards = [
    new Card('EMP001', '1234567890'),
    new Card('EMP002', '0987654321'),
    new Card('EMP003', '1122334455'),
];

$results = $cardService->batchAdd($cards);

/*
Returns:
[
    'total' => 3,
    'success' => 3,
    'failed' => 0,
    'errors' => []
]
*/

Pagination

$personService = app(PersonService::class);
$total = $personService->count();
$perPage = 30;
$pages = ceil($total / $perPage);

for ($page = 0; $page < $pages; $page++) {
    $persons = $personService->search($page, $perPage);
    // Process persons...
}

Custom HTTP Requests

use Shaykhnazar\HikvisionIsapi\Facades\HikvisionIsapi;

// GET request
$response = HikvisionIsapi::get('/ISAPI/CustomEndpoint', [
    'param1' => 'value1',
]);

// POST request
$response = HikvisionIsapi::post('/ISAPI/CustomEndpoint', [
    'key' => 'value',
]);

// PUT request
$response = HikvisionIsapi::put('/ISAPI/CustomEndpoint', [
    'key' => 'value',
]);

// DELETE request
$response = HikvisionIsapi::delete('/ISAPI/CustomEndpoint');

Testing

# Run tests
./vendor/bin/phpunit

# Run specific test file
./vendor/bin/phpunit tests/Feature/PersonServiceTest.php

# Run with coverage
./vendor/bin/phpunit --coverage-html coverage

Troubleshooting

Authentication Failed (401)

Check your credentials in .env:

# Test with curl
curl -v --digest -u admin:password http://192.168.1.100/ISAPI/System/deviceInfo

Connection Timeout

  • Verify device IP and network connectivity
  • Increase timeout in config: HIKVISION_TIMEOUT=60
  • Check firewall rules

Face Upload Fails

  • Ensure image is JPEG format
  • Check image size (max 200KB)
  • Verify person exists on device first
  • Image quality should be good with clear face

Device Not Found

  • Check IP address in .env
  • Verify device is powered on
  • Ensure device is on same network

Security

  • Store credentials in .env file (never commit)
  • Use HTTPS in production
  • Enable SSL verification with valid certificates
  • Implement rate limiting on your API endpoints
  • Validate all user input

Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch
  3. Write tests for new features
  4. Follow PSR-12 coding standards
  5. Submit a pull request

Changelog

v1.4.0 (2025-10-30)

  • EventNotificationService: New service for configuring HTTP event notifications (webhooks)
  • Real-Time Webhooks: Configure devices to push events to your API endpoints automatically
  • XML Support: Added putXml() method to HikvisionClient for XML-only endpoints
  • HTTP Client Enhancements:
    • Added arrayToXml() method for converting arrays to XML format
    • Added xmlToArray() method for parsing XML responses
    • Automatic XML/JSON format detection based on Content-Type
  • Event Notification Methods:
    • configureWebhook() - Simple one-line webhook setup
    • configureHttpHost() - Advanced webhook configuration with authentication
    • enableHttpHost() / disableHttpHost() - Enable/disable webhooks
    • testHttpNotification() - Test webhook connectivity
    • getCapabilities() - Get notification capabilities
  • Dependencies: Added ext-simplexml and ext-libxml for XML processing
  • Multi-Device Webhooks: Configure webhooks for multiple devices simultaneously
  • Webhook Security: Support for HTTP Basic and Digest authentication
  • 100% backward compatible with v1.3.0

v1.3.0 (2025-10-13)

  • Universal Device Providers: Framework for loading devices from any source
  • DatabaseDeviceProvider: Load terminals from database tables with built-in caching
  • CallbackDeviceProvider: Maximum flexibility with custom callbacks (API, Redis, etc.)
  • Multi-Tenant Support: Tenant-scoped device loading and isolation
  • Runtime Device Registration: Register devices dynamically at runtime
  • Provider Switching: Change device providers on-the-fly
  • Hot Reload: Update database-driven configurations without restart
  • Configurable Caching: Built-in cache support with TTL control
  • 100% backward compatible with v1.2.0

v1.2.0 (2025-10-13)

  • Multi-Device Support: Manage unlimited Hikvision devices simultaneously
  • DeviceManager: Central manager with lazy initialization and caching
  • New Hikvision Facade: Multi-device access with Hikvision::device()
  • Device Discovery: List and validate configured devices
  • Device-Specific Clients: Automatic client caching per device
  • 100% backward compatible with single-device setups

v1.1.0 (2025-10-13)

  • Face Search Functionality: Search face data with pagination support
  • Face Data Record Upload: Multipart/form-data support for face images
  • Fixed PersonService Delete Endpoint: Updated to match ISAPI specification
  • Configuration Validation: Required username/password validation
  • Extended HTTP Client: Added multipart form-data upload support
  • Enhanced error handling and validation

v1.0.0 (2025-10-09)

  • Initial release
  • Full ISAPI support
  • Person, Card, Face, Fingerprint management
  • Access control and event handling
  • Laravel 12.x support
  • PHP 8.2+ with modern features

License

This package is open-sourced software licensed under the MIT license.

Credits

Support

For issues, questions, or contributions:

Disclaimer

This package is designed for legitimate access control systems and defensive security purposes only. Do not use for malicious purposes.

Made with ❤️ for the Laravel community

统计信息

  • 总下载量: 66
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 6
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 5
  • Watchers: 0
  • Forks: 1
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-10-10