@apocaliss92/nodelink-js 0.4.22 → 0.4.24

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.
@@ -60,7 +60,7 @@ var init_urls = __esm({
60
60
  function bcHeaderHasPayloadOffset(messageClass) {
61
61
  return messageClass === BC_CLASS_MODERN_24 || messageClass === BC_CLASS_MODERN_24_ALT || messageClass === BC_CLASS_FILE_DOWNLOAD;
62
62
  }
63
- var BC_TCP_DEFAULT_PORT, BC_MAGIC, BC_MAGIC_REV, BC_XML_KEY, BC_AES_IV, BC_CLASS_LEGACY, BC_CLASS_MODERN_20, BC_CLASS_MODERN_24, BC_CLASS_MODERN_24_ALT, BC_CLASS_FILE_DOWNLOAD, BC_CMD_ID_LOGOUT, BC_CMD_ID_VIDEO, BC_CMD_ID_VIDEO_STOP, BC_CMD_ID_FILE_INFO_LIST_REPLAY, BC_CMD_ID_FILE_INFO_LIST_STOP, BC_CMD_ID_FILE_INFO_LIST_DL_VIDEO, BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD, BC_CMD_ID_FILE_INFO_LIST_OPEN, BC_CMD_ID_FILE_INFO_LIST_GET, BC_CMD_ID_FILE_INFO_LIST_CLOSE, BC_CMD_ID_FIND_REC_VIDEO_OPEN, BC_CMD_ID_FIND_REC_VIDEO_GET, BC_CMD_ID_FIND_REC_VIDEO_CLOSE, BC_CMD_ID_TALK_ABILITY, BC_CMD_ID_TALK_RESET, BC_CMD_ID_TALK_CONFIG, BC_CMD_ID_TALK, BC_CMD_ID_PTZ_CONTROL, BC_CMD_ID_PTZ_CONTROL_PRESET, BC_CMD_ID_GET_PTZ_PRESET, BC_CMD_ID_GET_PTZ_POSITION, BC_CMD_ID_GET_ZOOM_FOCUS, BC_CMD_ID_SET_ZOOM_FOCUS, BC_CMD_ID_GET_BATTERY_INFO_LIST, BC_CMD_ID_GET_BATTERY_INFO, BC_CMD_ID_UDP_KEEP_ALIVE, BC_CMD_ID_GET_PIR_INFO, BC_CMD_ID_SET_PIR_INFO, BC_CMD_ID_GET_MOTION_ALARM, BC_CMD_ID_SET_MOTION_ALARM, BC_CMD_ID_GET_AI_ALARM, BC_CMD_ID_SET_AI_ALARM, BC_CMD_ID_GET_AUDIO_ALARM, BC_CMD_ID_AUDIO_ALARM_PLAY, BC_CMD_ID_GET_WHITE_LED, BC_CMD_ID_SET_WHITE_LED_STATE, BC_CMD_ID_SET_WHITE_LED_TASK, BC_CMD_ID_FLOODLIGHT_STATUS_LIST, BC_CMD_ID_ABILITY_INFO, BC_CMD_ID_SUPPORT, BC_CMD_ID_PING, BC_CMD_ID_CHANNEL_INFO_ALL, BC_CMD_ID_GET_OSD_DATETIME, BC_CMD_ID_SET_OSD_DATETIME, BC_CMD_ID_GET_RECORD_CFG, BC_CMD_ID_GET_ABILITY_SUPPORT, BC_CMD_ID_GET_FTP_TASK, BC_CMD_ID_GET_VERSION_INFO, BC_CMD_ID_GET_RECORD, BC_CMD_ID_GET_HDD_INFO_LIST, BC_CMD_ID_GET_WIFI_SIGNAL, BC_CMD_ID_GET_WIFI, BC_CMD_ID_GET_ONLINE_USER_LIST, BC_CMD_ID_GET_DAY_RECORDS, BC_CMD_ID_GET_STREAM_INFO_LIST, BC_CMD_ID_GET_LED_STATE, BC_CMD_ID_GET_EMAIL_TASK, BC_CMD_ID_GET_AUDIO_TASK, BC_CMD_ID_GET_AUDIO_CFG, BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD, BC_CMD_ID_GET_TIMELAPSE_CFG, BC_CMD_ID_GET_AI_DENOISE, BC_CMD_ID_GET_KIT_AP_CFG, BC_CMD_ID_GET_REC_ENC_CFG, BC_CMD_ID_GET_ACCESS_USER_LIST, BC_CMD_ID_GET_SLEEP_STATE, BC_CMD_ID_GET_VIDEO_INPUT, BC_CMD_ID_GET_SYSTEM_GENERAL, BC_CMD_ID_GET_SUPPORT, BC_CMD_ID_GET_AI_CFG, BC_CMD_ID_SET_AI_CFG, BC_CMD_ID_GET_SIREN_STATUS, BC_CMD_ID_SET_AUDIO_TASK, BC_CMD_ID_SET_VIDEO_INPUT, BC_CMD_ID_SET_DAY_NIGHT_THRESHOLD, BC_CMD_ID_GET_ENC, BC_CMD_ID_SET_ENC, BC_CMD_ID_GET_PRIVACY_MASK, BC_CMD_ID_SET_PRIVACY_MASK, BC_CMD_ID_SET_AI_DENOISE, BC_CMD_ID_SET_LED_STATE, BC_CMD_ID_SET_AUDIO_CFG, BC_CMD_ID_SET_EMAIL_TASK, BC_CMD_ID_GET_AUTO_FOCUS, BC_CMD_ID_SET_AUTO_FOCUS, BC_CMD_ID_GET_EMAIL, BC_CMD_ID_SET_EMAIL, BC_CMD_ID_TEST_EMAIL, BC_CMD_ID_GET_NTP, BC_CMD_ID_SET_NTP, BC_CMD_ID_SET_SYSTEM_GENERAL, BC_CMD_ID_GET_DST, BC_CMD_ID_SET_DST, BC_CMD_ID_GET_AUTO_REBOOT, BC_CMD_ID_SET_AUTO_REBOOT, BC_CMD_ID_CMD_123, BC_CMD_ID_CMD_209, BC_CMD_ID_CMD_265, BC_CMD_ID_CMD_440, BC_CMD_ID_PUSH_VIDEO_INPUT, BC_CMD_ID_PUSH_SERIAL, BC_CMD_ID_PUSH_NET_INFO, BC_CMD_ID_PUSH_DINGDONG_LIST, BC_CMD_ID_PUSH_SLEEP_STATUS, BC_CMD_ID_PUSH_COORDINATE_POINT_LIST, BC_CMD_ID_DING_DONG_CTRL, BC_CMD_ID_GET_DING_DONG_LIST, BC_CMD_ID_DING_DONG_OPT, BC_CMD_ID_GET_DING_DONG_CFG, BC_CMD_ID_SET_DING_DONG_CFG, BC_CMD_ID_QUICK_REPLY_PLAY, BC_CMD_ID_GET_DING_DONG_SILENT, BC_CMD_ID_SET_DING_DONG_SILENT;
63
+ var BC_TCP_DEFAULT_PORT, BC_MAGIC, BC_MAGIC_REV, BC_XML_KEY, BC_AES_IV, BC_CLASS_LEGACY, BC_CLASS_MODERN_20, BC_CLASS_MODERN_24, BC_CLASS_MODERN_24_ALT, BC_CLASS_FILE_DOWNLOAD, BC_CMD_ID_LOGOUT, BC_CMD_ID_VIDEO, BC_CMD_ID_VIDEO_STOP, BC_CMD_ID_FILE_INFO_LIST_REPLAY, BC_CMD_ID_FILE_INFO_LIST_STOP, BC_CMD_ID_FILE_INFO_LIST_DL_VIDEO, BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD, BC_CMD_ID_FILE_INFO_LIST_OPEN, BC_CMD_ID_FILE_INFO_LIST_GET, BC_CMD_ID_FILE_INFO_LIST_CLOSE, BC_CMD_ID_FIND_REC_VIDEO_OPEN, BC_CMD_ID_FIND_REC_VIDEO_GET, BC_CMD_ID_FIND_REC_VIDEO_CLOSE, BC_CMD_ID_TALK_ABILITY, BC_CMD_ID_TALK_RESET, BC_CMD_ID_TALK_CONFIG, BC_CMD_ID_TALK, BC_CMD_ID_PTZ_CONTROL, BC_CMD_ID_PTZ_CONTROL_PRESET, BC_CMD_ID_GET_PTZ_PRESET, BC_CMD_ID_GET_PTZ_POSITION, BC_CMD_ID_GET_ZOOM_FOCUS, BC_CMD_ID_SET_ZOOM_FOCUS, BC_CMD_ID_GET_BATTERY_INFO_LIST, BC_CMD_ID_GET_BATTERY_INFO, BC_CMD_ID_UDP_KEEP_ALIVE, BC_CMD_ID_GET_PIR_INFO, BC_CMD_ID_SET_PIR_INFO, BC_CMD_ID_GET_MOTION_ALARM, BC_CMD_ID_SET_MOTION_ALARM, BC_CMD_ID_GET_AI_ALARM, BC_CMD_ID_SET_AI_ALARM, BC_CMD_ID_GET_AUDIO_ALARM, BC_CMD_ID_AUDIO_ALARM_PLAY, BC_CMD_ID_GET_WHITE_LED, BC_CMD_ID_SET_WHITE_LED_STATE, BC_CMD_ID_SET_WHITE_LED_TASK, BC_CMD_ID_FLOODLIGHT_STATUS_LIST, BC_CMD_ID_ABILITY_INFO, BC_CMD_ID_SUPPORT, BC_CMD_ID_PING, BC_CMD_ID_GET_SNAPSHOT, BC_CMD_ID_GET_UID, BC_CMD_ID_CHANNEL_INFO_ALL, BC_CMD_ID_GET_OSD_DATETIME, BC_CMD_ID_SET_OSD_DATETIME, BC_CMD_ID_GET_RECORD_CFG, BC_CMD_ID_GET_ABILITY_SUPPORT, BC_CMD_ID_GET_FTP_TASK, BC_CMD_ID_GET_VERSION_INFO, BC_CMD_ID_GET_RECORD, BC_CMD_ID_GET_HDD_INFO_LIST, BC_CMD_ID_GET_WIFI_SIGNAL, BC_CMD_ID_GET_WIFI, BC_CMD_ID_GET_ONLINE_USER_LIST, BC_CMD_ID_GET_DAY_RECORDS, BC_CMD_ID_GET_STREAM_INFO_LIST, BC_CMD_ID_GET_LED_STATE, BC_CMD_ID_GET_EMAIL_TASK, BC_CMD_ID_GET_AUDIO_TASK, BC_CMD_ID_GET_AUDIO_CFG, BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD, BC_CMD_ID_GET_TIMELAPSE_CFG, BC_CMD_ID_GET_AI_DENOISE, BC_CMD_ID_GET_KIT_AP_CFG, BC_CMD_ID_GET_REC_ENC_CFG, BC_CMD_ID_GET_ACCESS_USER_LIST, BC_CMD_ID_GET_SLEEP_STATE, BC_CMD_ID_GET_VIDEO_INPUT, BC_CMD_ID_GET_SYSTEM_GENERAL, BC_CMD_ID_GET_SUPPORT, BC_CMD_ID_GET_AI_CFG, BC_CMD_ID_SET_AI_CFG, BC_CMD_ID_GET_SIREN_STATUS, BC_CMD_ID_SET_AUDIO_TASK, BC_CMD_ID_SET_VIDEO_INPUT, BC_CMD_ID_SET_DAY_NIGHT_THRESHOLD, BC_CMD_ID_GET_ENC, BC_CMD_ID_SET_ENC, BC_CMD_ID_GET_PRIVACY_MASK, BC_CMD_ID_SET_PRIVACY_MASK, BC_CMD_ID_SET_AI_DENOISE, BC_CMD_ID_SET_LED_STATE, BC_CMD_ID_SET_AUDIO_CFG, BC_CMD_ID_SET_EMAIL_TASK, BC_CMD_ID_GET_AUTO_FOCUS, BC_CMD_ID_SET_AUTO_FOCUS, BC_CMD_ID_GET_EMAIL, BC_CMD_ID_SET_EMAIL, BC_CMD_ID_TEST_EMAIL, BC_CMD_ID_GET_NTP, BC_CMD_ID_SET_NTP, BC_CMD_ID_SET_SYSTEM_GENERAL, BC_CMD_ID_GET_DST, BC_CMD_ID_SET_DST, BC_CMD_ID_GET_AUTO_REBOOT, BC_CMD_ID_SET_AUTO_REBOOT, BC_CMD_ID_CMD_123, BC_CMD_ID_CMD_209, BC_CMD_ID_CMD_265, BC_CMD_ID_CMD_440, BC_CMD_ID_PUSH_VIDEO_INPUT, BC_CMD_ID_PUSH_SERIAL, BC_CMD_ID_PUSH_NET_INFO, BC_CMD_ID_PUSH_DINGDONG_LIST, BC_CMD_ID_PUSH_SLEEP_STATUS, BC_CMD_ID_PUSH_COORDINATE_POINT_LIST, BC_CMD_ID_DING_DONG_CTRL, BC_CMD_ID_GET_DING_DONG_LIST, BC_CMD_ID_DING_DONG_OPT, BC_CMD_ID_GET_DING_DONG_CFG, BC_CMD_ID_SET_DING_DONG_CFG, BC_CMD_ID_QUICK_REPLY_PLAY, BC_CMD_ID_GET_DING_DONG_SILENT, BC_CMD_ID_SET_DING_DONG_SILENT;
64
64
  var init_constants = __esm({
65
65
  "src/protocol/constants.ts"() {
66
66
  "use strict";
@@ -124,6 +124,8 @@ var init_constants = __esm({
124
124
  BC_CMD_ID_ABILITY_INFO = 151;
125
125
  BC_CMD_ID_SUPPORT = 199;
126
126
  BC_CMD_ID_PING = 93;
127
+ BC_CMD_ID_GET_SNAPSHOT = 109;
128
+ BC_CMD_ID_GET_UID = 114;
127
129
  BC_CMD_ID_CHANNEL_INFO_ALL = 145;
128
130
  BC_CMD_ID_GET_OSD_DATETIME = 44;
129
131
  BC_CMD_ID_SET_OSD_DATETIME = 45;
@@ -16480,15 +16482,14 @@ var getAiStateViaGetAiAlarm = async (params) => {
16480
16482
  0
16481
16483
  );
16482
16484
  };
16485
+ const isSupportedResponse = (xml) => {
16486
+ return /<AiDetectCfg[\s>]/i.test(xml) && getXmlText(xml, "type") != null;
16487
+ };
16483
16488
  for (const type of candidateTypes) {
16484
16489
  try {
16485
16490
  const xml = await tryOnce(type, ch);
16486
- if (xml) {
16487
- return {
16488
- channel: ch,
16489
- alarm_state: Number(getXmlText(xml, "alarm_state") ?? "0"),
16490
- support: Number(getXmlText(xml, "support") ?? "0")
16491
- };
16491
+ if (xml && isSupportedResponse(xml)) {
16492
+ return { channel: ch, alarm_state: 0, support: 1 };
16492
16493
  }
16493
16494
  } catch (e) {
16494
16495
  if (looksLikeConnectionDrop(e)) throw e;
@@ -16498,12 +16499,8 @@ var getAiStateViaGetAiAlarm = async (params) => {
16498
16499
  for (const type of candidateTypes) {
16499
16500
  try {
16500
16501
  const xml = await tryOnce(type, void 0);
16501
- if (xml) {
16502
- return {
16503
- channel: ch,
16504
- alarm_state: Number(getXmlText(xml, "alarm_state") ?? "0"),
16505
- support: Number(getXmlText(xml, "support") ?? "0")
16506
- };
16502
+ if (xml && isSupportedResponse(xml)) {
16503
+ return { channel: ch, alarm_state: 0, support: 1 };
16507
16504
  }
16508
16505
  } catch (e) {
16509
16506
  if (looksLikeConnectionDrop(e)) throw e;
@@ -17003,6 +17000,355 @@ var calculatePipOverlayPosition = (params) => {
17003
17000
  };
17004
17001
  };
17005
17002
 
17003
+ // src/reolink/baichuan/utils/privacyMask.ts
17004
+ init_xml();
17005
+ var DEFAULT_SHELTER_CANVAS = {
17006
+ width: 540,
17007
+ height: 304
17008
+ };
17009
+ function decodeShelterCoord(value) {
17010
+ const v = (value | 0) >>> 0;
17011
+ return { pixel: v >>> 16, canvas: v & 65535 };
17012
+ }
17013
+ function encodeShelterCoord(pixel, canvas) {
17014
+ const p = Math.min(65535, Math.max(0, Math.floor(pixel)));
17015
+ const c = Math.min(65535, Math.max(0, Math.floor(canvas)));
17016
+ return p * 65536 + c;
17017
+ }
17018
+ function clamp01(value) {
17019
+ if (!Number.isFinite(value)) return 0;
17020
+ if (value < 0) return 0;
17021
+ if (value > 1) return 1;
17022
+ return value;
17023
+ }
17024
+ function decodeRect(raw, canvas, idKey) {
17025
+ const xRaw = Number(raw["topLeftX"] ?? 0) | 0;
17026
+ const yRaw = Number(raw["topLeftY"] ?? 0) | 0;
17027
+ const wRaw = Number(raw["width"] ?? 0) | 0;
17028
+ const hRaw = Number(raw["height"] ?? 0) | 0;
17029
+ const xPixel = decodeShelterCoord(xRaw).pixel;
17030
+ const yPixel = decodeShelterCoord(yRaw).pixel;
17031
+ const wPixel = decodeShelterCoord(wRaw).pixel;
17032
+ const hPixel = decodeShelterCoord(hRaw).pixel;
17033
+ return {
17034
+ id: Number(raw[idKey] ?? 0) | 0,
17035
+ enable: Number(raw["enable"] ?? 0) === 1,
17036
+ layer: Number(raw["layer"] ?? 0) | 0,
17037
+ color: Number(raw["color"] ?? 0) | 0,
17038
+ x: canvas.width > 0 ? xPixel / canvas.width : 0,
17039
+ y: canvas.height > 0 ? yPixel / canvas.height : 0,
17040
+ width: canvas.width > 0 ? wPixel / canvas.width : 0,
17041
+ height: canvas.height > 0 ? hPixel / canvas.height : 0
17042
+ };
17043
+ }
17044
+ function extractCanvasFromShelterXml(xml) {
17045
+ const xMatch = xml.match(/<topLeftX>(\d+)<\/topLeftX>/g) ?? [];
17046
+ const yMatch = xml.match(/<topLeftY>(\d+)<\/topLeftY>/g) ?? [];
17047
+ let canvasX = 0;
17048
+ let canvasY = 0;
17049
+ for (const tag of xMatch) {
17050
+ const v = Number(tag.replace(/<\/?topLeftX>/g, "")) | 0;
17051
+ if (v > 0) {
17052
+ canvasX = v & 65535;
17053
+ break;
17054
+ }
17055
+ }
17056
+ for (const tag of yMatch) {
17057
+ const v = Number(tag.replace(/<\/?topLeftY>/g, "")) | 0;
17058
+ if (v > 0) {
17059
+ canvasY = v & 65535;
17060
+ break;
17061
+ }
17062
+ }
17063
+ return {
17064
+ width: canvasX > 0 ? canvasX : DEFAULT_SHELTER_CANVAS.width,
17065
+ height: canvasY > 0 ? canvasY : DEFAULT_SHELTER_CANVAS.height
17066
+ };
17067
+ }
17068
+ function parseRectTags(block) {
17069
+ const out = {};
17070
+ const grab = (tag) => {
17071
+ const m = block.match(new RegExp(`<${tag}>([^<]*)<\\/${tag}>`));
17072
+ if (m && m[1] !== void 0) out[tag] = m[1];
17073
+ };
17074
+ grab("id");
17075
+ grab("name");
17076
+ grab("enable");
17077
+ grab("layer");
17078
+ grab("color");
17079
+ grab("topLeftX");
17080
+ grab("topLeftY");
17081
+ grab("width");
17082
+ grab("height");
17083
+ grab("pPos");
17084
+ grab("tPos");
17085
+ grab("zPos");
17086
+ return out;
17087
+ }
17088
+ function decodePrivacyMaskZones(xml) {
17089
+ const canvas = extractCanvasFromShelterXml(xml);
17090
+ const enable = Number(getXmlText(xml, "enable") ?? "0") === 1;
17091
+ const trackEnable = Number(getXmlText(xml, "trackEnable") ?? "0") === 1;
17092
+ const maxTrackShelterNum = Number(getXmlText(xml, "maxTrackShelterNum") ?? "0") | 0;
17093
+ const rawMaxNum = getXmlText(xml, "maxNum");
17094
+ const maxNum = rawMaxNum !== void 0 ? Number(rawMaxNum) | 0 : maxTrackShelterNum > 0 ? maxTrackShelterNum : 4;
17095
+ const shelterList = [];
17096
+ const shelterListBlock = xml.match(
17097
+ /<shelterList>([\s\S]*?)<\/shelterList>/
17098
+ )?.[1];
17099
+ if (shelterListBlock) {
17100
+ const blocks = shelterListBlock.matchAll(
17101
+ /<Shelter>([\s\S]*?)<\/Shelter>/g
17102
+ );
17103
+ for (const m of blocks) {
17104
+ const tags = parseRectTags(m[1] ?? "");
17105
+ const decoded = decodeRect(tags, canvas, "id");
17106
+ shelterList.push(decoded);
17107
+ }
17108
+ }
17109
+ const trackShelterList = [];
17110
+ const trackBlock = xml.match(
17111
+ /<trackShelterList>([\s\S]*?)<\/trackShelterList>/
17112
+ )?.[1];
17113
+ if (trackBlock) {
17114
+ const blocks = trackBlock.matchAll(
17115
+ /<trackShelter>([\s\S]*?)<\/trackShelter>/g
17116
+ );
17117
+ for (const m of blocks) {
17118
+ const tags = parseRectTags(m[1] ?? "");
17119
+ const base = decodeRect(
17120
+ tags,
17121
+ canvas,
17122
+ "name"
17123
+ );
17124
+ trackShelterList.push({
17125
+ ...base,
17126
+ pPos: Number(tags.pPos ?? -1) | 0,
17127
+ tPos: Number(tags.tPos ?? -1) | 0,
17128
+ zPos: Number(tags.zPos ?? -1) | 0
17129
+ });
17130
+ }
17131
+ }
17132
+ return {
17133
+ enable,
17134
+ maxNum,
17135
+ shelterList,
17136
+ trackEnable,
17137
+ maxTrackShelterNum,
17138
+ trackShelterList,
17139
+ canvas
17140
+ };
17141
+ }
17142
+ function denormalizeRect(rect, canvas) {
17143
+ const xPx = Math.min(canvas.width, Math.round(clamp01(rect.x) * canvas.width));
17144
+ const yPx = Math.min(canvas.height, Math.round(clamp01(rect.y) * canvas.height));
17145
+ const wPx = Math.min(
17146
+ canvas.width - xPx,
17147
+ Math.round(clamp01(rect.width) * canvas.width)
17148
+ );
17149
+ const hPx = Math.min(
17150
+ canvas.height - yPx,
17151
+ Math.round(clamp01(rect.height) * canvas.height)
17152
+ );
17153
+ return {
17154
+ topLeftX: encodeShelterCoord(xPx, canvas.width),
17155
+ topLeftY: encodeShelterCoord(yPx, canvas.height),
17156
+ width: encodeShelterCoord(wPx, canvas.width),
17157
+ height: encodeShelterCoord(hPx, canvas.height)
17158
+ };
17159
+ }
17160
+ function encodeShelterListXml(rects, canvas, maxNum) {
17161
+ if (maxNum <= 0) return "<shelterList />";
17162
+ const sorted = [...rects].sort((a, b) => a.id - b.id).slice(0, maxNum);
17163
+ const byId = new Map(sorted.map((r) => [r.id, r]));
17164
+ const parts = [];
17165
+ parts.push("<shelterList>");
17166
+ for (let id = 0; id < maxNum; id++) {
17167
+ const r = byId.get(id);
17168
+ if (r && r.enable) {
17169
+ const { topLeftX, topLeftY, width, height } = denormalizeRect(r, canvas);
17170
+ parts.push(
17171
+ `<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>`
17172
+ );
17173
+ } else if (r) {
17174
+ const { topLeftX, topLeftY, width, height } = denormalizeRect(r, canvas);
17175
+ parts.push(
17176
+ `<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>`
17177
+ );
17178
+ } else {
17179
+ parts.push(
17180
+ `<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>`
17181
+ );
17182
+ }
17183
+ }
17184
+ parts.push("</shelterList>");
17185
+ return parts.join("");
17186
+ }
17187
+ function encodeTrackShelterListXml(rects, canvas, maxNum) {
17188
+ if (maxNum <= 0) return "<trackShelterList />";
17189
+ const sorted = [...rects].sort((a, b) => a.id - b.id).slice(0, maxNum);
17190
+ const byId = new Map(sorted.map((r) => [r.id, r]));
17191
+ const parts = [];
17192
+ parts.push("<trackShelterList>");
17193
+ for (let id = 0; id < maxNum; id++) {
17194
+ const r = byId.get(id);
17195
+ if (r) {
17196
+ const { topLeftX, topLeftY, width, height } = denormalizeRect(r, canvas);
17197
+ parts.push(
17198
+ `<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>`
17199
+ );
17200
+ } else {
17201
+ parts.push(
17202
+ `<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>`
17203
+ );
17204
+ }
17205
+ }
17206
+ parts.push("</trackShelterList>");
17207
+ return parts.join("");
17208
+ }
17209
+ function patchShelterXml(shelterXml, patch, canvas, maxNum, maxTrackShelterNum) {
17210
+ let out = shelterXml;
17211
+ if (patch.enable !== void 0) {
17212
+ out = out.replace(
17213
+ /<enable>[^<]*<\/enable>/,
17214
+ `<enable>${patch.enable ? 1 : 0}</enable>`
17215
+ );
17216
+ }
17217
+ if (patch.shelterList !== void 0) {
17218
+ const fragment = encodeShelterListXml(patch.shelterList, canvas, maxNum);
17219
+ out = out.replace(/<shelterList[\s\S]*?(?:\/>|<\/shelterList>)/, fragment);
17220
+ }
17221
+ if (patch.trackEnable !== void 0) {
17222
+ out = out.replace(
17223
+ /<trackEnable>[^<]*<\/trackEnable>/,
17224
+ `<trackEnable>${patch.trackEnable ? 1 : 0}</trackEnable>`
17225
+ );
17226
+ }
17227
+ if (patch.trackShelterList !== void 0) {
17228
+ const fragment = encodeTrackShelterListXml(
17229
+ patch.trackShelterList,
17230
+ canvas,
17231
+ maxTrackShelterNum
17232
+ );
17233
+ out = out.replace(
17234
+ /<trackShelterList[\s\S]*?(?:\/>|<\/trackShelterList>)/,
17235
+ fragment
17236
+ );
17237
+ }
17238
+ return out;
17239
+ }
17240
+
17241
+ // src/reolink/baichuan/utils/aiDetectCfg.ts
17242
+ init_xml();
17243
+ function decodeAiDetectCfg(xml) {
17244
+ const type = getXmlText(xml, "type");
17245
+ if (type == null) throw new Error("AiDetectCfg: missing <type>");
17246
+ return {
17247
+ chn: Number(getXmlText(xml, "chn") ?? "0") | 0,
17248
+ type,
17249
+ sensitivity: Number(getXmlText(xml, "sensitivity") ?? "0") | 0,
17250
+ stayTime: Number(getXmlText(xml, "stayTime") ?? "0") | 0,
17251
+ minTargetWidth: Number(getXmlText(xml, "minTargetWidth") ?? "0"),
17252
+ minTargetHeight: Number(getXmlText(xml, "minTargetHeight") ?? "0"),
17253
+ maxTargetWidth: Number(getXmlText(xml, "maxTargetWidth") ?? "0"),
17254
+ maxTargetHeight: Number(getXmlText(xml, "maxTargetHeight") ?? "0"),
17255
+ width: Number(getXmlText(xml, "width") ?? "0") | 0,
17256
+ height: Number(getXmlText(xml, "height") ?? "0") | 0,
17257
+ area: getXmlText(xml, "area") ?? ""
17258
+ };
17259
+ }
17260
+ function clamp012(value) {
17261
+ if (!Number.isFinite(value)) return 0;
17262
+ if (value < 0) return 0;
17263
+ if (value > 1) return 1;
17264
+ return value;
17265
+ }
17266
+ function fmtFraction(value) {
17267
+ const v = clamp012(value);
17268
+ const e = v.toExponential(6);
17269
+ return e.replace(/e([+-])(\d)$/, "e$10$2");
17270
+ }
17271
+ function patchAiDetectCfgXml(currentXml, patch) {
17272
+ let out = currentXml;
17273
+ if (patch.sensitivity !== void 0) {
17274
+ out = out.replace(
17275
+ /<sensitivity>[^<]*<\/sensitivity>/,
17276
+ `<sensitivity>${patch.sensitivity | 0}</sensitivity>`
17277
+ );
17278
+ }
17279
+ if (patch.stayTime !== void 0) {
17280
+ out = out.replace(
17281
+ /<stayTime>[^<]*<\/stayTime>/,
17282
+ `<stayTime>${patch.stayTime | 0}</stayTime>`
17283
+ );
17284
+ }
17285
+ if (patch.minTargetWidth !== void 0) {
17286
+ out = out.replace(
17287
+ /<minTargetWidth>[^<]*<\/minTargetWidth>/,
17288
+ `<minTargetWidth>${fmtFraction(patch.minTargetWidth)}</minTargetWidth>`
17289
+ );
17290
+ }
17291
+ if (patch.minTargetHeight !== void 0) {
17292
+ out = out.replace(
17293
+ /<minTargetHeight>[^<]*<\/minTargetHeight>/,
17294
+ `<minTargetHeight>${fmtFraction(patch.minTargetHeight)}</minTargetHeight>`
17295
+ );
17296
+ }
17297
+ if (patch.maxTargetWidth !== void 0) {
17298
+ out = out.replace(
17299
+ /<maxTargetWidth>[^<]*<\/maxTargetWidth>/,
17300
+ `<maxTargetWidth>${fmtFraction(patch.maxTargetWidth)}</maxTargetWidth>`
17301
+ );
17302
+ }
17303
+ if (patch.maxTargetHeight !== void 0) {
17304
+ out = out.replace(
17305
+ /<maxTargetHeight>[^<]*<\/maxTargetHeight>/,
17306
+ `<maxTargetHeight>${fmtFraction(patch.maxTargetHeight)}</maxTargetHeight>`
17307
+ );
17308
+ }
17309
+ if (patch.area !== void 0) {
17310
+ out = out.replace(
17311
+ /<area>[^<]*<\/area>/,
17312
+ `<area>${patch.area}</area>`
17313
+ );
17314
+ }
17315
+ return out;
17316
+ }
17317
+
17318
+ // src/reolink/baichuan/utils/motionSensitivity.ts
17319
+ function clampInt(value, min, max) {
17320
+ if (!Number.isFinite(value)) return min;
17321
+ return Math.min(max, Math.max(min, Math.floor(value)));
17322
+ }
17323
+ function clampSensitivity(value) {
17324
+ return clampInt(value, 0, 50);
17325
+ }
17326
+ function clampHour(value) {
17327
+ return clampInt(value, 0, 23);
17328
+ }
17329
+ function clampMinute(value) {
17330
+ return clampInt(value, 0, 59);
17331
+ }
17332
+ function encodeMotionSensitivityListXml(bands) {
17333
+ if (bands.length === 0) return "<sensitivityInfoList />";
17334
+ const sorted = [...bands].sort((a, b) => a.id - b.id);
17335
+ const parts = ["<sensitivityInfoList>"];
17336
+ for (const b of sorted) {
17337
+ parts.push(
17338
+ `<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>`
17339
+ );
17340
+ }
17341
+ parts.push("</sensitivityInfoList>");
17342
+ return parts.join("");
17343
+ }
17344
+ function patchMotionSensitivityListXml(currentXml, bands) {
17345
+ const fragment = encodeMotionSensitivityListXml(bands);
17346
+ return currentXml.replace(
17347
+ /<sensitivityInfoList[\s\S]*?(?:\/>|<\/sensitivityInfoList>)/,
17348
+ fragment
17349
+ );
17350
+ }
17351
+
17006
17352
  // src/reolink/baichuan/utils/ptz.ts
17007
17353
  init_xml();
17008
17354
  var resolvePtzDirection = (command) => {
@@ -22358,7 +22704,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
22358
22704
  * Note: Snapshot uses a special message ID system for binary responses
22359
22705
  */
22360
22706
  async getSnapshot(channel, options) {
22361
- const cmdId = 109;
22707
+ const cmdId = BC_CMD_ID_GET_SNAPSHOT;
22362
22708
  if (channel === void 0) {
22363
22709
  const composite = options?.compositeOptions;
22364
22710
  const widerChannel = composite?.widerChannel ?? 0;
@@ -25961,6 +26307,12 @@ ${stderr}`)
25961
26307
  `<valueTable>${opts.valueTable}</valueTable>`
25962
26308
  );
25963
26309
  }
26310
+ if (opts.sensitivitySchedule !== void 0) {
26311
+ modifiedXml = patchMotionSensitivityListXml(
26312
+ modifiedXml,
26313
+ opts.sensitivitySchedule
26314
+ );
26315
+ }
25964
26316
  await this.sendXml({
25965
26317
  cmdId: BC_CMD_ID_SET_MOTION_ALARM,
25966
26318
  channel: ch,
@@ -26008,6 +26360,72 @@ ${stderr}`)
26008
26360
  payloadXml: modifiedXml
26009
26361
  });
26010
26362
  }
26363
+ /**
26364
+ * SetAiDetectionFull (cmd_id=343 — same as `setAiDetection`).
26365
+ *
26366
+ * Pcap-confirmed (May 2026, E1 Zoom): the Reolink app sends the full
26367
+ * `<AiDetectCfg>` block via cmd_id=343 for ALL fields — area mask,
26368
+ * min/maxTarget* thresholds, sensitivity, stayTime — in one round-trip.
26369
+ * There's no separate "full setter" command (we previously suspected
26370
+ * cmd_id=345 but that's used for something else not yet identified).
26371
+ *
26372
+ * Wire format note: min/maxTarget* fractions are serialized in
26373
+ * scientific notation with 6 decimal places (e.g. "5.000000e-01" for
26374
+ * 0.5). The helper {@link patchAiDetectCfgXml} handles the formatting.
26375
+ *
26376
+ * @param channel 0-based channel
26377
+ * @param aiType one of "people" / "vehicle" / "dog_cat" / "face" / "package"
26378
+ * @param patch partial edit; any omitted field is preserved as-is
26379
+ */
26380
+ async setAiDetectionFull(channel, aiType, patch) {
26381
+ const ch = this.normalizeChannel(channel);
26382
+ const resolvedAiType = await this.resolveAiTypeForSetAiDetection(
26383
+ ch,
26384
+ aiType
26385
+ );
26386
+ const getXml = `<?xml version="1.0" encoding="UTF-8" ?>
26387
+ <body>
26388
+ <AiDetectCfg version="1.1">
26389
+ <chn>${ch}</chn>
26390
+ <type>${xmlEscape(resolvedAiType)}</type>
26391
+ </AiDetectCfg>
26392
+ </body>`;
26393
+ const currentXml = await this.sendXml({
26394
+ cmdId: BC_CMD_ID_GET_AI_ALARM,
26395
+ channel: ch,
26396
+ payloadXml: getXml
26397
+ });
26398
+ const patchedXml = patchAiDetectCfgXml(currentXml, patch);
26399
+ await this.sendXml({
26400
+ cmdId: BC_CMD_ID_SET_AI_ALARM,
26401
+ channel: ch,
26402
+ payloadXml: patchedXml
26403
+ });
26404
+ }
26405
+ /**
26406
+ * GetAiDetectionFull (cmd_id=342). Typed version of `getAiAlarmRaw`
26407
+ * returning the full {@link AiDetectCfgZone} for a single AI type.
26408
+ */
26409
+ async getAiDetectionFull(channel, aiType) {
26410
+ const ch = this.normalizeChannel(channel);
26411
+ const resolvedAiType = await this.resolveAiTypeForSetAiDetection(
26412
+ ch,
26413
+ aiType
26414
+ );
26415
+ const getXml = `<?xml version="1.0" encoding="UTF-8" ?>
26416
+ <body>
26417
+ <AiDetectCfg version="1.1">
26418
+ <chn>${ch}</chn>
26419
+ <type>${xmlEscape(resolvedAiType)}</type>
26420
+ </AiDetectCfg>
26421
+ </body>`;
26422
+ const xml = await this.sendXml({
26423
+ cmdId: BC_CMD_ID_GET_AI_ALARM,
26424
+ channel: ch,
26425
+ payloadXml: getXml
26426
+ });
26427
+ return decodeAiDetectCfg(xml);
26428
+ }
26011
26429
  // --------------------
26012
26430
  // Siren/Audio Alarm APIs
26013
26431
  // --------------------
@@ -27588,6 +28006,69 @@ ${xml}`
27588
28006
  ...timeoutOpts
27589
28007
  });
27590
28008
  }
28009
+ /**
28010
+ * GetMaskZones (cmdId=52) returning a typed structure with normalized
28011
+ * 0..1 rectangles instead of the raw camera fixed-point integers.
28012
+ *
28013
+ * The camera-side coord system is `(pixel << 16) | canvas_dim` where
28014
+ * `canvas_dim` is the firmware-specific mask canvas (E1 Zoom = 540×304).
28015
+ * The returned `canvas` block remembers those dims so callers can
28016
+ * round-trip without re-fetching.
28017
+ *
28018
+ * @see decodePrivacyMaskZones — pure helper exported for UI consumers.
28019
+ */
28020
+ async getMaskZones(channel, options) {
28021
+ const xml = await this.sendPcapDerivedSettingsGetXml({
28022
+ cmdId: BC_CMD_ID_GET_PRIVACY_MASK,
28023
+ ...channel != null ? { channel } : {},
28024
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
28025
+ });
28026
+ return decodePrivacyMaskZones(xml);
28027
+ }
28028
+ /**
28029
+ * SetMaskZones (cmdId=53, read-modify-write of cmdId=52).
28030
+ *
28031
+ * Edits the rectangular shelter list and/or PTZ-tracking shelter list
28032
+ * in one call. Coordinates are normalized 0..1 of the camera mask
28033
+ * canvas — the lib re-fetches the current Shelter block, extracts the
28034
+ * canvas dims from the wire (low-16 of any non-zero coord), and
28035
+ * denormalizes back to the camera's pixel × canvas encoding.
28036
+ *
28037
+ * Pass `enable`/`trackEnable` to toggle the master enable flags without
28038
+ * touching the rectangle data. Pass `shelterList`/`trackShelterList` to
28039
+ * replace the corresponding list — missing slots are padded with
28040
+ * disabled zero-rects matching the camera's idle pattern.
28041
+ *
28042
+ * @param channel 0-based channel
28043
+ * @param patch partial edit; any omitted field is preserved as-is
28044
+ * @param options.canvas override the auto-detected mask canvas (only
28045
+ * needed when the GET response has no non-zero coords to extract from)
28046
+ * @param options.timeoutMs custom request timeout
28047
+ */
28048
+ async setMaskZones(channel, patch, options) {
28049
+ const ch = this.normalizeChannel(channel);
28050
+ const timeoutOpts = options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {};
28051
+ const currentXml = await this.sendXml({
28052
+ cmdId: BC_CMD_ID_GET_PRIVACY_MASK,
28053
+ channel: ch,
28054
+ ...timeoutOpts
28055
+ });
28056
+ const current = decodePrivacyMaskZones(currentXml);
28057
+ const canvas = options?.canvas ?? current.canvas;
28058
+ const patchedXml = patchShelterXml(
28059
+ currentXml,
28060
+ patch,
28061
+ canvas,
28062
+ current.maxNum,
28063
+ current.maxTrackShelterNum
28064
+ );
28065
+ await this.sendXml({
28066
+ cmdId: BC_CMD_ID_SET_PRIVACY_MASK,
28067
+ channel: ch,
28068
+ payloadXml: ensureXmlHeader(patchedXml),
28069
+ ...timeoutOpts
28070
+ });
28071
+ }
27591
28072
  /**
27592
28073
  * GetAudioNoise via Baichuan (cmdId=439). Reads `enable` + `level`
27593
28074
  * from the aiDenoise block. Mirrors reolink_aio's `GetAudioNoise`.
@@ -28274,6 +28755,18 @@ ${xml}`
28274
28755
  });
28275
28756
  return parseVersionInfo(xml);
28276
28757
  }
28758
+ /**
28759
+ * GetUid (cmd_id=114). Returns the device UID / serial visible in the
28760
+ * Reolink app. Response shape: `<Uid><uid>9527000XXXXX</uid></Uid>`.
28761
+ * Device-global — no channel parameter.
28762
+ */
28763
+ async getUid(options) {
28764
+ const xml = await this.sendXml({
28765
+ cmdId: BC_CMD_ID_GET_UID,
28766
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
28767
+ });
28768
+ return (getXmlText(xml, "uid") ?? "").trim();
28769
+ }
28277
28770
  async getLedState(channel, options) {
28278
28771
  const rawXml = await this.sendPcapDerivedSettingsGetXml({
28279
28772
  cmdId: BC_CMD_ID_GET_LED_STATE,