shaykhnazar/hikvision-isapi
A Laravel/PHP client for Hikvision ISAPI devices, offering convenient access to camera and access-control endpoints. Simplifies authentication, HTTP requests, and common ISAPI operations while leaving room for custom device calls.
Before installing, confirm the runtime: v2 requires Laravel 12 or 13 (illuminate/support ^12.0|^13.0), PHP 8.2–8.4, and Testbench 10/11 for package tests. If the project is on Laravel 11, stop here and stay on the 1.x line; v2 cannot be installed or tested there.
Install the beta explicitly and pin it, rather than floating on a broad constraint:
composer require <vendor>/<package>:2.0.0-beta.1
Because this is a beta that has not run against a real terminal, treat the first install as a staging exercise. Look first at the package config (device credentials, HTTP timeouts, retry settings), then at EventService, HttpClient, and the exception hierarchy. The exception classes matter more in v2 than before because retry behavior is now part of the public API.
The best first use case is a read-only event backfill: inject EventService, call between() over a small recent time window, and persist each yielded event with an idempotent upsert. Wrap the loop in a try/catch for HikvisionException, log statusCode() and a sanitized responseBody(), and branch on isRetryable(). That exercises authentication, HTTP transport, pagination, event mapping, and retry classification in one small workflow before you wire anything into webhooks or queues.
Backfill dropped webhook events. EventService::between() is the primitive for repairing missed deliveries. Run it from a scheduled command or queued job over a bounded window, iterate the generator, and upsert by device event ID. Keep a checkpoint of the last successfully persisted event time; on failure, restart from the checkpoint with a small overlap rather than from the original window start. Because it reuses one searchID across pages and advances by records actually returned, it is safer than manually paging search().
Reconcile webhooks with backfill. Keep webhook handling for low-latency ingestion, then run between() on a schedule for the same window. Make persistence idempotent so the two paths can overlap. This gives you both timely events and a repair path when webhook delivery drops.
Paginate search() correctly. When calling search() directly, pass the optional $searchId through every page request and stop when a page returns no records. Do not calculate pages from a fixed page size; advance by the records actually returned. If the search ID is lost or an exception occurs mid-pagination, restart the search window rather than guessing the next offset.
Classify retries centrally. Catch DeviceUnreachableException and DeviceBusyException as retryable-by-default candidates, use isRetryable() as the general decision point, and treat AuthenticationException separately. HTTP 401 now raises AuthenticationException, so do not retry it blindly; refresh credentials or surface a configuration error. Use statusCode() and responseBody() for structured logs, but sanitize the body before logging.
Inject a pre-configured Guzzle client for tests and infrastructure. In tests, bind an HttpClient built with a Guzzle MockHandler to assert pagination, 401 handling, and retry paths without a device. In production, use the same extension point to add proxy settings, timeouts, or middleware. Verify whether your custom client must also carry the package’s base URI and authentication defaults.
Queue per device. Backfills can be long-running. Dispatch one job per device and window, apply a rate limit or concurrency limit per device, and avoid holding a database transaction open across the generator. Persist in batches and update the checkpoint after each successful batch.
The beta is not terminal-tested. The release notes explicitly say the new APIs have not run against a real terminal. Pin the exact beta version, test against a real device in staging, and avoid production rollout until you have validated authentication, pagination, malformed responses, and retry behavior. Keep the 1.x line as a fallback.
Laravel 11 is a hard break, not a soft deprecation. Composer 2.9+ will refuse to resolve Laravel 11 because of unresolved security advisories, and v2’s constraints exclude it. Do not try to force the constraints; upgrade Laravel first or stay on 1.x.
Generator lifetime is your responsibility. between() is memory-friendly, but a long window can outlive HTTP timeouts, queue visibility timeouts, or database connections. Bound windows, checkpoint aggressively, and prefer smaller scheduled windows over one giant historical backfill.
Do not mix searchID values across contexts. A search ID belongs to one search operation. Reusing it across different devices, windows, or criteria can produce incorrect pagination. If an exception interrupts a search, discard the ID and restart the window.
401 handling may break existing catch blocks. Code that previously caught a generic exception and retried will now see AuthenticationException. Update handlers so bad credentials fail fast and do not enter exponential backoff.
Malformed XML no longer raises a PHP warning. That is an improvement, but it means malformed device responses may surface as empty or partial data rather than a warning. Validate required fields and log the raw body at debug level when mapping produces unexpected results.
Custom Guzzle clients can bypass package defaults. If you inject a client, make sure timeouts, base URI, authentication, and middleware are configured as the package expects. A mock client that works in tests can still hide missing production defaults.
Be careful with responseBody() in logs. It may contain device identifiers, credentials, or internal URLs. Log status codes and sanitized snippets, and never log authorization headers.
Keep Guzzle aligned. v2 updates Guzzle to 7.15.5 and clears audit advisories. Check transitive dependencies if your application pins an older Guzzle, and run composer audit after upgrading.
Use the extension points, not forks. The custom Guzzle client and the exception taxonomy are the intended seams for retry policy, testing, and observability. Avoid forking pagination logic; rely on between() and search($searchId) so future fixes to search behavior apply cleanly.
How can I help you explore Laravel packages today?