@apocaliss92/nodelink-js 0.4.22 → 0.4.23

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.
@@ -30,7 +30,7 @@ import {
30
30
  runAllDiagnosticsConsecutively,
31
31
  runMultifocalDiagnosticsConsecutively,
32
32
  xmlEscape
33
- } from "./chunk-S2UTGNFN.js";
33
+ } from "./chunk-AHY4L7JI.js";
34
34
  import {
35
35
  BC_CLASS_FILE_DOWNLOAD,
36
36
  BC_CLASS_LEGACY,
@@ -94,10 +94,12 @@ import {
94
94
  BC_CMD_ID_GET_REC_ENC_CFG,
95
95
  BC_CMD_ID_GET_SIREN_STATUS,
96
96
  BC_CMD_ID_GET_SLEEP_STATE,
97
+ BC_CMD_ID_GET_SNAPSHOT,
97
98
  BC_CMD_ID_GET_STREAM_INFO_LIST,
98
99
  BC_CMD_ID_GET_SUPPORT,
99
100
  BC_CMD_ID_GET_SYSTEM_GENERAL,
100
101
  BC_CMD_ID_GET_TIMELAPSE_CFG,
102
+ BC_CMD_ID_GET_UID,
101
103
  BC_CMD_ID_GET_VERSION_INFO,
102
104
  BC_CMD_ID_GET_VIDEO_INPUT,
103
105
  BC_CMD_ID_GET_WHITE_LED,
@@ -176,7 +178,7 @@ import {
176
178
  splitAnnexBToNalPayloads2,
177
179
  talkTraceLog,
178
180
  traceLog
179
- } from "./chunk-C57QV7IL.js";
181
+ } from "./chunk-GVWJGQPT.js";
180
182
 
181
183
  // src/protocol/framing.ts
182
184
  function encodeHeader(h) {
@@ -8319,6 +8321,352 @@ function decideSleepInferenceTransition(input) {
8319
8321
  };
8320
8322
  }
8321
8323
 
8324
+ // src/reolink/baichuan/utils/privacyMask.ts
8325
+ var DEFAULT_SHELTER_CANVAS = {
8326
+ width: 540,
8327
+ height: 304
8328
+ };
8329
+ function decodeShelterCoord(value) {
8330
+ const v = (value | 0) >>> 0;
8331
+ return { pixel: v >>> 16, canvas: v & 65535 };
8332
+ }
8333
+ function encodeShelterCoord(pixel, canvas) {
8334
+ const p = Math.min(65535, Math.max(0, Math.floor(pixel)));
8335
+ const c = Math.min(65535, Math.max(0, Math.floor(canvas)));
8336
+ return p * 65536 + c;
8337
+ }
8338
+ function clamp01(value) {
8339
+ if (!Number.isFinite(value)) return 0;
8340
+ if (value < 0) return 0;
8341
+ if (value > 1) return 1;
8342
+ return value;
8343
+ }
8344
+ function decodeRect(raw, canvas, idKey) {
8345
+ const xRaw = Number(raw["topLeftX"] ?? 0) | 0;
8346
+ const yRaw = Number(raw["topLeftY"] ?? 0) | 0;
8347
+ const wRaw = Number(raw["width"] ?? 0) | 0;
8348
+ const hRaw = Number(raw["height"] ?? 0) | 0;
8349
+ const xPixel = decodeShelterCoord(xRaw).pixel;
8350
+ const yPixel = decodeShelterCoord(yRaw).pixel;
8351
+ const wPixel = decodeShelterCoord(wRaw).pixel;
8352
+ const hPixel = decodeShelterCoord(hRaw).pixel;
8353
+ return {
8354
+ id: Number(raw[idKey] ?? 0) | 0,
8355
+ enable: Number(raw["enable"] ?? 0) === 1,
8356
+ layer: Number(raw["layer"] ?? 0) | 0,
8357
+ color: Number(raw["color"] ?? 0) | 0,
8358
+ x: canvas.width > 0 ? xPixel / canvas.width : 0,
8359
+ y: canvas.height > 0 ? yPixel / canvas.height : 0,
8360
+ width: canvas.width > 0 ? wPixel / canvas.width : 0,
8361
+ height: canvas.height > 0 ? hPixel / canvas.height : 0
8362
+ };
8363
+ }
8364
+ function extractCanvasFromShelterXml(xml) {
8365
+ const xMatch = xml.match(/<topLeftX>(\d+)<\/topLeftX>/g) ?? [];
8366
+ const yMatch = xml.match(/<topLeftY>(\d+)<\/topLeftY>/g) ?? [];
8367
+ let canvasX = 0;
8368
+ let canvasY = 0;
8369
+ for (const tag of xMatch) {
8370
+ const v = Number(tag.replace(/<\/?topLeftX>/g, "")) | 0;
8371
+ if (v > 0) {
8372
+ canvasX = v & 65535;
8373
+ break;
8374
+ }
8375
+ }
8376
+ for (const tag of yMatch) {
8377
+ const v = Number(tag.replace(/<\/?topLeftY>/g, "")) | 0;
8378
+ if (v > 0) {
8379
+ canvasY = v & 65535;
8380
+ break;
8381
+ }
8382
+ }
8383
+ return {
8384
+ width: canvasX > 0 ? canvasX : DEFAULT_SHELTER_CANVAS.width,
8385
+ height: canvasY > 0 ? canvasY : DEFAULT_SHELTER_CANVAS.height
8386
+ };
8387
+ }
8388
+ function parseRectTags(block) {
8389
+ const out = {};
8390
+ const grab = (tag) => {
8391
+ const m = block.match(new RegExp(`<${tag}>([^<]*)<\\/${tag}>`));
8392
+ if (m && m[1] !== void 0) out[tag] = m[1];
8393
+ };
8394
+ grab("id");
8395
+ grab("name");
8396
+ grab("enable");
8397
+ grab("layer");
8398
+ grab("color");
8399
+ grab("topLeftX");
8400
+ grab("topLeftY");
8401
+ grab("width");
8402
+ grab("height");
8403
+ grab("pPos");
8404
+ grab("tPos");
8405
+ grab("zPos");
8406
+ return out;
8407
+ }
8408
+ function decodePrivacyMaskZones(xml) {
8409
+ const canvas = extractCanvasFromShelterXml(xml);
8410
+ const enable = Number(getXmlText(xml, "enable") ?? "0") === 1;
8411
+ const maxNum = Number(getXmlText(xml, "maxNum") ?? "0") | 0;
8412
+ const trackEnable = Number(getXmlText(xml, "trackEnable") ?? "0") === 1;
8413
+ const maxTrackShelterNum = Number(getXmlText(xml, "maxTrackShelterNum") ?? "0") | 0;
8414
+ const shelterList = [];
8415
+ const shelterListBlock = xml.match(
8416
+ /<shelterList>([\s\S]*?)<\/shelterList>/
8417
+ )?.[1];
8418
+ if (shelterListBlock) {
8419
+ const blocks = shelterListBlock.matchAll(
8420
+ /<Shelter>([\s\S]*?)<\/Shelter>/g
8421
+ );
8422
+ for (const m of blocks) {
8423
+ const tags = parseRectTags(m[1] ?? "");
8424
+ const decoded = decodeRect(tags, canvas, "id");
8425
+ shelterList.push(decoded);
8426
+ }
8427
+ }
8428
+ const trackShelterList = [];
8429
+ const trackBlock = xml.match(
8430
+ /<trackShelterList>([\s\S]*?)<\/trackShelterList>/
8431
+ )?.[1];
8432
+ if (trackBlock) {
8433
+ const blocks = trackBlock.matchAll(
8434
+ /<trackShelter>([\s\S]*?)<\/trackShelter>/g
8435
+ );
8436
+ for (const m of blocks) {
8437
+ const tags = parseRectTags(m[1] ?? "");
8438
+ const base = decodeRect(
8439
+ tags,
8440
+ canvas,
8441
+ "name"
8442
+ );
8443
+ trackShelterList.push({
8444
+ ...base,
8445
+ pPos: Number(tags.pPos ?? -1) | 0,
8446
+ tPos: Number(tags.tPos ?? -1) | 0,
8447
+ zPos: Number(tags.zPos ?? -1) | 0
8448
+ });
8449
+ }
8450
+ }
8451
+ return {
8452
+ enable,
8453
+ maxNum,
8454
+ shelterList,
8455
+ trackEnable,
8456
+ maxTrackShelterNum,
8457
+ trackShelterList,
8458
+ canvas
8459
+ };
8460
+ }
8461
+ function denormalizeRect(rect, canvas) {
8462
+ const xPx = Math.min(canvas.width, Math.round(clamp01(rect.x) * canvas.width));
8463
+ const yPx = Math.min(canvas.height, Math.round(clamp01(rect.y) * canvas.height));
8464
+ const wPx = Math.min(
8465
+ canvas.width - xPx,
8466
+ Math.round(clamp01(rect.width) * canvas.width)
8467
+ );
8468
+ const hPx = Math.min(
8469
+ canvas.height - yPx,
8470
+ Math.round(clamp01(rect.height) * canvas.height)
8471
+ );
8472
+ return {
8473
+ topLeftX: encodeShelterCoord(xPx, canvas.width),
8474
+ topLeftY: encodeShelterCoord(yPx, canvas.height),
8475
+ width: encodeShelterCoord(wPx, canvas.width),
8476
+ height: encodeShelterCoord(hPx, canvas.height)
8477
+ };
8478
+ }
8479
+ function encodeShelterListXml(rects, canvas, maxNum) {
8480
+ if (maxNum <= 0) return "<shelterList />";
8481
+ const sorted = [...rects].sort((a, b) => a.id - b.id).slice(0, maxNum);
8482
+ const byId = new Map(sorted.map((r) => [r.id, r]));
8483
+ const parts = [];
8484
+ parts.push("<shelterList>");
8485
+ for (let id = 0; id < maxNum; id++) {
8486
+ const r = byId.get(id);
8487
+ if (r && r.enable) {
8488
+ const { topLeftX, topLeftY, width, height } = denormalizeRect(r, canvas);
8489
+ parts.push(
8490
+ `<Shelter><id>${id}</id><enable>1</enable><layer>${r.layer | 0}</layer><color>${r.color | 0}</color><topLeftX>${topLeftX}</topLeftX><topLeftY>${topLeftY}</topLeftY><width>${width}</width><height>${height}</height></Shelter>`
8491
+ );
8492
+ } else if (r) {
8493
+ const { topLeftX, topLeftY, width, height } = denormalizeRect(r, canvas);
8494
+ parts.push(
8495
+ `<Shelter><id>${id}</id><enable>0</enable><layer>${r.layer | 0}</layer><color>${r.color | 0}</color><topLeftX>${topLeftX}</topLeftX><topLeftY>${topLeftY}</topLeftY><width>${width}</width><height>${height}</height></Shelter>`
8496
+ );
8497
+ } else {
8498
+ parts.push(
8499
+ `<Shelter><id>${id}</id><enable>0</enable><layer>0</layer><color>0</color><topLeftX>0</topLeftX><topLeftY>0</topLeftY><width>0</width><height>0</height></Shelter>`
8500
+ );
8501
+ }
8502
+ }
8503
+ parts.push("</shelterList>");
8504
+ return parts.join("");
8505
+ }
8506
+ function encodeTrackShelterListXml(rects, canvas, maxNum) {
8507
+ if (maxNum <= 0) return "<trackShelterList />";
8508
+ const sorted = [...rects].sort((a, b) => a.id - b.id).slice(0, maxNum);
8509
+ const byId = new Map(sorted.map((r) => [r.id, r]));
8510
+ const parts = [];
8511
+ parts.push("<trackShelterList>");
8512
+ for (let id = 0; id < maxNum; id++) {
8513
+ const r = byId.get(id);
8514
+ if (r) {
8515
+ const { topLeftX, topLeftY, width, height } = denormalizeRect(r, canvas);
8516
+ parts.push(
8517
+ `<trackShelter><name>${id}</name><enable>${r.enable ? 1 : 0}</enable><layer>${r.layer | 0}</layer><color>${r.color | 0}</color><topLeftX>${topLeftX}</topLeftX><topLeftY>${topLeftY}</topLeftY><width>${width}</width><height>${height}</height><pPos>${r.pPos | 0}</pPos><tPos>${r.tPos | 0}</tPos><zPos>${r.zPos | 0}</zPos></trackShelter>`
8518
+ );
8519
+ } else {
8520
+ parts.push(
8521
+ `<trackShelter><name>${id}</name><enable>0</enable><layer>0</layer><color>0</color><topLeftX>0</topLeftX><topLeftY>0</topLeftY><width>0</width><height>0</height><pPos>-1</pPos><tPos>-1</tPos><zPos>-1</zPos></trackShelter>`
8522
+ );
8523
+ }
8524
+ }
8525
+ parts.push("</trackShelterList>");
8526
+ return parts.join("");
8527
+ }
8528
+ function patchShelterXml(shelterXml, patch, canvas, maxNum, maxTrackShelterNum) {
8529
+ let out = shelterXml;
8530
+ if (patch.enable !== void 0) {
8531
+ out = out.replace(
8532
+ /<enable>[^<]*<\/enable>/,
8533
+ `<enable>${patch.enable ? 1 : 0}</enable>`
8534
+ );
8535
+ }
8536
+ if (patch.shelterList !== void 0) {
8537
+ const fragment = encodeShelterListXml(patch.shelterList, canvas, maxNum);
8538
+ out = out.replace(/<shelterList[\s\S]*?(?:\/>|<\/shelterList>)/, fragment);
8539
+ }
8540
+ if (patch.trackEnable !== void 0) {
8541
+ out = out.replace(
8542
+ /<trackEnable>[^<]*<\/trackEnable>/,
8543
+ `<trackEnable>${patch.trackEnable ? 1 : 0}</trackEnable>`
8544
+ );
8545
+ }
8546
+ if (patch.trackShelterList !== void 0) {
8547
+ const fragment = encodeTrackShelterListXml(
8548
+ patch.trackShelterList,
8549
+ canvas,
8550
+ maxTrackShelterNum
8551
+ );
8552
+ out = out.replace(
8553
+ /<trackShelterList[\s\S]*?(?:\/>|<\/trackShelterList>)/,
8554
+ fragment
8555
+ );
8556
+ }
8557
+ return out;
8558
+ }
8559
+
8560
+ // src/reolink/baichuan/utils/aiDetectCfg.ts
8561
+ function decodeAiDetectCfg(xml) {
8562
+ const type = getXmlText(xml, "type");
8563
+ if (type == null) throw new Error("AiDetectCfg: missing <type>");
8564
+ return {
8565
+ chn: Number(getXmlText(xml, "chn") ?? "0") | 0,
8566
+ type,
8567
+ sensitivity: Number(getXmlText(xml, "sensitivity") ?? "0") | 0,
8568
+ stayTime: Number(getXmlText(xml, "stayTime") ?? "0") | 0,
8569
+ minTargetWidth: Number(getXmlText(xml, "minTargetWidth") ?? "0"),
8570
+ minTargetHeight: Number(getXmlText(xml, "minTargetHeight") ?? "0"),
8571
+ maxTargetWidth: Number(getXmlText(xml, "maxTargetWidth") ?? "0"),
8572
+ maxTargetHeight: Number(getXmlText(xml, "maxTargetHeight") ?? "0"),
8573
+ width: Number(getXmlText(xml, "width") ?? "0") | 0,
8574
+ height: Number(getXmlText(xml, "height") ?? "0") | 0,
8575
+ area: getXmlText(xml, "area") ?? ""
8576
+ };
8577
+ }
8578
+ function clamp012(value) {
8579
+ if (!Number.isFinite(value)) return 0;
8580
+ if (value < 0) return 0;
8581
+ if (value > 1) return 1;
8582
+ return value;
8583
+ }
8584
+ function fmtFraction(value) {
8585
+ const v = clamp012(value);
8586
+ const e = v.toExponential(6);
8587
+ return e.replace(/e([+-])(\d)$/, "e$10$2");
8588
+ }
8589
+ function patchAiDetectCfgXml(currentXml, patch) {
8590
+ let out = currentXml;
8591
+ if (patch.sensitivity !== void 0) {
8592
+ out = out.replace(
8593
+ /<sensitivity>[^<]*<\/sensitivity>/,
8594
+ `<sensitivity>${patch.sensitivity | 0}</sensitivity>`
8595
+ );
8596
+ }
8597
+ if (patch.stayTime !== void 0) {
8598
+ out = out.replace(
8599
+ /<stayTime>[^<]*<\/stayTime>/,
8600
+ `<stayTime>${patch.stayTime | 0}</stayTime>`
8601
+ );
8602
+ }
8603
+ if (patch.minTargetWidth !== void 0) {
8604
+ out = out.replace(
8605
+ /<minTargetWidth>[^<]*<\/minTargetWidth>/,
8606
+ `<minTargetWidth>${fmtFraction(patch.minTargetWidth)}</minTargetWidth>`
8607
+ );
8608
+ }
8609
+ if (patch.minTargetHeight !== void 0) {
8610
+ out = out.replace(
8611
+ /<minTargetHeight>[^<]*<\/minTargetHeight>/,
8612
+ `<minTargetHeight>${fmtFraction(patch.minTargetHeight)}</minTargetHeight>`
8613
+ );
8614
+ }
8615
+ if (patch.maxTargetWidth !== void 0) {
8616
+ out = out.replace(
8617
+ /<maxTargetWidth>[^<]*<\/maxTargetWidth>/,
8618
+ `<maxTargetWidth>${fmtFraction(patch.maxTargetWidth)}</maxTargetWidth>`
8619
+ );
8620
+ }
8621
+ if (patch.maxTargetHeight !== void 0) {
8622
+ out = out.replace(
8623
+ /<maxTargetHeight>[^<]*<\/maxTargetHeight>/,
8624
+ `<maxTargetHeight>${fmtFraction(patch.maxTargetHeight)}</maxTargetHeight>`
8625
+ );
8626
+ }
8627
+ if (patch.area !== void 0) {
8628
+ out = out.replace(
8629
+ /<area>[^<]*<\/area>/,
8630
+ `<area>${patch.area}</area>`
8631
+ );
8632
+ }
8633
+ return out;
8634
+ }
8635
+
8636
+ // src/reolink/baichuan/utils/motionSensitivity.ts
8637
+ function clampInt(value, min, max) {
8638
+ if (!Number.isFinite(value)) return min;
8639
+ return Math.min(max, Math.max(min, Math.floor(value)));
8640
+ }
8641
+ function clampSensitivity(value) {
8642
+ return clampInt(value, 0, 50);
8643
+ }
8644
+ function clampHour(value) {
8645
+ return clampInt(value, 0, 23);
8646
+ }
8647
+ function clampMinute(value) {
8648
+ return clampInt(value, 0, 59);
8649
+ }
8650
+ function encodeMotionSensitivityListXml(bands) {
8651
+ if (bands.length === 0) return "<sensitivityInfoList />";
8652
+ const sorted = [...bands].sort((a, b) => a.id - b.id);
8653
+ const parts = ["<sensitivityInfoList>"];
8654
+ for (const b of sorted) {
8655
+ parts.push(
8656
+ `<sensitivityInfo><id>${b.id | 0}</id><sensitivity>${clampSensitivity(b.sensitivity)}</sensitivity><beginHour>${clampHour(b.beginHour)}</beginHour><beginMinute>${clampMinute(b.beginMinute)}</beginMinute><endHour>${clampHour(b.endHour)}</endHour><endMinute>${clampMinute(b.endMinute)}</endMinute></sensitivityInfo>`
8657
+ );
8658
+ }
8659
+ parts.push("</sensitivityInfoList>");
8660
+ return parts.join("");
8661
+ }
8662
+ function patchMotionSensitivityListXml(currentXml, bands) {
8663
+ const fragment = encodeMotionSensitivityListXml(bands);
8664
+ return currentXml.replace(
8665
+ /<sensitivityInfoList[\s\S]*?(?:\/>|<\/sensitivityInfoList>)/,
8666
+ fragment
8667
+ );
8668
+ }
8669
+
8322
8670
  // src/reolink/baichuan/ReolinkBaichuanApi.ts
8323
8671
  import { spawn as spawn2 } from "child_process";
8324
8672
  import { mkdir } from "fs/promises";
@@ -8819,15 +9167,14 @@ var getAiStateViaGetAiAlarm = async (params) => {
8819
9167
  0
8820
9168
  );
8821
9169
  };
