@apocaliss92/nodelink-js 0.2.5 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -300,27 +300,30 @@ export declare type AutoDetectResult = {
300
300
  };
301
301
 
302
302
  /**
303
- * Client per discovery continuato di telecamere Reolink sulla rete.
304
- * Supporta TCP/IPC (HTTP/HTTPS scanning) e/o UDP broadcast discovery.
303
+ * Continuous discovery client for Reolink cameras on the network.
304
+ * Runs periodic scans using all configured discovery methods and maintains
305
+ * an always-up-to-date list of discovered devices.
305
306
  *
306
- * Mantiene una lista aggiornata delle telecamere discoverate e offre
307
- * metodi per ottenere la lista corrente e controllare il processo di discovery.
307
+ * Supports callbacks for new/updated devices, making it ideal for plugins
308
+ * that want to be notified as cameras appear (e.g. battery cameras waking up).
308
309
  *
309
310
  * @example
310
311
  * ```typescript
311
312
  * const client = new AutodiscoveryClient({
312
- * username: "admin",
313
- * password: "password",
314
- * scanIntervalMs: 30000, // 30 secondi
313
+ * enableArpLookup: true,
314
+ * enableOnvifDiscovery: true,
315
+ * scanIntervalMs: 60_000,
315
316
  * autoStart: true,
317
+ * onDeviceDiscovered: (device) => {
318
+ * console.log(`New device: ${device.host} (${device.model})`);
319
+ * },
316
320
  * });
317
321
  *
318
- * // Ottieni la lista corrente
322
+ * // Get the current list
319
323
  * const devices = client.getDiscoveredDevices();
320
- * console.log(`Trovate ${devices.length} telecamere`);
321
324
  *
322
- * // Ferma il discovery
323
- * await client.stop();
325
+ * // Stop
326
+ * client.stop();
324
327
  * ```
325
328
  */
