@apocaliss92/nodelink-js 0.4.11 → 0.4.13

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
@@ -180,15 +180,27 @@ export declare type AiTypesCacheEntry = {
180
180
  export declare type AnyBuffer = Buffer<ArrayBufferLike>;
181
181
 
182
182
  /**
183
- * Patch one or more fields inside an `<Enc>` stream block
184
- * (`<mainStream>` or `<subStream>`). Used by `setEnc` —
185
- * Reolink emits both blocks in the same document so a per-block scope
186
- * is needed to avoid clobbering the wrong stream.
187
- */
188
- export declare function applyStreamPatch(xml: string, streamTag: "mainStream" | "subStream", patch: {
183
+ * Patch fields inside a `<Compression>` stream block (`<mainStream>`,
184
+ * `<subStream>`, or `<thirdStream>`). Used by `setEnc` — Reolink emits all
185
+ * three blocks in the same document so a per-block scope is needed to avoid
186
+ * clobbering the wrong stream.
187
+ *
188
+ * The mapping `videoEncType` codec uses the convention seen on the wire:
189
+ * 0 = H.264
190
+ * 1 = H.265
191
+ * Both `<frame>` (camera-side preferred) and `<frameRate>` (older reolink_aio
192
+ * naming) are patched defensively — no-op if only one is present.
193
+ */
194
+ export declare function applyStreamPatch(xml: string, streamTag: "mainStream" | "subStream" | "thirdStream", patch: {
195
+ audio?: 0 | 1;
196
+ width?: number;
197
+ height?: number;
189
198
  bitRate?: number;
190
199
  frameRate?: number;
191
200
  videoEncType?: "h264" | "h265";
201
+ encoderType?: "vbr" | "cbr";
202
+ encoderProfile?: "high" | "main" | "baseline";
203
+ gop?: number;
192
204
  } | undefined): string;
193
205
 
194
206
  /**
@@ -435,6 +447,17 @@ export declare interface AutoFocusConfig {
435
447
  };
436
448
  }
437
449
 
450
+ /** AutoReboot schedule (cmdId=100/101). */
451
+ export declare interface AutoRebootConfig {
452
+ enable: 0 | 1;
453
+ weekDay: DayOfWeek;
454
+ hour: number;
455
+ minute: number;
456
+ second: number;
457
+ }
458
+
459
+ export declare type AutoRebootConfigPatch = Partial<AutoRebootConfig>;
460
+
438
461
  export declare type BaichuanCachedPush<T> = {
439
462
  updatedAtMs: number;
440
463
  value: T;
@@ -464,6 +487,23 @@ export declare class BaichuanClient extends EventEmitter<{
464
487
  * even if the current client instance is idle/disconnected.
465
488
  */
466
489
  private static readonly streamingRegistry;
490
+ /**
491
+ * Per-device set of live BaichuanClient instances.
492
+ *
493
+ * Why: when a streaming client unsubscribes (e.g. RTSP grace timer expires
494
+ * and SocketPool tears the streaming socket down), the global streaming
495
+ * registry decrements but the GENERAL client of the same device has no
496
+ * way of knowing — its idle-disconnect timer was last evaluated while
497
+ * `isDeviceStreamingActive()` was still true (because the streaming socket
498
+ * was still alive) and wasn't rescheduled. Without this registry the
499
+ * general socket stays connected, the 60-second session-guard timer keeps
500
+ * sending getOnlineUserList() to the camera, and a battery camera ends up
501
+ * waking up every minute (issue #18).
502
+ *
503
+ * On streamingRegistry decrement-to-zero we walk this set and kick every
504
+ * sibling's idle-disconnect timer so it can re-evaluate eligibility.
505
+ */
506
+ private static readonly deviceClients;
467
507
  /**
468
508
  * Per-host D2C_DISC backoff state that persists across client instance recreation.
469
509
  *
@@ -552,6 +592,10 @@ export declare class BaichuanClient extends EventEmitter<{
552
592
  private streamTraceStats;
553
593
  private rxCmdTraceStats;
554
594
  private readonly alarmEventState;
595
+ /** Whether this instance is currently in BaichuanClient.deviceClients. */
596
+ private registeredInDeviceClients;
597
+ private registerInDeviceClients;
598
+ private unregisterFromDeviceClients;
555
599
  constructor(options: BaichuanClientOptions);
556
600
  private newSocketSessionId;
557
601
  private logFixed;
@@ -1701,6 +1745,50 @@ export declare type BaichuanStreamInfoList = {
1701
1745
 
1702
1746
  export declare type BaichuanTransport = "tcp" | "udp";
1703
1747
 
1748
+ /**
1749
+ * Snapshot of `<VersionInfo>` returned by Baichuan cmd_id=80
1750
+ * (`BC_CMD_ID_GET_VERSION_INFO`) — the same payload the Reolink mobile app
1751
+ * displays in "About this device". Distinct from `getSystemGeneral`
1752
+ * (cmd_104) which holds time/locale.
1753
+ *
1754
+ * All fields optional because firmware variants and models include
1755
+ * different subsets. Empty XML elements (e.g. `<cc3200Version></cc3200Version>`)
1756
+ * come through as the empty string so callers can distinguish "absent"
1757
+ * (undefined) from "explicitly empty" ("").
1758
+ */
1759
+ export declare interface BaichuanVersionInfo {
1760
+ /** User-assigned name (matches the OSD camera name). */
1761
+ name?: string;
1762
+ /** Model code, e.g. `"E1 Zoom"`, `"RLC-810A"`. */
1763
+ type?: string;
1764
+ /** Hardware serial number. */
1765
+ serialNumber?: string;
1766
+ /** Build identifier, typically a date string like `"build 2503281992"`. */
1767
+ buildDay?: string;
1768
+ /** Internal hardware revision, e.g. `"IPC_NT14NA48MPSD6"`. */
1769
+ hardwareVersion?: string;
1770
+ /** Config-format version Reolink uses to identify the schema the device speaks. */
1771
+ cfgVersion?: string;
1772
+ /** Firmware version, e.g. `"v3.2.0.4741_2503281992"`. */
1773
+ firmwareVersion?: string;
1774
+ /** Extended hardware detail (often hardwareVersion + region/sku suffix). */
1775
+ detail?: string;
1776
+ /** IE-Client identifier (legacy plug-in tag, usually `"IEClient"`). */
1777
+ IEClient?: string;
1778
+ /** Legacy MCU firmware version (present on some Reolink wireless models). */
1779
+ cc3200Version?: string;
1780
+ /** Signal-processor firmware version (rarely populated). */
1781
+ spVersion?: string;
1782
+ /** File extension Reolink expects for firmware updates (`"pak"`). */
1783
+ pakSuffix?: string;
1784
+ /** Reolink item / SKU number, e.g. `"E340"`. */
1785
+ itemNo?: string;
1786
+ /** AI-model bundle version (the on-device people/vehicle/animal detector). */
1787
+ aiVersion?: string;
1788
+ /** Diagnostic helper string the app sends to support, e.g. `"blackPointsLevel=0"`. */
1789
+ helpVersion?: string;
1790
+ }
1791
+
1704
1792
  export declare type BaichuanVideoInputPush = {
1705
1793
  channelId?: number;
1706
1794
  bright?: number;
@@ -1737,6 +1825,22 @@ export declare class BaichuanVideoStream extends EventEmitter<{
1737
1825
  time?: number;
1738
1826
  }
1739
1827
  ];
1828
+ /**
1829
+ * Raw BcMedia `additionalHeader` block emitted for every I-frame and P-frame.
1830
+ * Carries Reolink's per-frame side-channel metadata (detection bounding boxes).
1831
+ * `frameWidth`/`frameHeight` come from the most recent BcMedia InfoV1/V2 packet
1832
+ * so consumers can normalize pixel coordinates against the actual stream size.
1833
+ */
1834
+ additionalHeader: [
1835
+ {
1836
+ raw: Buffer;
1837
+ frameType: "Iframe" | "Pframe";
1838
+ videoType: "H264" | "H265";
1839
+ microseconds: number;
1840
+ frameWidth?: number;
1841
+ frameHeight?: number;
1842
+ }
1843
+ ];
1740
1844
  audioFrame: [Buffer];
1741
1845
  error: [Error];
1742
1846
  close: [];
@@ -1755,6 +1859,13 @@ export declare class BaichuanVideoStream extends EventEmitter<{
1755
1859
  private readonly acceptAnyStreamType;
1756
1860
  private lockedChannelId;
1757
1861
  private bcMediaCodec;
1862
+ /**
1863
+ * Diagnostic-only accessor for the BcMedia codec. Used by tools that need to
1864
+ * inspect unknown chunks (for example to discover undocumented audio
1865
+ * sub-packets the parser currently skips). Not part of the supported public
1866
+ * surface — do not rely on it in application code.
1867
+ */
1868
+ get _bcMediaCodec(): BcMediaCodec;
1758
1869
  private debugH264LogsLeft;
1759
1870
  private debugSavedSamples;
1760
1871
  private warnedNonAnnexBOnce;
@@ -1773,6 +1884,9 @@ export declare class BaichuanVideoStream extends EventEmitter<{
1773
1884
  private lastPpsH265;
1774
1885
  private lastPrependedParamSetsH265;
1775
1886
  private aesStreamDecryptor;
1887
+ private latestFrameWidth;
1888
+ private latestFrameHeight;
1889
+ private detectionTeardown;
1776
1890
  /**
1777
1891
  * Pending startup error stashed when emitSafeError is called before any
1778
1892
  * "error" listener is registered (e.g. camera returns 400 during start()).
@@ -1902,6 +2016,14 @@ export declare class BaichuanWebRTCServer extends EventEmitter {
1902
2016
  * H.265 → DataChannel with raw Annex-B frames (decoded by WebCodecs in browser)
1903
2017
  */
1904
2018
  private pumpFramesToWebRTC;
2019
+ /**
2020
+ * Lazily start the AAC → Opus transcoder for `session` and wire it to the
2021
+ * audio RTP track. ffmpeg writes RTP-formatted Opus packets back to a UDP
2022
+ * loopback the transcoder owns; we strip the RTP header and rewrap the
2023
+ * Opus payload with our audioTrack's SSRC so the browser receives a
2024
+ * coherent stream.
2025
+ */
2026
+ private ensureAudioTranscoder;
1905
2027
  /**
1906
2028
  * Send H.264 frame via RTP media track
1907
2029
  * Returns the number of RTP packets sent
@@ -1960,6 +2082,12 @@ export declare interface BaichuanWebRTCServerOptions {
1960
2082
  iceAdditionalHostAddresses?: string[];
1961
2083
  /** Force relay-only (TURN) if needed */
1962
2084
  iceTransportPolicy?: "all" | "relay";
2085
+ /**
2086
+ * Path to ffmpeg used for AAC → Opus audio transcoding. Defaults to
2087
+ * "ffmpeg" on PATH. Pass an empty string to disable audio (the audio track
2088
+ * will still appear in the SDP for SDP-stability, but stay silent).
2089
+ */
2090
+ ffmpegPath?: string;
1963
2091
  /** Logger callback */
1964
2092
  logger?: (level: "debug" | "info" | "warn" | "error", message: string) => void;
1965
2093
  }
@@ -2095,6 +2223,8 @@ export declare const BC_CMD_ID_GET_AUDIO_TASK = 232;
2095
2223
 
2096
2224
  export declare const BC_CMD_ID_GET_AUTO_FOCUS = 224;
2097
2225
 
2226
+ export declare const BC_CMD_ID_GET_AUTO_REBOOT = 101;
2227
+
2098
2228
  export declare const BC_CMD_ID_GET_BATTERY_INFO = 253;
2099
2229
 
2100
2230
  export declare const BC_CMD_ID_GET_BATTERY_INFO_LIST = 252;
@@ -2109,6 +2239,10 @@ export declare const BC_CMD_ID_GET_DING_DONG_LIST = 484;
2109
2239
 
2110
2240
  export declare const BC_CMD_ID_GET_DING_DONG_SILENT = 609;
2111
2241
 
2242
+ export declare const BC_CMD_ID_GET_DST = 106;
2243
+
2244
+ export declare const BC_CMD_ID_GET_EMAIL = 42;
2245
+
2112
2246
  export declare const BC_CMD_ID_GET_EMAIL_TASK = 217;
2113
2247
 
2114
2248
  export declare const BC_CMD_ID_GET_ENC = 56;
@@ -2123,6 +2257,8 @@ export declare const BC_CMD_ID_GET_LED_STATE = 208;
2123
2257
 
2124
2258
  export declare const BC_CMD_ID_GET_MOTION_ALARM = 46;
2125
2259
 
2260
+ export declare const BC_CMD_ID_GET_NTP = 38;
2261
+
2126
2262
  export declare const BC_CMD_ID_GET_ONLINE_USER_LIST = 120;
2127
2263
 
2128
2264
  export declare const BC_CMD_ID_GET_OSD_DATETIME = 44;
@@ -2155,6 +2291,8 @@ export declare const BC_CMD_ID_GET_SYSTEM_GENERAL = 104;
2155
2291
 
2156
2292
  export declare const BC_CMD_ID_GET_TIMELAPSE_CFG = 319;
2157
2293
 
2294
+ export declare const BC_CMD_ID_GET_VERSION_INFO = 80;
2295
+
2158
2296
  export declare const BC_CMD_ID_GET_VIDEO_INPUT = 26;
2159
2297
 
2160
2298
  export declare const BC_CMD_ID_GET_WHITE_LED = 289;
@@ -2208,12 +2346,18 @@ export declare const BC_CMD_ID_SET_AUDIO_TASK = 231;
2208
2346
 
2209
2347
  export declare const BC_CMD_ID_SET_AUTO_FOCUS = 225;
2210
2348
 
2349
+ export declare const BC_CMD_ID_SET_AUTO_REBOOT = 100;
2350
+
2211
2351
  export declare const BC_CMD_ID_SET_DAY_NIGHT_THRESHOLD = 297;
2212
2352
 
2213
2353
  export declare const BC_CMD_ID_SET_DING_DONG_CFG = 487;
2214
2354
 
2215
2355
  export declare const BC_CMD_ID_SET_DING_DONG_SILENT = 610;
2216
2356
 
2357
+ export declare const BC_CMD_ID_SET_DST = 107;
2358
+
2359
+ export declare const BC_CMD_ID_SET_EMAIL = 43;
2360
+
2217
2361
  export declare const BC_CMD_ID_SET_EMAIL_TASK = 216;
2218
2362
 
2219
2363
  export declare const BC_CMD_ID_SET_ENC = 57;
@@ -2222,6 +2366,10 @@ export declare const BC_CMD_ID_SET_LED_STATE = 209;
2222
2366
 
2223
2367
  export declare const BC_CMD_ID_SET_MOTION_ALARM = 47;
2224
2368
 
2369
+ export declare const BC_CMD_ID_SET_NTP = 39;
2370
+
2371
+ export declare const BC_CMD_ID_SET_OSD_DATETIME = 45;
2372
+
2225
2373
  export declare const BC_CMD_ID_SET_PIR_INFO = 213;
2226
2374
 
2227
2375
  export declare const BC_CMD_ID_SET_PRIVACY_MASK = 53;
@@ -2232,6 +2380,8 @@ export declare const BC_CMD_ID_SET_RECORD = 82;
2232
2380
 
2233
2381
  export declare const BC_CMD_ID_SET_RECORD_CFG = 55;
2234
2382
 
2383
+ export declare const BC_CMD_ID_SET_SYSTEM_GENERAL = 105;
2384
+
2235
2385
  export declare const BC_CMD_ID_SET_VIDEO_INPUT = 25;
2236
2386
 
2237
2387
  export declare const BC_CMD_ID_SET_WHITE_LED_STATE = 288;
@@ -2250,6 +2400,8 @@ export declare const BC_CMD_ID_TALK_CONFIG = 201;
2250
2400
 
2251
2401
  export declare const BC_CMD_ID_TALK_RESET = 11;
2252
2402
 
2403
+ export declare const BC_CMD_ID_TEST_EMAIL = 141;
2404
+
2253
2405
  export declare const BC_CMD_ID_UDP_KEEP_ALIVE = 234;
2254
2406
 
2255
2407
  /**
@@ -2373,7 +2525,10 @@ export declare class BcMediaCodec {
2373
2525
  private strict;
2374
2526
  private amountSkipped;
2375
2527
  private logger;
2528
+ private onUnknownChunk;
2376
2529
  constructor(strict?: boolean, logger?: Logger);
2530
+ /** Register a listener that fires for every unknown chunk before recovery. */
2531
+ setUnknownChunkListener(listener: UnknownChunkListener | undefined): void;
2377
2532
  /**
2378
2533
  * Push data into the codec buffer and try to parse complete BcMedia packets.
2379
2534
  * Returns an array of complete BcMedia packets found.
@@ -3964,6 +4119,9 @@ export declare interface DayNightThresholdConfig {
3964
4119
  };
3965
4120
  }
3966
4121
 
4122
+ /** Day-of-week labels accepted by Dst and AutoReboot blocks. */
4123
+ export declare type DayOfWeek = "Sunday" | "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday" | "everyday";
4124
+
3967
4125
  export declare type DebugConfig = {
3968
4126
  general: boolean;
3969
4127
  debugRtsp: boolean;
@@ -4046,6 +4204,23 @@ export declare function decodeHeader(buf: AnyBuffer): {
4046
4204
  messageKey: number;
4047
4205
  };
4048
4206
 
4207
+ /**
4208
+ * Decode the base64 `valueTable` from a `<scope>` (or `<area>`) into a flat
4209
+ * boolean grid. Bytes are packed MSB-first: bit 7 of byte 0 is cell (0,0).
4210
+ *
4211
+ * The camera ships TWO pairs of dimensions: `<scope><columns>×<rows>`
4212
+ * (bitmap size — typically 96×64) and `<width>×<height>` in the parent
4213
+ * block (the effective motion grid — typically smaller, e.g. 60×33 on
4214
+ * E1 Zoom). The user-editable region matches `width × height`, not the
4215
+ * full bitmap; bits past that are camera-side padding that stays 0.
4216
+ *
4217
+ * When `width`/`height` are omitted we treat the whole bitmap as the
4218
+ * active region (back-compat).
4219
+ *
4220
+ * Throws if the base64 contains too few bytes for `columns*rows` bits.
4221
+ */
4222
+ export declare function decodeMotionScopeBitmap(valueTable: string, columns: number, rows: number, width?: number, height?: number): MotionZoneScope;
4223
+
4049
4224
  /**
4050
4225
  * AES key derivation used by Reolink:
4051
4226
  * keyString = md5_str_modern(`${nonce}-${password}`).slice(0,16)
@@ -4351,6 +4526,30 @@ export declare interface DownloadRecordingParams {
4351
4526
  timeoutMs?: number;
4352
4527
  }
4353
4528
 
4529
+ /** Daylight Saving Time configuration (cmdId=106/107). */
4530
+ export declare interface DstConfig {
4531
+ enable: 0 | 1;
4532
+ /** Offset in hours (typically 1). */
4533
+ offset: number;
4534
+ startMonth: number;
4535
+ /** Week-of-month index (1..5). 5 = "last week of the month". */
4536
+ startWeekIndex: number;
4537
+ startWeekday: DayOfWeek;
4538
+ startHour: number;
4539
+ startMinute: number;
4540
+ startSecond: number;
4541
+ endMonth: number;
4542
+ endWeekIndex: number;
4543
+ endWeekday: DayOfWeek;
4544
+ endHour: number;
4545
+ endMinute: number;
4546
+ endSecond: number;
4547
+ /** Schema version (returned by camera, defaults to 0). */
4548
+ version?: number;
4549
+ }
4550
+
4551
+ export declare type DstConfigPatch = Partial<DstConfig>;
4552
+
4354
4553
  export declare const DUAL_LENS_DUAL_MOTION_MODELS: Set<string>;
4355
4554
 
4356
4555
  export declare const DUAL_LENS_MODELS: Set<string>;
@@ -4425,19 +4624,73 @@ export declare interface DualLensChannelInfo {
4425
4624
  };
4426
4625
  }
4427
4626
 
4627
+ /** Image vs video attachment for motion alert emails. */
4628
+ export declare type EmailAttachmentType = "picture" | "video" | "none";
4629
+
4428
4630
  /**
4429
- * Email task configuration.
4430
- */
4631
+ * Email SMTP server configuration. Returned by GetEmail (cmdId=42) and
4632
+ * accepted by SetEmail (cmdId=43) and TestEmail (cmdId=141).
4633
+ *
4634
+ * Read-only fields (only present on GET): `senderMaxLen`, `pwdMaxLen`,
4635
+ * `emailAttachAbility` — the camera-reported capability bitmap.
4636
+ */
4637
+ export declare interface EmailConfig {
4638
+ smtpServer: string;
4639
+ /** Sender email address / SMTP username. Empty when unconfigured. */
4640
+ userName: string;
4641
+ /** SMTP password. Always sent in cleartext — camera limitation. */
4642
+ password: string;
4643
+ /** Recipient 1. */
4644
+ address1: string;
4645
+ /** Recipient 2 (optional). */
4646
+ address2: string;
4647
+ /** Recipient 3 (optional). */
4648
+ address3: string;
4649
+ smtpPort: number;
4650
+ sendNickname: string;
4651
+ /** 1 = attach picture/video, 0 = text-only. */
4652
+ attachment: 0 | 1;
4653
+ attachmentType: EmailAttachmentType;
4654
+ textType: EmailTextType;
4655
+ /** 1 = SSL/TLS, 0 = plain SMTP. */
4656
+ ssl: 0 | 1;
4657
+ /** Throttle between successive motion mails in seconds. Ignored on battery cams. */
4658
+ interval: number;
4659
+ senderMaxLen?: number;
4660
+ pwdMaxLen?: number;
4661
+ /** Bitmap of supported attachment combinations. */
4662
+ emailAttachAbility?: number;
4663
+ }
4664
+
4665
+ /** Patch payload accepted by SetEmail. All fields optional — only supplied ones are written. */
4666
+ export declare type EmailConfigPatch = Partial<Omit<EmailConfig, "senderMaxLen" | "pwdMaxLen" | "emailAttachAbility">>;
4667
+
4668
+ /** Email schedule configuration (cmdId=216/217). */
4431
4669
  export declare interface EmailTaskConfig {
4432
- body?: {
4433
- EmailTask?: {
4434
- channelId?: number | undefined;
4435
- enable?: number | undefined;
4436
- [key: string]: unknown;
4437
- };
4438
- };
4670
+ channelId: number;
4671
+ enable: 0 | 1;
4672
+ typeScheduleList: EmailTaskScheduleItem[];
4439
4673
  }
4440
4674
 
4675
+ /**
4676
+ * Single entry in the EmailTask `typeScheduleList`. `valueTable` is a
4677
+ * 168-char string of '0'/'1' representing the 7-days × 24-hours grid.
4678
+ */
4679
+ export declare interface EmailTaskScheduleItem {
4680
+ /**
4681
+ * Trigger type. Known: "MD" (motion), "Normal" (continuous record),
4682
+ * "people", "vehicle", "dog_cat", "face", "package", "cry", "visitor",
4683
+ * "doorbell", "none" (placeholder slot).
4684
+ * Treated as string to cover firmware-specific extensions.
4685
+ */
4686
+ type: string;
4687
+ /** 168-char 0/1 schedule bitmap. */
4688
+ valueTable: string;
4689
+ }
4690
+
4691
+ /** Whether the alert body carries the standard human-readable text. */
4692
+ export declare type EmailTextType = "withText" | "noText";
4693
+
4441
4694
  /**
4442
4695
  * Encoding configuration (getEnc response).
4443
4696
  * cmdId=56 (GetEnc) — payload is wrapped in `Compression`, not `Enc`.
@@ -4463,6 +4716,49 @@ export declare function encodeHeader(h: Omit<BaichuanHeader, "magic"> & {
4463
4716
  magic?: AnyBuffer;
4464
4717
  }): AnyBuffer;
4465
4718
 
4719
+ /**
4720
+ * Encode a `width × height` boolean grid back into the camera's
4721
+ * `columns × rows` `valueTable`. Bits outside the active region stay 0,
4722
+ * matching what the camera ships on the way down.
4723
+ *
4724
+ * `scope.cells.length` must equal `scope.width * scope.height`.
4725
+ */
4726
+ export declare function encodeMotionScopeBitmap(scope: MotionZoneScope): string;
4727
+
4728
+ /**
4729
+ * Reply from `getEncOptions` — the set of allowable values for `setEnc`,
4730
+ * derived from `getStreamInfoList`. Use this to validate user input or
4731
+ * populate UI selectors.
4732
+ */
4733
+ export declare interface EncOptions {
4734
+ channel: number;
4735
+ mainStream?: EncStreamOptions;
4736
+ subStream?: EncStreamOptions;
4737
+ thirdStream?: EncStreamOptions;
4738
+ }
4739
+
4740
+ /**
4741
+ * One allowable resolution from a `getEncOptions` reply.
4742
+ * `videoEncTypeList` enumerates the codecs supported at this resolution
4743
+ * (mapped to `"h264"`/`"h265"`).
4744
+ */
4745
+ export declare interface EncResolutionOption {
4746
+ width: number;
4747
+ height: number;
4748
+ /** Codecs available at this resolution. */
4749
+ videoEncTypes: Array<"h264" | "h265">;
4750
+ /** Camera-default framerate at this resolution, if reported. */
4751
+ defaultFramerate?: number;
4752
+ /** Camera-default bitrate (kbps) at this resolution, if reported. */
4753
+ defaultBitrate?: number;
4754
+ /** Camera-default GOP at this resolution, if reported. */
4755
+ defaultGop?: number;
4756
+ /** Allowed framerate values. */
4757
+ framerateOptions: number[];
4758
+ /** Allowed bitrate values (kbps). */
4759
+ bitrateOptions: number[];
4760
+ }
4761
+
4466
4762
  export declare type EncryptionProtocol = {
4467
4763
  kind: "none";
4468
4764
  } | {
@@ -4475,6 +4771,40 @@ export declare type EncryptionProtocol = {
4475
4771
  key: Buffer;
4476
4772
  };
4477
4773
 
4774
+ /**
4775
+ * Allowable values for a single stream profile (mainStream / subStream / thirdStream).
4776
+ * Aggregated from `getStreamInfoList` (cmd_146) so consumers can populate UI
4777
+ * pickers without re-implementing the parsing logic.
4778
+ */
4779
+ export declare interface EncStreamOptions {
4780
+ /** Stream profile (one of `mainStream` / `subStream` / `thirdStream`). */
4781
+ type: string;
4782
+ /** Each entry is a `{width, height}` paired with its allowed values. */
4783
+ resolutions: EncResolutionOption[];
4784
+ /** Encoder rate-control modes Reolink exposes in the app. */
4785
+ encoderTypes: Array<"vbr" | "cbr">;
4786
+ /** Encoder profiles Reolink exposes in the app. */
4787
+ encoderProfiles: Array<"high" | "main" | "baseline">;
4788
+ }
4789
+
4790
+ /**
4791
+ * Patch payload accepted by `setEnc` for a single stream block
4792
+ * (`mainStream` / `subStream` / `thirdStream`). All fields optional —
4793
+ * unspecified ones are preserved from the device's current config.
4794
+ */
4795
+ export declare interface EncStreamPatch {
4796
+ audio?: 0 | 1;
4797
+ width?: number;
4798
+ height?: number;
4799
+ bitRate?: number;
4800
+ frameRate?: number;
4801
+ videoEncType?: "h264" | "h265";
4802
+ encoderType?: "vbr" | "cbr";
4803
+ encoderProfile?: "high" | "main" | "baseline";
4804
+ /** Keyframe interval in seconds — patches `<gop><cur>`. */
4805
+ gop?: number;
4806
+ }
4807
+
4478
4808
  /**
4479
4809
  * Prepend the XML declaration if the body doesn't already start with
4480
4810
  * one. Reolink rejects payloads without it on most setX commands.
@@ -4557,7 +4887,7 @@ export declare interface FloodlightTaskConfig {
4557
4887
  * Parse FloodlightTask XML to extract floodlight-on-motion state.
4558
4888
  * Returns the alarmMode (1 = floodlight turns on when motion detected, 0 = off)
4559
4889
  */
4560
- declare interface FloodlightTaskState {
4890
+ export declare interface FloodlightTaskState {
4561
4891
  /** Whether floodlight turns on when motion is detected (alarmMode) */
4562
4892
  floodlightOnMotion: boolean;
4563
4893
  /** Whether the task is enabled */
@@ -4585,6 +4915,13 @@ export declare interface FtpTaskConfig {
4585
4915
  };
4586
4916
  }
4587
4917
 
4918
+ /**
4919
+ * Convenience: build an "everything enabled" grid of the given dimensions.
4920
+ * Useful when the camera response has no `<valueTable>` and we want to
4921
+ * start the user off with a clean slate.
4922
+ */
4923
+ export declare function fullCoverageScope(columns: number, rows: number, width?: number, height?: number): MotionZoneScope;
4924
+
4588
4925
  /**
4589
4926
  * Get constructed video stream options for all available profiles.
4590
4927
  *
@@ -4733,6 +5070,9 @@ export declare class Go2rtcTcpServer extends EventEmitter<{
4733
5070
  private resolvedPort;
4734
5071
  private nativeFanout;
4735
5072
  private nativeStreamActive;
5073
+ private nativeStreamStopping;
5074
+ private nativeStreamRetryTimer;
5075
+ private nativeStreamRetryDelayMs;
4736
5076
  private dedicatedSessionRelease;
4737
5077
  private detectedVideoType;
4738
5078
  private connectedClients;
@@ -4794,6 +5134,21 @@ export declare class Go2rtcTcpServer extends EventEmitter<{
4794
5134
  * Returns null when the buffer is not a valid ADTS frame.
4795
5135
  */
4796
5136
  private static parseAdtsSamplingInfo;
5137
+ /**
5138
+ * Schedule another startNativeStream() attempt after the given delay.
5139
+ * Idempotent: a no-op if a retry is already scheduled, the server is no
5140
+ * longer active, or an explicit stop is in progress. Implements unbounded
5141
+ * exponential backoff (5s → 60s) so a camera that stays unreachable for
5142
+ * minutes (e.g. nightly maintenance reboot) eventually recovers without
5143
+ * manual intervention — see issue #16.
5144
+ */
5145
+ private scheduleNativeStreamRetry;
5146
+ /**
5147
+ * Cancel any pending retry timer and reset the backoff. Called on explicit
5148
+ * stop and on first-frame-received so the next failure starts the backoff
5149
+ * window from scratch.
5150
+ */
5151
+ private clearNativeStreamRetry;
4797
5152
  private startNativeStream;
4798
5153
  private stopNativeStream;
4799
5154
  private startStreamHealthMonitor;
@@ -5463,6 +5818,37 @@ export declare interface MotionEvent {
5463
5818
  source?: "md" | "pir" | "unknown";
5464
5819
  }
5465
5820
 
5821
+ /**
5822
+ * Motion-detection zone grid helpers.
5823
+ *
5824
+ * Reolink's GetMdAlarm response (cmd_id=46) carries the active detection
5825
+ * region as a base64-encoded bitmap inside `<scope><valueTable>...</valueTable></scope>`.
5826
+ * The bitmap has one bit per grid cell:
5827
+ *
5828
+ * <scope>
5829
+ * <columns>96</columns>
5830
+ * <rows>64</rows>
5831
+ * <valueTable>{base64 of columns*rows bits, packed MSB-first per byte}</valueTable>
5832
+ * </scope>
5833
+ *
5834
+ * The same shape is reused by AI detection (`<AiDetectCfg><area>...</area>`)
5835
+ * — only the column/row counts differ across firmwares. Use the helpers
5836
+ * here to round-trip between the camera's base64 string and a flat boolean
5837
+ * grid the UI can render and edit.
5838
+ */
5839
+ export declare interface MotionZoneScope {
5840
+ /** Active region width (effective grid columns, `<width>` in MD). */
5841
+ width: number;
5842
+ /** Active region height (effective grid rows, `<height>` in MD). */
5843
+ height: number;
5844
+ /** Bitmap columns reported by `<scope><columns>`. */
5845
+ columns: number;
5846
+ /** Bitmap rows reported by `<scope><rows>`. */
5847
+ rows: number;
5848
+ /** Flat `width × height` array, row-major. `true` = cell included. */
5849
+ cells: boolean[];
5850
+ }
5851
+
5466
5852
  /**
5467
5853
  * Stateful MPEG-TS muxer. Each instance has its own continuity counters —
5468
5854
  * create one per output stream (or per client connection for prebuffer replay).
@@ -5561,6 +5947,20 @@ export declare function normalizeOpenClose(input: string): string;
5561
5947
  */
5562
5948
  export declare function normalizeUid(uid?: string): string | undefined;
5563
5949
 
5950
+ /** NTP server configuration (cmdId=38/39). */
5951
+ export declare interface NtpConfig {
5952
+ /** 1 = NTP sync enabled, 0 = disabled. */
5953
+ enable: 0 | 1;
5954
+ /** Hostname or IP of the NTP server. */
5955
+ server: string;
5956
+ /** Sync interval in minutes. */
5957
+ synchronizeInterval: number;
5958
+ /** UDP port. Default 123. */
5959
+ port: number;
5960
+ }
5961
+
5962
+ export declare type NtpConfigPatch = Partial<NtpConfig>;
5963
+
5564
5964
  /**
5565
5965
  * Exact type values that indicate NVR/Hub devices.
5566
5966
  * These are checked with exact case-insensitive match.
@@ -5628,6 +6028,9 @@ export declare interface OsdConfig {
5628
6028
  bgcolor?: number;
5629
6029
  }
5630
6030
 
6031
+ /** OSD date format. */
6032
+ export declare type OsdDateFormat = "DMY" | "MDY" | "YMD";
6033
+
5631
6034
  export declare interface OsdTime {
5632
6035
  enable: number;
5633
6036
  pos: string;
@@ -5996,6 +6399,12 @@ export declare class ReolinkBaichuanApi {
5996
6399
  private sessionGuardIntervalTimer;
5997
6400
  private readonly simpleEventListeners;
5998
6401
  private simpleEventSubscribed;
6402
+ private readonly detectionEventListeners;
6403
+ private readonly detectionEventStreamHooks;
6404
+ private readonly objectDetectionListeners;
6405
+ private objectDetectionStream;
6406
+ private objectDetectionStreamStartInFlight;
6407
+ private objectDetectionInternalListener;
5999
6408
  private simpleEventSubscribeInFlight;
6000
6409
  private simpleEventUnsubscribeInFlight;
6001
6410
  private simpleEventResubscribeTimer;
@@ -6413,6 +6822,73 @@ export declare class ReolinkBaichuanApi {
6413
6822
  * When the last listener is removed, the API unsubscribes from Baichuan events.
6414
6823
  */
6415
6824
  offSimpleEvent(callback?: (event: ReolinkSimpleEvent) => void | Promise<void>): Promise<void>;
6825
+ /**
6826
+ * Subscribe to per-frame detection events sourced from the BcMedia
6827
+ * `additionalHeader` block on active video streams.
6828
+ *
6829
+ * Mirrors {@link onSimpleEvent} but is fed by the streaming side-channel:
6830
+ * one event fires for every I-frame / P-frame that carries an overlay block.
6831
+ * Coordinates are reported in normalized [0, 1] fractions of the source
6832
+ * frame, so the same box renders correctly on mainStream, subStream, and
6833
+ * externStream.
6834
+ *
6835
+ * Unlike `onSimpleEvent`, no Baichuan subscribe command is involved — events
6836
+ * only flow while a video stream is open. The library hooks every
6837
+ * `BaichuanVideoStream` created via this API for the listener's lifetime.
6838
+ */
6839
+ onDetection(callback: (event: ReolinkDetectionEvent) => void | Promise<void>): void;
6840
+ /**
6841
+ * Remove a single detection callback, or all of them if `callback` is omitted.
6842
+ */
6843
+ offDetection(callback?: (event: ReolinkDetectionEvent) => void | Promise<void>): void;
6844
+ /**
6845
+ * Subscribe to AI object detections (people / vehicle / animal / face boxes
6846
+ * with class label and confidence) without managing a video stream yourself.
6847
+ *
6848
+ * Mirrors {@link onSimpleEvent} end-to-end: the API opens a dedicated
6849
+ * substream behind the scenes on the first listener, forwards every box-bearing
6850
+ * `additionalHeader` to your callback, and tears the stream down when the last
6851
+ * listener unsubscribes. The substream is the lightest profile (typically
6852
+ * 640×360) so the additional bandwidth/CPU overhead is minimal.
6853
+ *
6854
+ * Each event carries normalized `[0, 1]` box coordinates, a class label, and
6855
+ * a confidence score — render-ready without further conversion.
6856
+ */
6857
+ onObjectDetections(callback: (event: ReolinkDetectionEvent) => void | Promise<void>): Promise<void>;
6858
+ /**
6859
+ * Remove one detection callback, or all of them if `callback` is omitted.
6860
+ * When the last listener is removed the auto-managed substream is closed.
6861
+ */
6862
+ offObjectDetections(callback?: (event: ReolinkDetectionEvent) => void | Promise<void>): Promise<void>;
6863
+ private ensureObjectDetectionStream;
6864
+ private tearDownObjectDetectionStream;
6865
+ /**
6866
+ * Internal: invoked by BaichuanVideoStream when it starts so the API can hook
6867
+ * its `additionalHeader` event. Returns a teardown function the stream calls
6868
+ * on stop. Not intended for direct use by consumers.
6869
+ */
6870
+ _registerVideoStreamForDetection(stream: {
6871
+ on: (event: "additionalHeader", listener: (info: {
6872
+ raw: Buffer;
6873
+ frameType: "Iframe" | "Pframe";
6874
+ videoType: "H264" | "H265";
6875
+ microseconds: number;
6876
+ frameWidth?: number;
6877
+ frameHeight?: number;
6878
+ }) => void) => void;
6879
+ off: (event: "additionalHeader", listener: (info: {
6880
+ raw: Buffer;
6881
+ frameType: "Iframe" | "Pframe";
6882
+ videoType: "H264" | "H265";
6883
+ microseconds: number;
6884
+ frameWidth?: number;
6885
+ frameHeight?: number;
6886
+ }) => void) => void;
6887
+ }, context: {
6888
+ channel: number;
6889
+ profile: "main" | "sub" | "ext";
6890
+ }): () => void;
6891
+ private dispatchDetectionEvent;
6416
6892
  private startSimpleEventResubscribeTimer;
6417
6893
  private stopSimpleEventResubscribeTimer;
6418
6894
  /**
@@ -6568,6 +7044,55 @@ export declare class ReolinkBaichuanApi {
6568
7044
  port: "rtsp" | "rtmp" | "onvif" | "http" | "https";
6569
7045
  enable: boolean;
6570
7046
  }): Promise<void>;
7047
+ /**
7048
+ * Full port-config setter (cmd_id 36). Patches one or more of the six
7049
+ * service ports the camera serves — Server (Baichuan), HTTP, HTTPS,
7050
+ * RTSP, RTMP, ONVIF. Each entry takes an optional `port` (number) and
7051
+ * `enable` (boolean); fields the caller doesn't pass are left alone.
7052
+ *
7053
+ * Sends one block per port that has any field set, then issues a
7054
+ * single cmd_36 with the merged body. The camera accepts multiple
7055
+ * `<XxxPort>` siblings in the same payload.
7056
+ *
7057
+ * Wire format observed on E1 Zoom:
7058
+ *
7059
+ * <body>
7060
+ * <RtspPort version="1.1">
7061
+ * <rtspPort>554</rtspPort>
7062
+ * <enable>1</enable>
7063
+ * </RtspPort>
7064
+ * <HttpsPort version="1.1">
7065
+ * <enable>0</enable>
7066
+ * </HttpsPort>
7067
+ * ...
7068
+ * </body>
7069
+ */
7070
+ setPortConfig(patch: {
7071
+ server?: {
7072
+ port?: number;
7073
+ enable?: boolean;
7074
+ };
7075
+ http?: {
7076
+ port?: number;
7077
+ enable?: boolean;
7078
+ };
7079
+ https?: {
7080
+ port?: number;
7081
+ enable?: boolean;
7082
+ };
7083
+ rtsp?: {
7084
+ port?: number;
7085
+ enable?: boolean;
7086
+ };
7087
+ rtmp?: {
7088
+ port?: number;
7089
+ enable?: boolean;
7090
+ };
7091
+ onvif?: {
7092
+ port?: number;
7093
+ enable?: boolean;
7094
+ };
7095
+ }): Promise<void>;
6571
7096
  /** GetDevInfo via Baichuan: host cmd_id 80, channel cmd_id 318 */
6572
7097
  getInfo(channel?: number, options?: {
6573
7098
  timeoutMs?: number;
@@ -7358,6 +7883,15 @@ export declare class ReolinkBaichuanApi {
7358
7883
  */
7359
7884
  setPtzPreset(presetId: number, name: string, channel?: number): Promise<void>;
7360
7885
  setPtzPreset(channel: number, presetId: number, name: string): Promise<void>;
7886
+ /**
7887
+ * Recall (move to) a saved PTZ preset.
7888
+ *
7889
+ * cmd_id 19 (PTZ_CONTROL_PRESET) with command="toPos". The camera moves
7890
+ * the head at its own default preset-recall speed; we don't expose
7891
+ * speed here because most firmwares ignore the field on toPos.
7892
+ */
7893
+ gotoPtzPreset(presetId: number, channel?: number): Promise<void>;
7894
+ gotoPtzPreset(channel: number, presetId: number): Promise<void>;
7361
7895
  /**
7362
7896
  * Best-effort delete/disable a PTZ preset.
7363
7897
  *
@@ -7532,6 +8066,39 @@ export declare class ReolinkBaichuanApi {
7532
8066
  */
7533
8067
  setMotionDetection(enabled: boolean, sensitivity?: number, channel?: number): Promise<void>;
7534
8068
  setMotionDetection(channel: number, enabled: boolean, sensitivity?: number): Promise<void>;
8069
+ /**
8070
+ * Set motion alarm with full control, including the detection-zone grid.
8071
+ *
8072
+ * Wire format observed on E1 Zoom (cmd_id=47 SetMdAlarm body):
8073
+ *
8074
+ * <MD version="1.1">
8075
+ * <channelId>0</channelId>
8076
+ * <enable>1</enable>
8077
+ * <usepir>0</usepir>
8078
+ * <width>60</width> <height>33</height>
8079
+ * <scope>
8080
+ * <columns>96</columns> <rows>64</rows>
8081
+ * <valueTable>{base64 6144-bit bitmap}</valueTable>
8082
+ * </scope>
8083
+ * ... other camera-specific fields ...
8084
+ * </MD>
8085
+ *
8086
+ * We do a read-modify-write of the GET response so any camera-specific
8087
+ * extension fields are preserved untouched. Pass `valueTable` to update
8088
+ * the detection zone — see `encodeMotionScopeBitmap` for the bitmap layout.
8089
+ *
8090
+ * @param channel - 0-based channel
8091
+ * @param enabled - toggle motion detection on/off (optional)
8092
+ * @param sensitivity - 0-50, higher = more sensitive (optional)
8093
+ * @param valueTable - base64-encoded grid bitmap; size must match
8094
+ * `<scope><columns>×<rows></scope>` from the GET (optional)
8095
+ */
8096
+ setMotionAlarmFull(opts: {
8097
+ channel?: number;
8098
+ enabled?: boolean;
8099
+ sensitivity?: number;
8100
+ valueTable?: string;
8101
+ }): Promise<void>;
7535
8102
  /**
7536
8103
  * Set AI detection settings via Baichuan.
7537
8104
  * cmd_id: 343 (SetAiAlarm)
@@ -7822,25 +8389,30 @@ export declare class ReolinkBaichuanApi {
7822
8389
  }): Promise<EncConfig>;
7823
8390
  /**
7824
8391
  * SetEnc via Baichuan (cmdId=57). Read-modify-write — preserves
7825
- * unspecified fields. Mirrors reolink_aio's `SetEnc`.
8392
+ * unspecified fields. Mirrors reolink_aio's `SetEnc` plus the additional
8393
+ * `width`/`height`/`encoderType`/`encoderProfile`/`gop`/`thirdStream`
8394
+ * fields observed in the official mobile app (see `pcap/resolution.pcapng`).
8395
+ *
8396
+ * Field meaning per stream:
8397
+ * - `audio` — 0/1 toggle
8398
+ * - `width`/`height` — resolution in pixels. Must be one of the
8399
+ * resolutions returned by {@link getStreamInfoList}.
8400
+ * - `bitRate` — kbps. Must match the table from `getStreamInfoList`.
8401
+ * - `frameRate` — fps. Must match the table from `getStreamInfoList`.
8402
+ * - `videoEncType` — `"h264"` or `"h265"`
8403
+ * - `encoderType` — `"vbr"` or `"cbr"`
8404
+ * - `encoderProfile` — `"high"`, `"main"`, or `"baseline"`
8405
+ * - `gop` — keyframe interval in seconds (sets `<gop><cur>`)
7826
8406
  *
7827
8407
  * @param channel - Channel number (0-based)
7828
- * @param patch - Fields to update on `mainStream` and/or `subStream`,
7829
- * plus a top-level `audio` toggle (0/1). Pass only what you want
7830
- * to change.
8408
+ * @param patch - Fields to update. Pass only the fields you want to change;
8409
+ * everything else is preserved from the device's current configuration.
7831
8410
  */
7832
8411
  setEnc(channel: number, patch: {
7833
8412
  audio?: 0 | 1;
7834
- mainStream?: {
7835
- bitRate?: number;
7836
- frameRate?: number;
7837
- videoEncType?: "h264" | "h265";
7838
- };
7839
- subStream?: {
7840
- bitRate?: number;
7841
- frameRate?: number;
7842
- videoEncType?: "h264" | "h265";
7843
- };
8413
+ mainStream?: EncStreamPatch;
8414
+ subStream?: EncStreamPatch;
8415
+ thirdStream?: EncStreamPatch;
7844
8416
  }, options?: {
7845
8417
  timeoutMs?: number;
7846
8418
  }): Promise<void>;
@@ -8100,6 +8672,35 @@ export declare class ReolinkBaichuanApi {
8100
8672
  getCoordinatePointListFromPushCache(channel?: number): BaichuanCachedPush<BaichuanCoordinatePointListPush> | undefined;
8101
8673
  private isNvrLikeDevice;
8102
8674
  private sendPcapDerivedSettingsGetXml;
8675
+ /**
8676
+ * Update the OSD timestamp + channel-name overlay via cmd_id=45
8677
+ * (SetOsdDatetime). The schema is the same `<body><OsdDatetime>` +
8678
+ * `<OsdChannelName>` block returned by `getOsdDatetime` — we
8679
+ * read-modify-write so any extension fields the camera sent are
8680
+ * preserved.
8681
+ *
8682
+ * Position is in **camera pixel coordinates** (e.g. (1,1) for top-left,
8683
+ * not preset strings). Set `enable=0` to hide the overlay; the camera
8684
+ * keeps the stored position so re-enabling later restores it.
8685
+ */
8686
+ setOsdDatetime(channel: number, patch: {
8687
+ datetime?: {
8688
+ enable?: boolean | 0 | 1;
8689
+ topLeftX?: number;
8690
+ topLeftY?: number;
8691
+ language?: string;
8692
+ };
8693
+ channelName?: {
8694
+ name?: string;
8695
+ enable?: boolean | 0 | 1;
8696
+ topLeftX?: number;
8697
+ topLeftY?: number;
8698
+ enWatermark?: boolean | 0 | 1;
8699
+ enBgcolor?: boolean | 0 | 1;
8700
+ };
8701
+ }, options?: {
8702
+ timeoutMs?: number;
8703
+ }): Promise<void>;
8103
8704
  getOsdDatetime(channel: number, options?: {
8104
8705
  timeoutMs?: number;
8105
8706
  }): Promise<BaichuanGetOsdDatetimeResult>;
@@ -8118,6 +8719,36 @@ export declare class ReolinkBaichuanApi {
8118
8719
  getStreamInfoList(channel: number, options?: {
8119
8720
  timeoutMs?: number;
8120
8721
  }): Promise<BaichuanStreamInfoList>;
8722
+ /**
8723
+ * Return the set of values `setEnc` will accept on each stream of `channel`.
8724
+ * Aggregates `getStreamInfoList` (cmd_146) into a UI-friendly shape:
8725
+ * per-stream resolutions with their allowed codecs/framerates/bitrates plus
8726
+ * the enumerated encoder modes/profiles Reolink exposes.
8727
+ *
8728
+ * Useful for populating selectors and validating user input before calling
8729
+ * `setEnc` — picking an unsupported combination causes the camera to reject
8730
+ * the SET_ENC command (responseCode != 200).
8731
+ */
8732
+ getEncOptions(channel: number, options?: {
8733
+ timeoutMs?: number;
8734
+ }): Promise<EncOptions>;
8735
+ /**
8736
+ * Read the camera's `<VersionInfo>` block (cmd_id=80). Returns the
8737
+ * friendly name, model code (e.g. `"E1 Zoom"`), serial number, firmware
8738
+ * version, hardware revision, build day, AI model bundle version, etc.
8739
+ *
8740
+ * This is the same info the Reolink mobile app shows in "About this
8741
+ * device" — distinct from `getSystemGeneral` (cmd_104) which carries
8742
+ * time/locale.
8743
+ *
8744
+ * No channel parameter: this command is device-global on NVRs/Hubs and
8745
+ * camera-global on standalone cameras. Pass an explicit channel via the
8746
+ * underlying `sendXml` only if a specific firmware demands it (none we've
8747
+ * tested do).
8748
+ */
8749
+ getVersionInfo(options?: {
8750
+ timeoutMs?: number;
8751
+ }): Promise<BaichuanVersionInfo>;
8121
8752
  getLedState(channel: number, options?: {
8122
8753
  timeoutMs?: number;
8123
8754
  }): Promise<BaichuanLedState>;
@@ -8139,6 +8770,178 @@ export declare class ReolinkBaichuanApi {
8139
8770
  getEmailTask(channel?: number, options?: {
8140
8771
  timeoutMs?: number;
8141
8772
  }): Promise<EmailTaskConfig>;
8773
+ /**
8774
+ * SetEmailTask via Baichuan (cmdId=216). Updates the email alarm schedule
8775
+ * (per-event-type 7×24 valueTable + master enable).
8776
+ *
8777
+ * Reolink expects the FULL `typeScheduleList` — pass the array from a prior
8778
+ * GET and only flip the entries you care about. Slots you don't track must
8779
+ * be sent back unchanged to avoid the camera dropping them.
8780
+ */
8781
+ setEmailTask(channel: number | undefined, task: EmailTaskConfig, options?: {
8782
+ timeoutMs?: number;
8783
+ }): Promise<void>;
8784
+ /**
8785
+ * Convenience wrapper that patches the schedule of one or more trigger
8786
+ * types on the camera's EmailTask without touching the others.
8787
+ *
8788
+ * Pass a high-level schedule spec (`always` / `never` / explicit windows)
8789
+ * and the trigger types it should apply to. The method:
8790
+ *
8791
+ * 1. Reads the current EmailTask via GET (so we keep every existing slot).
8792
+ * 2. Builds the new `valueTable` once from `schedule`.
8793
+ * 3. Replaces the `valueTable` of every matching `type` in the list.
8794
+ * 4. Appends entries for any requested type not already present.
8795
+ * 5. Writes the merged list back via SET.
8796
+ *
8797
+ * Returns the list of types that were actually touched.
8798
+ */
8799
+ patchEmailSchedule(channel: number | undefined, spec: {
8800
+ types: string[];
8801
+ schedule: {
8802
+ kind: "always";
8803
+ } | {
8804
+ kind: "never";
8805
+ } | {
8806
+ kind: "windows";
8807
+ windows: Array<{
8808
+ days: number[];
8809
+ startHour: number;
8810
+ endHour: number;
8811
+ }>;
8812
+ };
8813
+ /** When provided, also flips the EmailTask master `enable` flag. */
8814
+ enable?: 0 | 1;
8815
+ }, options?: {
8816
+ timeoutMs?: number;
8817
+ }): Promise<{
8818
+ touchedTypes: string[];
8819
+ }>;
8820
+ /**
8821
+ * Read the SMTP email configuration (cmdId=42). Returns the full `<Email>`
8822
+ * block including capability hints (`senderMaxLen`, `pwdMaxLen`,
8823
+ * `emailAttachAbility`).
8824
+ */
8825
+ getEmail(options?: {
8826
+ timeoutMs?: number;
8827
+ }): Promise<EmailConfig>;
8828
+ /**
8829
+ * Patch the SMTP email configuration (cmdId=43). Reads the current config
8830
+ * first then merges the patch — Reolink rejects partial `<Email>` blocks.
8831
+ */
8832
+ setEmail(patch: EmailConfigPatch, options?: {
8833
+ timeoutMs?: number;
8834
+ }): Promise<void>;
8835
+ /**
8836
+ * Send a test email using either the current config or an override patch
8837
+ * (cmdId=141). Returns true when the camera reports 200 (test succeeded),
8838
+ * false when it reports 482 (test failed — server unreachable / bad creds).
8839
+ * Other non-200 codes propagate as exceptions via `sendXml`.
8840
+ */
8841
+ testEmail(patch?: EmailConfigPatch, options?: {
8842
+ timeoutMs?: number;
8843
+ }): Promise<boolean>;
8844
+ /**
8845
+ * Read the NTP server configuration (cmdId=38).
8846
+ */
8847
+ getNtp(options?: {
8848
+ timeoutMs?: number;
8849
+ }): Promise<NtpConfig>;
8850
+ /**
8851
+ * Patch the NTP server configuration (cmdId=39). Reads the current state
8852
+ * first and merges the patch — Reolink rejects partial `<Ntp>` blocks.
8853
+ */
8854
+ setNtp(patch: NtpConfigPatch, options?: {
8855
+ timeoutMs?: number;
8856
+ }): Promise<void>;
8857
+ /**
8858
+ * Patch SystemGeneral (cmdId=105). Supports partial payloads: include only
8859
+ * the fields you want to change. By default the builder emits `<year>0</year>`
8860
+ * as the "do not set manual clock" marker; pass `manualTime` to actually
8861
+ * set the date/time. Setting only `deviceName` automatically uses the
8862
+ * Reolink Client's `deviceNameOnly=1` shape.
8863
+ */
8864
+ setSystemGeneral(patch: SystemGeneralPatch, options?: {
8865
+ timeoutMs?: number;
8866
+ }): Promise<void>;
8867
+ /**
8868
+ * Read the Daylight Saving Time configuration (cmdId=106).
8869
+ */
8870
+ getDst(options?: {
8871
+ timeoutMs?: number;
8872
+ }): Promise<DstConfig>;
8873
+ /**
8874
+ * Patch the DST configuration (cmdId=107). Reads the current state first
8875
+ * and merges the patch.
8876
+ */
8877
+ setDst(patch: DstConfigPatch, options?: {
8878
+ timeoutMs?: number;
8879
+ }): Promise<void>;
8880
+ /**
8881
+ * Read the auto-reboot schedule (cmdId=101).
8882
+ */
8883
+ getAutoReboot(options?: {
8884
+ timeoutMs?: number;
8885
+ }): Promise<AutoRebootConfig>;
8886
+ /**
8887
+ * Patch the auto-reboot schedule (cmdId=100).
8888
+ */
8889
+ setAutoReboot(patch: AutoRebootConfigPatch, options?: {
8890
+ timeoutMs?: number;
8891
+ }): Promise<void>;
8892
+ /**
8893
+ * High-level helper that configures the camera to deliver motion alerts via
8894
+ * SMTP to the local nodelink manager. Orchestrates `setEmail` + `setEmailTask`
8895
+ * in a single call so UI code can offer "auto-configure" without juggling
8896
+ * the underlying commands.
8897
+ *
8898
+ * Pass `runTest: true` to also send a test email (cmdId=141). Returns a
8899
+ * structured result describing each leg of the flow so the caller can show
8900
+ * granular feedback.
8901
+ *
8902
+ * @param params Auto-configuration parameters
8903
+ * @param channel Logical channel (default 0). Used for the EmailTask SET.
8904
+ */
8905
+ setupEmailPushToManager(params: {
8906
+ /** Manager hostname or IP reachable from the camera's network. */
8907
+ managerHost: string;
8908
+ /** Manager SMTP port. Default 2525. */
8909
+ managerPort?: number;
8910
+ /** Per-camera recipient local-part — typically `cam-<cameraId>`. */
8911
+ recipientLocalPart: string;
8912
+ /** Virtual mail domain (must match the server-side setting). */
8913
+ domain?: string;
8914
+ /** Attachment kind on motion. Default "picture". */
8915
+ attachmentType?: "picture" | "video" | "none";
8916
+ /** Optional sender nickname shown in the From header. */
8917
+ sendNickname?: string;
8918
+ /** Interval throttle in seconds (ignored on battery cams). Default 30. */
8919
+ interval?: number;
8920
+ /** Optional SMTP auth — required when the server's `requireAuth` is on. */
8921
+ authUsername?: string;
8922
+ authPassword?: string;
8923
+ /**
8924
+ * Trigger types to enable in the email schedule. Must match the types
8925
+ * already present in the current EmailTask (we patch their schedule to
8926
+ * full-week 24/7, leaving any other slot untouched).
8927
+ */
8928
+ triggerTypes?: string[];
8929
+ /** Send a test email after the SET. Default false. */
8930
+ runTest?: boolean;
8931
+ }, channel?: number, options?: {
8932
+ timeoutMs?: number;
8933
+ }): Promise<{
8934
+ setEmail: {
8935
+ applied: true;
8936
+ };
8937
+ setEmailTask: {
8938
+ applied: true;
8939
+ touchedTypes: string[];
8940
+ };
8941
+ testEmail?: {
8942
+ success: boolean;
8943
+ };
8944
+ }>;
8142
8945
  /**
8143
8946
  * Get siren-on-motion state via AudioTask (cmdId=232).
8144
8947
  *
@@ -9353,6 +10156,64 @@ export declare interface ReolinkDayNightNotification {
9353
10156
  timestamp?: number;
9354
10157
  }
9355
10158
 
10159
+ /**
10160
+ * A single detection bounding box in normalized [0, 1] coordinates relative to
10161
+ * the source video frame. Using fractions instead of pixels keeps the same box
10162
+ * valid across mainStream / subStream / externStream output and across firmware
10163
+ * resolution changes.
10164
+ */
10165
+ export declare interface ReolinkDetectionBox {
10166
+ /** Left edge in [0, 1] (0 = left, 1 = right). */
10167
+ x: number;
10168
+ /** Top edge in [0, 1] (0 = top, 1 = bottom). */
10169
+ y: number;
10170
+ /** Width in [0, 1]. */
10171
+ width: number;
10172
+ /** Height in [0, 1]. */
10173
+ height: number;
10174
+ /** AI class label if the camera reports one (e.g. "person", "vehicle"). */
10175
+ label?: string;
10176
+ /** Confidence in [0, 1] if exposed by the camera. */
10177
+ confidence?: number;
10178
+ }
10179
+
10180
+ /**
10181
+ * Diagnostic state describing how much of the BcMedia additionalHeader the
10182
+ * decoder was able to interpret. Useful for consumers iterating on the format.
10183
+ */
10184
+ export declare type ReolinkDetectionDecodeState =
10185
+ /** Header marker was missing or malformed. */
10186
+ "invalid-marker"
10187
+ /** Baseline 128-byte header — camera reports no overlay metadata. */
10188
+ | "no-overlay"
10189
+ /** Overlay block present, but coordinates have not been decoded yet. */
10190
+ | "overlay-undecoded"
10191
+ /** Overlay block decoded successfully. */
10192
+ | "overlay-decoded";
10193
+
10194
+ /**
10195
+ * High-level "detection" event emitted alongside every video frame that carries
10196
+ * a non-empty BcMedia additionalHeader. Mirrors `ReolinkSimpleEvent` but is
10197
+ * sourced from the streaming side-channel rather than from cmd_id 33 push events.
10198
+ */
10199
+ export declare interface ReolinkDetectionEvent {
10200
+ channel: number;
10201
+ /** Microseconds timestamp from the BcMedia video frame. */
10202
+ microseconds: number;
10203
+ /** Stream profile that produced the underlying frame. */
10204
+ profile: "main" | "sub" | "ext";
10205
+ /** Boxes in [0, 1] fractional coordinates. */
10206
+ boxes: ReolinkDetectionBox[];
10207
+ /** Source frame width (from BcMedia InfoV1/V2) if known. */
10208
+ frameWidth?: number;
10209
+ /** Source frame height (from BcMedia InfoV1/V2) if known. */
10210
+ frameHeight?: number;
10211
+ /** Decoder diagnostic state. */
10212
+ decodeState: ReolinkDetectionDecodeState;
10213
+ /** Raw additionalHeader bytes — kept for downstream decoder work. */
10214
+ rawHeader: Buffer;
10215
+ }
10216
+
9356
10217
  export declare interface ReolinkDeviceInfo {
9357
10218
  type?: string;
9358
10219
  hardwareVersion?: string;
@@ -10126,21 +10987,55 @@ export declare interface SupportItem {
10126
10987
  }
10127
10988
 
10128
10989
  /**
10129
- * System general configuration.
10130
- * cmdId=77 (GetSystemGeneral)
10990
+ * Full SystemGeneral block returned by cmdId=104. SET (cmdId=105) accepts a
10991
+ * subset (any combination of these fields) plus the mandatory `<year>0</year>`
10992
+ * marker that signals "do not set the manual clock". When you DO want to set
10993
+ * the manual clock, pass year/month/day/hour/minute/second together.
10131
10994
  */
10132
10995
  export declare interface SystemGeneralConfig {
10133
- body?: {
10134
- SystemGeneral?: {
10135
- timeZone?: number | undefined;
10136
- deviceName?: string | undefined;
10137
- language?: string | undefined;
10138
- dstMode?: number | undefined;
10139
- [key: string]: unknown;
10140
- };
10141
- Norm?: {
10142
- [key: string]: unknown;
10143
- };
10996
+ /** Timezone offset in seconds. POSIX convention: UTC+1 → -3600. */
10997
+ timeZone: number;
10998
+ osdFormat: OsdDateFormat;
10999
+ year: number;
11000
+ month: number;
11001
+ day: number;
11002
+ hour: number;
11003
+ minute: number;
11004
+ second: number;
11005
+ deviceId: number;
11006
+ timeFormat: TimeFormat;
11007
+ language: string;
11008
+ deviceName: string;
11009
+ loginLock: 0 | 1;
11010
+ lockTime: number;
11011
+ allowedTimes: number;
11012
+ /** 1 when DST is currently active (camera-side). Read-only on most firmwares. */
11013
+ isDst: 0 | 1;
11014
+ }
11015
+
11016
+ /**
11017
+ * Patch accepted by SetSystemGeneral. When `manualTime` is provided, the
11018
+ * year/month/day/hour/minute/second fields are sent as-is. When omitted, the
11019
+ * builder injects `<year>0</year>` to skip the manual clock even when other
11020
+ * fields are present. Setting only `deviceName` triggers the
11021
+ * `<deviceNameOnly>1</deviceNameOnly>` flag automatically.
11022
+ */
11023
+ export declare interface SystemGeneralPatch {
11024
+ timeZone?: number;
11025
+ osdFormat?: OsdDateFormat;
11026
+ timeFormat?: TimeFormat;
11027
+ language?: string;
11028
+ deviceName?: string;
11029
+ loginLock?: 0 | 1;
11030
+ lockTime?: number;
11031
+ allowedTimes?: number;
11032
+ manualTime?: {
11033
+ year: number;
11034
+ month: number;
11035
+ day: number;
11036
+ hour: number;
11037
+ minute: number;
11038
+ second: number;
10144
11039
  };
10145
11040
  }
10146
11041
 
@@ -10204,6 +11099,9 @@ export declare function testChannelStreams(params: {
10204
11099
  logger?: Logger;
10205
11100
  }): Promise<Record<string, unknown>>;
10206
11101
 
11102
+ /** 24h (0) or 12h (1) clock format. */
11103
+ export declare type TimeFormat = 0 | 1;
11104
+
10207
11105
  /**
10208
11106
  * Timelapse configuration.
10209
11107
  */
@@ -10224,6 +11122,27 @@ export declare interface TwoWayAudioConfig {
10224
11122
  mode?: "mixAudioStream" | string;
10225
11123
  }
10226
11124
 
11125
+ /**
11126
+ * Optional listener invoked whenever the codec encounters a 4-byte sequence
11127
+ * at the buffer head that does NOT match any known BcMedia magic and is about
11128
+ * to be skipped as "recovery". Use it to discover undocumented sub-packet
11129
+ * shapes (e.g. AI overlay metadata) without altering the codec's behaviour.
11130
+ */
11131
+ export declare type UnknownChunkListener = (info: {
11132
+ magic: number;
11133
+ /** Up to 256 bytes starting at the unknown chunk's first byte. */
11134
+ preview: Buffer;
11135
+ /** Number of bytes the codec is about to skip before resyncing. */
11136
+ skipped: number;
11137
+ }) => void;
11138
+
11139
+ /**
11140
+ * Like {@link applyXmlTagPatch} but inserts the tag at the end of the block
11141
+ * when it is not already present. Required for fields the camera omits on
11142
+ * GET responses but expects on SET (e.g. `<encoderType>` on `setEnc`).
11143
+ */
11144
+ export declare function upsertXmlTag(xml: string, tag: string, value: string | number | boolean | undefined): string;
11145
+
10227
11146
  /**
10228
11147
  * Client information extracted from HTTP request headers.
10229
11148
  * Used to determine optimal video delivery format.
@@ -10470,13 +11389,13 @@ export declare function xmlEscape(text: string | undefined | null): string;
10470
11389
  */
10471
11390
  export declare function xmlIndicatesFloodlight(xml: string): boolean;
10472
11391
 
10473
- declare type XmlJsonObject = {
11392
+ export declare type XmlJsonObject = {
10474
11393
  [key: string]: XmlJsonValue;
10475
11394
  };
10476
11395
 
10477
- declare type XmlJsonPrimitive = string | number | boolean | null;
11396
+ export declare type XmlJsonPrimitive = string | number | boolean | null;
10478
11397
 
10479
- declare type XmlJsonValue = XmlJsonPrimitive | XmlJsonObject | XmlJsonValue[];
11398
+ export declare type XmlJsonValue = XmlJsonPrimitive | XmlJsonObject | XmlJsonValue[];
10480
11399
 
10481
11400
  export declare function zipDirectory(params: {
10482
11401
  sourceDir: string;