9170
+ const isSupportedResponse = (xml) => {
9171
+ return /<AiDetectCfg[\s>]/i.test(xml) && getXmlText(xml, "type") != null;
9172
+ };
8822
9173
  for (const type of candidateTypes) {
8823
9174
  try {
8824
9175
  const xml = await tryOnce(type, ch);
8825
- if (xml) {
8826
- return {
8827
- channel: ch,
8828
- alarm_state: Number(getXmlText(xml, "alarm_state") ?? "0"),
8829
- support: Number(getXmlText(xml, "support") ?? "0")
8830
- };
9176
+ if (xml && isSupportedResponse(xml)) {
9177
+ return { channel: ch, alarm_state: 0, support: 1 };
8831
9178
  }
8832
9179
  } catch (e) {
8833
9180
  if (looksLikeConnectionDrop(e)) throw e;
@@ -8837,12 +9184,8 @@ var getAiStateViaGetAiAlarm = async (params) => {
8837
9184
  for (const type of candidateTypes) {
8838
9185
  try {
8839
9186
  const xml = await tryOnce(type, void 0);
8840
- if (xml) {
8841
- return {
8842
- channel: ch,
8843
- alarm_state: Number(getXmlText(xml, "alarm_state") ?? "0"),
8844
- support: Number(getXmlText(xml, "support") ?? "0")
8845
- };
9187
+ if (xml && isSupportedResponse(xml)) {
9188
+ return { channel: ch, alarm_state: 0, support: 1 };
8846
9189
  }
8847
9190
  } catch (e) {
8848
9191
  if (looksLikeConnectionDrop(e)) throw e;
@@ -12924,7 +13267,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
12924
13267
  return;
12925
13268
  }
12926
13269
  entry.startInFlight = (async () => {
12927
- const { BaichuanVideoStream: BaichuanVideoStream2 } = await import("./BaichuanVideoStream-HGPU2MZ3.js");
13270
+ const { BaichuanVideoStream: BaichuanVideoStream2 } = await import("./BaichuanVideoStream-NTIGPHYJ.js");
12928
13271
  const sessionKey = `live:object-detections:ch${entry.channel}:${entry.profile}`;
12929
13272
  const dedicated = await this.createDedicatedSession(sessionKey);
12930
13273
  const stream = new BaichuanVideoStream2({
@@ -14625,7 +14968,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
14625
14968
  * Note: Snapshot uses a special message ID system for binary responses
14626
14969
  */
14627
14970
  async getSnapshot(channel, options) {
14628
- const cmdId = 109;
14971
+ const cmdId = BC_CMD_ID_GET_SNAPSHOT;
14629
14972
  if (channel === void 0) {
14630
14973
  const composite = options?.compositeOptions;
14631
14974
  const widerChannel = composite?.widerChannel ?? 0;
@@ -18228,6 +18571,12 @@ ${stderr}`)
18228
18571
  `<valueTable>${opts.valueTable}</valueTable>`
18229
18572
  );
18230
18573
  }
18574
+ if (opts.sensitivitySchedule !== void 0) {
18575
+ modifiedXml = patchMotionSensitivityListXml(
18576
+ modifiedXml,
18577
+ opts.sensitivitySchedule
18578
+ );
18579
+ }
18231
18580
  await this.sendXml({
18232
18581
  cmdId: BC_CMD_ID_SET_MOTION_ALARM,
18233
18582
  channel: ch,
@@ -18275,6 +18624,72 @@ ${stderr}`)
18275
18624
  payloadXml: modifiedXml
18276
18625
  });
18277
18626
  }
18627
+ /**
18628
+ * SetAiDetectionFull (cmd_id=343 — same as `setAiDetection`).
18629
+ *
18630
+ * Pcap-confirmed (May 2026, E1 Zoom): the Reolink app sends the full
18631
+ * `<AiDetectCfg>` block via cmd_id=343 for ALL fields — area mask,
18632
+ * min/maxTarget* thresholds, sensitivity, stayTime — in one round-trip.
18633
+ * There's no separate "full setter" command (we previously suspected
18634
+ * cmd_id=345 but that's used for something else not yet identified).
18635
+ *
18636
+ * Wire format note: min/maxTarget* fractions are serialized in
18637
+ * scientific notation with 6 decimal places (e.g. "5.000000e-01" for
18638
+ * 0.5). The helper {@link patchAiDetectCfgXml} handles the formatting.
18639
+ *
18640
+ * @param channel 0-based channel
18641
+ * @param aiType one of "people" / "vehicle" / "dog_cat" / "face" / "package"
18642
+ * @param patch partial edit; any omitted field is preserved as-is
18643
+ */
18644
+ async setAiDetectionFull(channel, aiType, patch) {
18645
+ const ch = this.normalizeChannel(channel);
18646
+ const resolvedAiType = await this.resolveAiTypeForSetAiDetection(
18647
+ ch,
18648
+ aiType
18649
+ );
18650
+ const getXml = `<?xml version="1.0" encoding="UTF-8" ?>
18651
+ <body>
18652
+ <AiDetectCfg version="1.1">
18653
+ <chn>${ch}</chn>
18654
+ <type>${xmlEscape(resolvedAiType)}</type>
18655
+ </AiDetectCfg>
18656
+ </body>`;
18657
+ const currentXml = await this.sendXml({
18658
+ cmdId: BC_CMD_ID_GET_AI_ALARM,
18659
+ channel: ch,
18660
+ payloadXml: getXml
18661
+ });
18662
+ const patchedXml = patchAiDetectCfgXml(currentXml, patch);
18663
+ await this.sendXml({
18664
+ cmdId: BC_CMD_ID_SET_AI_ALARM,
18665
+ channel: ch,
18666
+ payloadXml: patchedXml
18667
+ });
18668
+ }
18669
+ /**
18670
+ * GetAiDetectionFull (cmd_id=342). Typed version of `getAiAlarmRaw`
18671
+ * returning the full {@link AiDetectCfgZone} for a single AI type.
18672
+ */
18673
+ async getAiDetectionFull(channel, aiType) {
18674
+ const ch = this.normalizeChannel(channel);
18675
+ const resolvedAiType = await this.resolveAiTypeForSetAiDetection(
18676
+ ch,
18677
+ aiType
18678
+ );
18679
+ const getXml = `<?xml version="1.0" encoding="UTF-8" ?>
18680
+ <body>
18681
+ <AiDetectCfg version="1.1">
18682
+ <chn>${ch}</chn>
18683
+ <type>${xmlEscape(resolvedAiType)}</type>
18684
+ </AiDetectCfg>
18685
+ </body>`;
18686
+ const xml = await this.sendXml({
18687
+ cmdId: BC_CMD_ID_GET_AI_ALARM,
18688
+ channel: ch,
18689
+ payloadXml: getXml
18690
+ });
18691
+ return decodeAiDetectCfg(xml);
18692
+ }
18278
18693
  // --------------------
18279
18694
  // Siren/Audio Alarm APIs
18280
18695
  // --------------------
@@ -19517,7 +19932,7 @@ ${xml}`
19517
19932
  * @returns Test results for all stream types and profiles
19518
19933
  */
19519
19934
  async testChannelStreams(channel, logger) {
19520
- const { testChannelStreams } = await import("./DiagnosticsTools-HO3VI6D5.js");
19935
+ const { testChannelStreams } = await import("./DiagnosticsTools-ILDDJZL7.js");
19521
19936
  return await testChannelStreams({
19522
19937
  api: this,
19523
19938
  channel: this.normalizeChannel(channel),
@@ -19533,7 +19948,7 @@ ${xml}`
19533
19948
  * @returns Complete diagnostics for all channels and streams
19534
19949
  */
19535
19950
  async collectMultifocalDiagnostics(logger) {
19536
- const { collectMultifocalDiagnostics } = await import("./DiagnosticsTools-HO3VI6D5.js");
19951
+ const { collectMultifocalDiagnostics } = await import("./DiagnosticsTools-ILDDJZL7.js");
19537
19952
  return await collectMultifocalDiagnostics({
19538
19953
  api: this,
19539
19954
  logger
@@ -19855,6 +20270,69 @@ ${xml}`
19855
20270
  ...timeoutOpts
19856
20271
  });
19857
20272
  }
20273
+ /**
20274
+ * GetMaskZones (cmdId=52) returning a typed structure with normalized
20275
+ * 0..1 rectangles instead of the raw camera fixed-point integers.
20276
+ *
20277
+ * The camera-side coord system is `(pixel << 16) | canvas_dim` where
20278
+ * `canvas_dim` is the firmware-specific mask canvas (E1 Zoom = 540×304).
20279
+ * The returned `canvas` block remembers those dims so callers can
20280
+ * round-trip without re-fetching.
20281
+ *
20282
+ * @see decodePrivacyMaskZones — pure helper exported for UI consumers.
20283
+ */
20284
+ async getMaskZones(channel, options) {
20285
+ const xml = await this.sendPcapDerivedSettingsGetXml({
20286
+ cmdId: BC_CMD_ID_GET_PRIVACY_MASK,
20287
+ ...channel != null ? { channel } : {},
20288
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
20289
+ });
20290
+ return decodePrivacyMaskZones(xml);
20291
+ }
20292
+ /**
20293
+ * SetMaskZones (cmdId=53, read-modify-write of cmdId=52).
20294
+ *
20295
+ * Edits the rectangular shelter list and/or PTZ-tracking shelter list
20296
+ * in one call. Coordinates are normalized 0..1 of the camera mask
20297
+ * canvas — the lib re-fetches the current Shelter block, extracts the
20298
+ * canvas dims from the wire (low-16 of any non-zero coord), and
20299
+ * denormalizes back to the camera's pixel × canvas encoding.
20300
+ *
20301
+ * Pass `enable`/`trackEnable` to toggle the master enable flags without
20302
+ * touching the rectangle data. Pass `shelterList`/`trackShelterList` to
20303
+ * replace the corresponding list — missing slots are padded with
20304
+ * disabled zero-rects matching the camera's idle pattern.
20305
+ *
20306
+ * @param channel 0-based channel
20307
+ * @param patch partial edit; any omitted field is preserved as-is
20308
+ * @param options.canvas override the auto-detected mask canvas (only
20309
+ * needed when the GET response has no non-zero coords to extract from)
20310
+ * @param options.timeoutMs custom request timeout
20311
+ */
20312
+ async setMaskZones(channel, patch, options) {
20313
+ const ch = this.normalizeChannel(channel);
20314
+ const timeoutOpts = options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {};
20315
+ const currentXml = await this.sendXml({
20316
+ cmdId: BC_CMD_ID_GET_PRIVACY_MASK,
20317
+ channel: ch,
20318
+ ...timeoutOpts
20319
+ });
20320
+ const current = decodePrivacyMaskZones(currentXml);
20321
+ const canvas = options?.canvas ?? current.canvas;
20322
+ const patchedXml = patchShelterXml(
20323
+ currentXml,
20324
+ patch,
20325
+ canvas,
20326
+ current.maxNum,
20327
+ current.maxTrackShelterNum
20328
+ );
20329
+ await this.sendXml({
20330
+ cmdId: BC_CMD_ID_SET_PRIVACY_MASK,
20331
+ channel: ch,
20332
+ payloadXml: ensureXmlHeader(patchedXml),
20333
+ ...timeoutOpts
20334
+ });
20335
+ }
19858
20336
  /**
19859
20337
  * GetAudioNoise via Baichuan (cmdId=439). Reads `enable` + `level`
19860
20338
  * from the aiDenoise block. Mirrors reolink_aio's `GetAudioNoise`.
@@ -20541,6 +21019,18 @@ ${xml}`
20541
21019
  });
20542
21020
  return parseVersionInfo(xml);
20543
21021
  }
21022
+ /**
21023
+ * GetUid (cmd_id=114). Returns the device UID / serial visible in the
21024
+ * Reolink app. Response shape: `<Uid><uid>9527000XXXXX</uid></Uid>`.
21025
+ * Device-global — no channel parameter.
21026
+ */
21027
+ async getUid(options) {
21028
+ const xml = await this.sendXml({
21029
+ cmdId: BC_CMD_ID_GET_UID,
21030
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
21031
+ });
21032
+ return (getXmlText(xml, "uid") ?? "").trim();
21033
+ }
20544
21034
  async getLedState(channel, options) {
20545
21035
  const rawXml = await this.sendPcapDerivedSettingsGetXml({
20546
21036
  cmdId: BC_CMD_ID_GET_LED_STATE,
@@ -23985,6 +24475,18 @@ export {
23985
24475
  computeDeviceCapabilities,
23986
24476
  xmlIndicatesFloodlight,
23987
24477
  decideSleepInferenceTransition,
24478
+ DEFAULT_SHELTER_CANVAS,
24479
+ decodeShelterCoord,
24480
+ encodeShelterCoord,
24481
+ extractCanvasFromShelterXml,
24482
+ decodePrivacyMaskZones,
24483
+ encodeShelterListXml,
24484
+ encodeTrackShelterListXml,
24485
+ patchShelterXml,
24486
+ decodeAiDetectCfg,
24487
+ patchAiDetectCfgXml,
24488
+ encodeMotionSensitivityListXml,
24489
+ patchMotionSensitivityListXml,
23988
24490
  DUAL_LENS_DUAL_MOTION_MODELS,
23989
24491
  DUAL_LENS_SINGLE_MOTION_MODELS,
23990
24492
  DUAL_LENS_MODELS,
@@ -24006,4 +24508,4 @@ export {
24006
24508
  isTcpFailureThatShouldFallbackToUdp,
24007
24509
  autoDetectDeviceType
24008
24510
  };
24009
- //# sourceMappingURL=chunk-EZ7WNPLB.js.map
24511
+ //# sourceMappingURL=chunk-P4X5OU25.js.map