326
329
  export declare class AutodiscoveryClient {
@@ -329,96 +332,54 @@ export declare class AutodiscoveryClient {
329
332
  private scanTimer;
330
333
  private isRunning;
331
334
  private currentScanPromise;
332
- /**
333
- * Costruttore del client di autodiscovery.
334
- *
335
- * @param options - Opzioni di configurazione per il discovery
336
- */
337
335
  constructor(options?: AutodiscoveryClientOptions);
338
336
  /**
339
- * Avvia il discovery continuato.
340
- * Se già in esecuzione, non fa nulla.
337
+ * Start continuous discovery. If already running, does nothing.
341
338
  */
342
339
  start(): void;
343
340
  /**
344
- * Ferma il discovery continuato.
345
- * Se non è in esecuzione, non fa nulla.
341
+ * Stop continuous discovery. If not running, does nothing.
346
342
  */
347
343
  stop(): void;
348
344
  /**
349
- * Restituisce la lista corrente delle telecamere discoverate.
350
- *
351
- * @returns Array di dispositivi discoverati, ordinati per host
345
+ * Returns the current list of discovered devices, sorted by host IP.
352
346
  */
353
347
  getDiscoveredDevices(): DiscoveredDevice[];
354
348
  /**
355
- * Restituisce il numero di telecamere attualmente discoverate.
356
- *
357
- * @returns Numero di dispositivi discoverati
349
+ * Returns the number of currently discovered devices.
358
350
  */
359
351
  getDeviceCount(): number;
360
352
  /**
361
- * Verifica se il discovery è attualmente in esecuzione.
362
- *
363
- * @returns `true` se il discovery è in esecuzione, `false` altrimenti
353
+ * Returns whether continuous discovery is currently running.
364
354
  */
365
355
  isActive(): boolean;
366
356
  /**
367
- * Forza un scan immediato (non aspetta l'intervallo programmato).
368
- * Se uno scan è già in corso, attende il completamento prima di avviarne uno nuovo.
369
- *
370
- * @returns Promise che si risolve quando lo scan è completato
357
+ * Force an immediate scan (doesn't wait for the scheduled interval).
358
+ * If a scan is already in progress, waits for it to complete.
371
359
  */
372
360
  scanNow(): Promise<void>;
373
361
  /**
374
- * Rimuove un dispositivo dalla lista (utile se si sa che non è più disponibile).
375
- *
376
- * @param host - Indirizzo IP del dispositivo da rimuovere
377
- * @returns `true` se il dispositivo è stato rimosso, `false` se non era presente
362
+ * Remove a device from the discovered list.
378
363
  */
379
364
  removeDevice(host: string): boolean;
380
365
  /**
381
- * Pulisce tutte le telecamere discoverate dalla lista.
366
+ * Clear all discovered devices.
382
367
  */
383
368
  clearDevices(): void;
384
- /**
385
- * Esegue un singolo scan della rete.
386
- */
387
369
  private performScan;
388
- /**
389
- * Unisce le informazioni di un dispositivo esistente con quelle di un nuovo scan.
390
- * Restituisce il dispositivo aggiornato se ci sono state modifiche, altrimenti `null`.
391
- */
392
370
  private mergeDeviceInfo;
393
- /**
394
- * Programma il prossimo scan.
395
- */
396
371
  private scheduleNextScan;
397
372
  }
398
373
 
399
- export declare interface AutodiscoveryClientOptions {
400
- /** Network CIDR to scan (e.g., "192.168.1.0/24"). If not provided, auto-detects local network */
401
- networkCidr?: string;
402
- /** Username to use for authentication attempts (default: "admin") */
403
- username?: string;
404
- /** Password to use for authentication attempts (default: empty, will try unauthenticated) */
405
- password?: string;
406
- /** Timeout per HTTP probe in milliseconds (default: 2000) */
407
- httpProbeTimeoutMs?: number;
408
- /** Maximum number of concurrent HTTP probes (default: 50) */
409
- maxConcurrentProbes?: number;
410
- /** Logger instance for debug output */
411
- logger?: Logger;
412
- /** Ports to scan for HTTP (default: [80, 443]) */
413
- httpPorts?: number[];
414
- /** Interval between discovery scans in milliseconds (default: 60000 = 60 seconds) */
374
+ export declare interface AutodiscoveryClientOptions extends DiscoveryOptions {
375
+ /** Interval between discovery scans in milliseconds (default: 120000 = 2 minutes) */
415
376
  scanIntervalMs?: number;
416
377
  /** Whether to start discovery automatically on construction (default: false) */
417
378
  autoStart?: boolean;
418
- /** Discovery method to use: "http" (TCP/IPC), "udp" (broadcast), or "both" (default: "http") */
419
- discoveryMethod?: "http" | "udp" | "both";
420
- /** Timeout for UDP broadcast in milliseconds (default: 5000) */
421
- udpBroadcastTimeoutMs?: number;
379
+ /** Called when new (previously unseen) devices are discovered */
380
+ onDeviceDiscovered?: (device: DiscoveredDevice) => void;
381
+ /** Called when an existing device's info is updated (e.g. model filled in) */
382
+ onDeviceUpdated?: (device: DiscoveredDevice) => void;
422
383
  }
423
384
 
424
385
  export declare type BaichuanCachedPush<T> = {
@@ -461,6 +422,22 @@ export declare class BaichuanClient extends EventEmitter<{
461
422
  private static readonly coverPreviewBackoffMs;
462
423
  private static readonly COVER_PREVIEW_INITIAL_BACKOFF_MS;
463
424
  private static readonly COVER_PREVIEW_MAX_BACKOFF_MS;
425
+ /**
426
+ * Per-client snapshot (cmd_id=109) serialization queue.
427
+ *
428
+ * WHY: On NVR/multi-camera devices sharing one socket, concurrent snapshot requests
429
+ * can cause JPEG data to mix (even with per-request msgNum filtering):
430
+ * - Camera A and B both send frames on same socket
431
+ * - Frame listener is global per socket
432
+ * - Timing quirks can cause chunk reordering or listener confusion
433
+ *
434
+ * FIX: Serialize all cmd_id=109 requests on THIS client instance.
435
+ * Each snapshot waits for previous one to complete before starting.
436
+ * This ensures clean frame sequences per request, zero data corruption.
437
+ *
438
+ * Impact: Snapshots are ~0–50ms slower per camera (negligible for users).
439
+ */
440
+ private snapshotQueueTail;
464
441
  private readonly opts;
465
442
  private readonly debugCfg;
466
443
  private readonly logger;
@@ -807,6 +784,7 @@ export declare class BaichuanClient extends EventEmitter<{
807
784
  private sendBinaryFileDownload6482;
808
785
  private sendBinaryFileInfoListReplay5;
809
786
  private sendBinarySnapshot109;
787
+ private sendBinarySnapshot109Impl;
810
788
  /**
811
789
  * Send CoverPreview command (cmd_id=298) to get an I-frame from a past recording.
812
790
  * Similar to sendBinarySnapshot109 but handles the stream header + frame format
@@ -3628,7 +3606,7 @@ export declare interface DiscoveredDevice {
3628
3606
  /** Firmware version (if available) */
3629
3607
  firmwareVersion?: string;
3630
3608
  /** Discovery method used to find this device */
3631
- discoveryMethod: "http_probe" | "udp_broadcast" | "udp_direct";
3609
+ discoveryMethod: "http_probe" | "udp_broadcast" | "udp_direct" | "tcp_port_scan" | "arp" | "dhcp" | "onvif";
3632
3610
  /** Whether HTTPS is supported */
3633
3611
  supportsHttps?: boolean;
3634
3612
  /** Whether the device is accessible via HTTP */
@@ -3657,11 +3635,45 @@ export declare interface DiscoveredDevice {
3657
3635
  */
3658
3636
  export declare function discoverReolinkDevices(options?: DiscoveryOptions): Promise<DiscoveredDevice[]>;
3659
3637
 
3638
+ /**
3639
+ * Discover Reolink devices by reading the system ARP table and filtering
3640
+ * for Reolink MAC address prefixes (EC:71:DB). This mirrors the DHCP-based
3641
+ * discovery used by Home Assistant but reads the already-populated ARP cache
3642
+ * instead of sniffing live DHCP packets.
3643
+ *
3644
+ * Works on Linux, macOS, and Docker containers (reads /proc/net/arp or `arp -a`).
3645
+ */
3646
+ export declare function discoverViaArpTable(options: DiscoveryOptions): Promise<DiscoveredDevice[]>;
3647
+
3648
+ /**
3649
+ * Discover Reolink devices by passively listening for DHCP traffic (port 67/68).
3650
+ * Parses DHCP ACK/REQUEST packets and filters for Reolink MAC prefix or hostname.
3651
+ * Requires root or CAP_NET_RAW (typical in Docker with --net=host).
3652
+ *
3653
+ * This mirrors Home Assistant's DHCP discovery: matches hostname "reolink*" or MAC "EC:71:DB".
3654
+ */
3655
+ export declare function discoverViaDhcpListener(options: DiscoveryOptions): Promise<DiscoveredDevice[]>;
3656
+
3660
3657
  /**
3661
3658
  * Discover devices via HTTP port scanning.
3662
3659
  */
3663
3660
  export declare function discoverViaHttpScan(options: DiscoveryOptions): Promise<DiscoveredDevice[]>;
3664
3661
 
3662
+ /**
3663
+ * Discover devices via ONVIF WS-Discovery (multicast probe on 239.255.255.250:3702).
3664
+ * Most Reolink cameras and NVRs support ONVIF and will respond with their service
3665
+ * endpoint (XAddrs), model, and scopes. This is the standard IP camera discovery
3666
+ * mechanism used by NVRs, VMS, and similar software.
3667
+ */
3668
+ export declare function discoverViaOnvif(options: DiscoveryOptions): Promise<DiscoveredDevice[]>;
3669
+
3670
+ /**
3671
+ * Discover Reolink devices by scanning TCP port 9000 (Baichuan protocol).
3672
+ * This is the most reliable method — every Reolink camera/NVR listens on port 9000.
3673
+ * It only checks if the port is open; it does not authenticate or extract device info.
3674
+ */
3675
+ export declare function discoverViaTcpPortScan(options: DiscoveryOptions): Promise<DiscoveredDevice[]>;
3676
+
3665
3677
  /**
3666
3678
  * Discover devices via UDP broadcast (for battery cameras).
3667
3679
  */
@@ -3692,6 +3704,20 @@ export declare type DiscoveryOptions = {
3692
3704
  enableUdpDiscovery?: boolean;
3693
3705
  /** Whether to enable HTTP port scanning (default: true) */
3694
3706
  enableHttpScanning?: boolean;
3707
+ /** Whether to enable TCP port 9000 scanning (Baichuan protocol). Works with all Reolink devices. (default: false) */
3708
+ enableTcpPortScan?: boolean;
3709
+ /** Timeout per TCP port probe in milliseconds (default: 1500) */
3710
+ tcpProbeTimeoutMs?: number;
3711
+ /** Whether to enable ARP table lookup for Reolink MAC prefix (ec:71:db). Similar to Home Assistant DHCP discovery. (default: false) */
3712
+ enableArpLookup?: boolean;
3713
+ /** Whether to enable passive DHCP listening for Reolink devices (requires root/NET_RAW). (default: false) */
3714
+ enableDhcpListener?: boolean;
3715
+ /** Timeout for DHCP listener in milliseconds (default: 10000) */
3716
+ dhcpListenerTimeoutMs?: number;
3717
+ /** Whether to enable ONVIF WS-Discovery (multicast probe on 239.255.255.250:3702). Most Reolink cameras support ONVIF. (default: false) */
3718
+ enableOnvifDiscovery?: boolean;
3719
+ /** Timeout for ONVIF WS-Discovery in milliseconds (default: 5000) */
3720
+ onvifDiscoveryTimeoutMs?: number;
3695
3721
  /** Ports to scan for HTTP (default: [80, 443]) */
3696
3722
  httpPorts?: number[];
3697
3723
  };
@@ -4031,6 +4057,104 @@ export declare interface GetVodUrlParams {
4031
4057
  */
4032
4058
  export declare function getXmlText(xml: string, tagName: string): string | undefined;
4033
4059
 
4060
+ export declare class Go2rtcTcpServer extends EventEmitter<{
4061
+ client: [string];
4062
+ clientDisconnected: [string];
4063
+ error: [Error];
4064
+ close: [];
4065
+ listening: [{
4066
+ host: string;
4067
+ port: number;
4068
+ }];
4069
+ }> {
4070
+ private readonly api;
4071
+ private readonly channel;
4072
+ private readonly profile;
4073
+ private readonly variant;
4074
+ private readonly listenHost;
4075
+ private readonly listenPort;
4076
+ private readonly logger;
4077
+ private readonly deviceId;
4078
+ private readonly gracePeriodMs;
4079
+ private readonly prebufferMaxMs;
4080
+ private readonly maxBufferBytes;
4081
+ private readonly prestartStream;
4082
+ private active;
4083
+ private server;
4084
+ private resolvedPort;
4085
+ private nativeFanout;
4086
+ private nativeStreamActive;
4087
+ private dedicatedSessionRelease;
4088
+ private detectedVideoType;
4089
+ private connectedClients;
4090
+ private clientSockets;
4091
+ private stopGraceTimer;
4092
+ private prebuffer;
4093
+ constructor(options: Go2rtcTcpServerOptions);
4094
+ /** Start listening. Resolves once the TCP server is bound. */
4095
+ start(): Promise<void>;
4096
+ /** Stop the server and all active streams. */
4097
+ stop(): Promise<void>;
4098
+ /** The actual port the server is listening on (available after start()). */
4099
+ get port(): number | undefined;
4100
+ /** The go2rtc-compatible source URL. */
4101
+ get go2rtcSourceUrl(): string | undefined;
4102
+ /** Number of currently connected clients. */
4103
+ get clientCount(): number;
4104
+ private handleClient;
4105
+ private feedClient;
4106
+ /**
4107
+ * Convert a native frame to wire-ready Annex-B.
4108
+ * Audio frames are skipped — raw TCP carries only video (Annex-B).
4109
+ * go2rtc auto-detects the codec from SPS/PPS/VPS NALUs.
4110
+ */
4111
+ private convertFrame;
4112
+ /** Check if an Annex-B buffer contains a keyframe (IDR for H.264, IRAP for H.265). */
4113
+ private isAnnexBKeyframe;
4114
+ /** Split Annex-B byte stream into individual NAL units. */
4115
+ private static splitAnnexBNals;
4116
+ private startNativeStream;
4117
+ private stopNativeStream;
4118
+ private removeClient;
4119
+ private scheduleStop;
4120
+ }
4121
+
4122
+ export declare interface Go2rtcTcpServerOptions {
4123
+ /** API instance (required). */
4124
+ api: ReolinkBaichuanApi;
4125
+ /** Channel number (required). */
4126
+ channel: number;
4127
+ /** Stream profile (required). */
4128
+ profile: StreamProfile;
4129
+ /** TrackMix tele/autotrack variant. */
4130
+ variant?: NativeVideoStreamVariant;
4131
+ /** Host to listen on (default: "127.0.0.1"). */
4132
+ listenHost?: string;
4133
+ /** Port to listen on (default: 0 = OS picks). */
4134
+ listenPort?: number;
4135
+ /** Logger. */
4136
+ logger?: Logger;
4137
+ /**
4138
+ * External identifier for dedicated socket session.
4139
+ * When provided, a dedicated BaichuanClient is created for the stream,
4140
+ * isolating it from other streams on the shared socket.
4141
+ */
4142
+ deviceId?: string;
4143
+ /** Grace period in ms before stopping native stream after last client disconnects (default: 30 000). */
4144
+ gracePeriodMs?: number;
4145
+ /** Maximum prebuffer window in ms (default: 3 000). */
4146
+ prebufferMs?: number;
4147
+ /** Maximum write buffer per client before dropping the connection (default: 100 MB). */
4148
+ maxBufferBytes?: number;
4149
+ /**
4150
+ * When true, the native camera stream is started immediately on start()
4151
+ * rather than waiting for the first TCP client. This ensures frames are
4152
+ * already flowing (and the prebuffer is warm) when go2rtc connects.
4153
+ * Default: true.
4154
+ */
4155
+ prestartStream?: boolean;
4156
+ }
4157
+
4034
4158
  /**
4035
4159
  * Depacketizer for H.264 "RTP-like" payloads (RFC 6184) when the camera sends single NAL units,
4036
4160
  * STAP/MTAP aggregation or FU-A/FU-B fragmentation inside a BcMedia frame payload.