@apocaliss92/nodelink-js 0.4.12 → 0.4.14

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_GET_AUTO_FOCUS, BC_CMD_ID_SET_AUTO_FOCUS, 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_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";
@@ -165,8 +165,19 @@ var init_constants = __esm({
165
165
  BC_CMD_ID_SET_AI_DENOISE = 440;
166
166
  BC_CMD_ID_SET_LED_STATE = 209;
167
167
  BC_CMD_ID_SET_AUDIO_CFG = 265;
168
+ BC_CMD_ID_SET_EMAIL_TASK = 216;
168
169
  BC_CMD_ID_GET_AUTO_FOCUS = 224;
169
170
  BC_CMD_ID_SET_AUTO_FOCUS = 225;
171
+ BC_CMD_ID_GET_EMAIL = 42;
172
+ BC_CMD_ID_SET_EMAIL = 43;
173
+ BC_CMD_ID_TEST_EMAIL = 141;
174
+ BC_CMD_ID_GET_NTP = 38;
175
+ BC_CMD_ID_SET_NTP = 39;
176
+ BC_CMD_ID_SET_SYSTEM_GENERAL = 105;
177
+ BC_CMD_ID_GET_DST = 106;
178
+ BC_CMD_ID_SET_DST = 107;
179
+ BC_CMD_ID_GET_AUTO_REBOOT = 101;
180
+ BC_CMD_ID_SET_AUTO_REBOOT = 100;
170
181
  BC_CMD_ID_CMD_123 = 123;
171
182
  BC_CMD_ID_CMD_209 = 209;
172
183
  BC_CMD_ID_CMD_265 = 265;
@@ -15690,6 +15701,309 @@ function parseXmlFragmentToJson(xml) {
15690
15701
  return parsed.root;
15691
15702
  }
15692
15703
 
15704
+ // src/reolink/baichuan/utils/email.ts
15705
+ init_xml();
15706
+ var parseInt01 = (text) => {
15707
+ if (text === void 0) return void 0;
15708
+ return text.trim() === "1" ? 1 : 0;
15709
+ };
15710
+ var parseNumberSafe = (text) => {
15711
+ if (text === void 0) return void 0;
15712
+ const n = Number(text);
15713
+ return Number.isFinite(n) ? n : void 0;
15714
+ };
15715
+ var VALID_ATTACHMENT_TYPES = [
15716
+ "picture",
15717
+ "video",
15718
+ "none"
15719
+ ];
15720
+ var VALID_TEXT_TYPES = ["withText", "noText"];
15721
+ var normalizeAttachmentType = (text) => {
15722
+ const t = (text ?? "").trim();
15723
+ return VALID_ATTACHMENT_TYPES.includes(t) ? t : "picture";
15724
+ };
15725
+ var normalizeTextType = (text) => {
15726
+ const t = (text ?? "").trim();
15727
+ return VALID_TEXT_TYPES.includes(t) ? t : "withText";
15728
+ };
15729
+ function parseEmailConfigFromXml(xml) {
15730
+ const cfg = {
15731
+ smtpServer: getXmlText(xml, "smtpServer") ?? "",
15732
+ userName: getXmlText(xml, "userName") ?? "",
15733
+ password: getXmlText(xml, "password") ?? "",
15734
+ address1: getXmlText(xml, "address1") ?? "",
15735
+ address2: getXmlText(xml, "address2") ?? "",
15736
+ address3: getXmlText(xml, "address3") ?? "",
15737
+ smtpPort: parseNumberSafe(getXmlText(xml, "smtpPort")) ?? 0,
15738
+ sendNickname: getXmlText(xml, "sendNickname") ?? "",
15739
+ attachment: parseInt01(getXmlText(xml, "attachment")) ?? 0,
15740
+ attachmentType: normalizeAttachmentType(getXmlText(xml, "attachmentType")),
15741
+ textType: normalizeTextType(getXmlText(xml, "textType")),
15742
+ ssl: parseInt01(getXmlText(xml, "ssl")) ?? 0,
15743
+ interval: parseNumberSafe(getXmlText(xml, "interval")) ?? 30
15744
+ };
15745
+ const senderMaxLen = parseNumberSafe(getXmlText(xml, "senderMaxLen"));
15746
+ if (senderMaxLen !== void 0) cfg.senderMaxLen = senderMaxLen;
15747
+ const pwdMaxLen = parseNumberSafe(getXmlText(xml, "pwdMaxLen"));
15748
+ if (pwdMaxLen !== void 0) cfg.pwdMaxLen = pwdMaxLen;
15749
+ const ability = parseNumberSafe(getXmlText(xml, "emailAttachAbility"));
15750
+ if (ability !== void 0) cfg.emailAttachAbility = ability;
15751
+ return cfg;
15752
+ }
15753
+ function buildSetEmailXml(current, patch) {
15754
+ const merged = { ...current, ...patch };
15755
+ return ensureXmlHeader(
15756
+ `<body><Email version="1.1"><smtpServer>${xmlEscape(merged.smtpServer)}</smtpServer><userName>${xmlEscape(merged.userName)}</userName><password>${xmlEscape(merged.password)}</password><address1>${xmlEscape(merged.address1)}</address1><address2>${xmlEscape(merged.address2)}</address2><address3>${xmlEscape(merged.address3)}</address3><smtpPort>${merged.smtpPort}</smtpPort><sendNickname>${xmlEscape(merged.sendNickname)}</sendNickname><attachment>${merged.attachment}</attachment><attachmentType>${merged.attachmentType}</attachmentType><textType>${merged.textType}</textType><ssl>${merged.ssl}</ssl><interval>${merged.interval}</interval></Email></body>`
15757
+ );
15758
+ }
15759
+ function parseEmailTaskFromXml(xml) {
15760
+ const channelId = parseNumberSafe(getXmlText(xml, "channelId")) ?? 0;
15761
+ const enable = parseInt01(getXmlText(xml, "enable")) ?? 0;
15762
+ const items = [];
15763
+ const itemRe = /<item>([\s\S]*?)<\/item>/g;
15764
+ let match;
15765
+ while ((match = itemRe.exec(xml)) !== null) {
15766
+ const block = match[1] ?? "";
15767
+ items.push({
15768
+ type: getXmlText(block, "type") ?? "none",
15769
+ valueTable: getXmlText(block, "valueTable") ?? ""
15770
+ });
15771
+ }
15772
+ return { channelId, enable, typeScheduleList: items };
15773
+ }
15774
+ function buildEmailScheduleValueTable(spec) {
15775
+ if (spec.kind === "always") return "1".repeat(168);
15776
+ const grid = new Array(168).fill("0");
15777
+ if (spec.kind === "never") return grid.join("");
15778
+ for (const w of spec.windows) {
15779
+ const startH = Math.max(0, Math.min(24, w.startHour));
15780
+ const endH = Math.max(startH, Math.min(24, w.endHour));
15781
+ for (const d of w.days) {
15782
+ if (d < 0 || d > 6) continue;
15783
+ for (let h = startH; h < endH; h++) {
15784
+ grid[d * 24 + h] = "1";
15785
+ }
15786
+ }
15787
+ }
15788
+ return grid.join("");
15789
+ }
15790
+ function buildSetEmailTaskXml(task) {
15791
+ const items = task.typeScheduleList.map(
15792
+ (item) => `<item><type>${xmlEscape(item.type)}</type><valueTable>${xmlEscape(item.valueTable)}</valueTable></item>`
15793
+ ).join("");
15794
+ return ensureXmlHeader(
15795
+ `<body><EmailTask version="1.1"><channelId>${task.channelId}</channelId><enable>${task.enable}</enable><typeScheduleList>${items}</typeScheduleList></EmailTask></body>`
15796
+ );
15797
+ }
15798
+
15799
+ // src/reolink/baichuan/utils/ntp.ts
15800
+ init_xml();
15801
+ var parseNumberSafe2 = (text) => {
15802
+ if (text === void 0) return void 0;
15803
+ const n = Number(text);
15804
+ return Number.isFinite(n) ? n : void 0;
15805
+ };
15806
+ var parseInt012 = (text) => {
15807
+ if (text === void 0) return void 0;
15808
+ return text.trim() === "1" ? 1 : 0;
15809
+ };
15810
+ function parseNtpConfigFromXml(xml) {
15811
+ return {
15812
+ enable: parseInt012(getXmlText(xml, "enable")) ?? 0,
15813
+ server: getXmlText(xml, "server") ?? "",
15814
+ synchronizeInterval: parseNumberSafe2(getXmlText(xml, "synchronizeInterval")) ?? 1440,
15815
+ port: parseNumberSafe2(getXmlText(xml, "port")) ?? 123
15816
+ };
15817
+ }
15818
+ function buildSetNtpXml(current, patch) {
15819
+ const merged = { ...current, ...patch };
15820
+ return ensureXmlHeader(
15821
+ `<body><Ntp version="1.1"><enable>${merged.enable}</enable><server>${xmlEscape(merged.server)}</server><synchronizeInterval>${merged.synchronizeInterval}</synchronizeInterval><port>${merged.port}</port></Ntp></body>`
15822
+ );
15823
+ }
15824
+
15825
+ // src/reolink/baichuan/utils/dst.ts
15826
+ init_xml();
15827
+ var parseNumberSafe3 = (text) => {
15828
+ if (text === void 0) return void 0;
15829
+ const n = Number(text);
15830
+ return Number.isFinite(n) ? n : void 0;
15831
+ };
15832
+ var parseInt013 = (text) => {
15833
+ if (text === void 0) return void 0;
15834
+ return text.trim() === "1" ? 1 : 0;
15835
+ };
15836
+ var VALID_WEEKDAYS = [
15837
+ "Sunday",
15838
+ "Monday",
15839
+ "Tuesday",
15840
+ "Wednesday",
15841
+ "Thursday",
15842
+ "Friday",
15843
+ "Saturday"
15844
+ ];
15845
+ var normalizeWeekday = (text) => {
15846
+ const t = (text ?? "").trim();
15847
+ return VALID_WEEKDAYS.includes(t) ? t : "Sunday";
15848
+ };
15849
+ function parseDstConfigFromXml(xml) {
15850
+ const cfg = {
15851
+ enable: parseInt013(getXmlText(xml, "enable")) ?? 0,
15852
+ offset: parseNumberSafe3(getXmlText(xml, "offset")) ?? 1,
15853
+ startMonth: parseNumberSafe3(getXmlText(xml, "startMonth")) ?? 3,
15854
+ startWeekIndex: parseNumberSafe3(getXmlText(xml, "startWeekIndex")) ?? 5,
15855
+ startWeekday: normalizeWeekday(getXmlText(xml, "startWeekday")),
15856
+ startHour: parseNumberSafe3(getXmlText(xml, "startHour")) ?? 2,
15857
+ startMinute: parseNumberSafe3(getXmlText(xml, "startMinute")) ?? 0,
15858
+ startSecond: parseNumberSafe3(getXmlText(xml, "startSecond")) ?? 0,
15859
+ endMonth: parseNumberSafe3(getXmlText(xml, "endMonth")) ?? 10,
15860
+ endWeekIndex: parseNumberSafe3(getXmlText(xml, "endWeekIndex")) ?? 4,
15861
+ endWeekday: normalizeWeekday(getXmlText(xml, "endWeekday")),
15862
+ endHour: parseNumberSafe3(getXmlText(xml, "endHour")) ?? 3,
15863
+ endMinute: parseNumberSafe3(getXmlText(xml, "endMinute")) ?? 0,
15864
+ endSecond: parseNumberSafe3(getXmlText(xml, "endSecond")) ?? 0
15865
+ };
15866
+ const version = parseNumberSafe3(getXmlText(xml, "version"));
15867
+ if (version !== void 0) cfg.version = version;
15868
+ return cfg;
15869
+ }
15870
+ function buildSetDstXml(current, patch) {
15871
+ const merged = { ...current, ...patch };
15872
+ return ensureXmlHeader(
15873
+ `<body><Dst version="1.1"><enable>${merged.enable}</enable><offset>${merged.offset}</offset><startMonth>${merged.startMonth}</startMonth><startWeekIndex>${merged.startWeekIndex}</startWeekIndex><startWeekday>${xmlEscape(merged.startWeekday)}</startWeekday><startHour>${merged.startHour}</startHour><startMinute>${merged.startMinute}</startMinute><startSecond>${merged.startSecond}</startSecond><endMonth>${merged.endMonth}</endMonth><endWeekIndex>${merged.endWeekIndex}</endWeekIndex><endWeekday>${xmlEscape(merged.endWeekday)}</endWeekday><endHour>${merged.endHour}</endHour><endMinute>${merged.endMinute}</endMinute><endSecond>${merged.endSecond}</endSecond></Dst></body>`
15874
+ );
15875
+ }
15876
+
15877
+ // src/reolink/baichuan/utils/autoReboot.ts
15878
+ init_xml();
15879
+ var parseNumberSafe4 = (text) => {
15880
+ if (text === void 0) return void 0;
15881
+ const n = Number(text);
15882
+ return Number.isFinite(n) ? n : void 0;
15883
+ };
15884
+ var parseInt014 = (text) => {
15885
+ if (text === void 0) return void 0;
15886
+ return text.trim() === "1" ? 1 : 0;
15887
+ };
15888
+ var VALID_WEEKDAYS2 = [
15889
+ "Sunday",
15890
+ "Monday",
15891
+ "Tuesday",
15892
+ "Wednesday",
15893
+ "Thursday",
15894
+ "Friday",
15895
+ "Saturday",
15896
+ "everyday"
15897
+ ];
15898
+ var normalizeWeekday2 = (text) => {
15899
+ const t = (text ?? "").trim();
15900
+ return VALID_WEEKDAYS2.includes(t) ? t : "Sunday";
15901
+ };
15902
+ function parseAutoRebootFromXml(xml) {
15903
+ return {
15904
+ enable: parseInt014(getXmlText(xml, "enable")) ?? 0,
15905
+ weekDay: normalizeWeekday2(getXmlText(xml, "weekDay")),
15906
+ hour: parseNumberSafe4(getXmlText(xml, "hour")) ?? 0,
15907
+ minute: parseNumberSafe4(getXmlText(xml, "minute")) ?? 0,
15908
+ second: parseNumberSafe4(getXmlText(xml, "second")) ?? 0
15909
+ };
15910
+ }
15911
+ function buildSetAutoRebootXml(current, patch) {
15912
+ const merged = { ...current, ...patch };
15913
+ return ensureXmlHeader(
15914
+ `<body><AutoReboot version="1.1"><enable>${merged.enable}</enable><weekDay>${xmlEscape(merged.weekDay)}</weekDay><hour>${merged.hour}</hour><minute>${merged.minute}</minute><second>${merged.second}</second></AutoReboot></body>`
15915
+ );
15916
+ }
15917
+
15918
+ // src/reolink/baichuan/utils/systemGeneral.ts
15919
+ init_xml();
15920
+ var parseNumberSafe5 = (text) => {
15921
+ if (text === void 0) return void 0;
15922
+ const n = Number(text);
15923
+ return Number.isFinite(n) ? n : void 0;
15924
+ };
15925
+ var parseInt015 = (text) => {
15926
+ if (text === void 0) return void 0;
15927
+ return text.trim() === "1" ? 1 : 0;
15928
+ };
15929
+ var VALID_OSD_FORMATS = ["DMY", "MDY", "YMD"];
15930
+ var normalizeOsdFormat = (text) => {
15931
+ const t = (text ?? "").trim();
15932
+ return VALID_OSD_FORMATS.includes(t) ? t : "YMD";
15933
+ };
15934
+ function parseSystemGeneralFromXml(xml) {
15935
+ return {
15936
+ timeZone: parseNumberSafe5(getXmlText(xml, "timeZone")) ?? 0,
15937
+ osdFormat: normalizeOsdFormat(getXmlText(xml, "osdFormat")),
15938
+ year: parseNumberSafe5(getXmlText(xml, "year")) ?? 0,
15939
+ month: parseNumberSafe5(getXmlText(xml, "month")) ?? 0,
15940
+ day: parseNumberSafe5(getXmlText(xml, "day")) ?? 0,
15941
+ hour: parseNumberSafe5(getXmlText(xml, "hour")) ?? 0,
15942
+ minute: parseNumberSafe5(getXmlText(xml, "minute")) ?? 0,
15943
+ second: parseNumberSafe5(getXmlText(xml, "second")) ?? 0,
15944
+ deviceId: parseNumberSafe5(getXmlText(xml, "deviceId")) ?? 0,
15945
+ timeFormat: parseInt015(getXmlText(xml, "timeFormat")) ?? 0,
15946
+ language: getXmlText(xml, "language") ?? "English",
15947
+ deviceName: getXmlText(xml, "deviceName") ?? "",
15948
+ loginLock: parseInt015(getXmlText(xml, "loginLock")) ?? 0,
15949
+ lockTime: parseNumberSafe5(getXmlText(xml, "lockTime")) ?? 0,
15950
+ allowedTimes: parseNumberSafe5(getXmlText(xml, "allowedTimes")) ?? 0,
15951
+ isDst: parseInt015(getXmlText(xml, "isDst")) ?? 0
15952
+ };
15953
+ }
15954
+ function buildSetSystemGeneralXml(patch) {
15955
+ const parts = [];
15956
+ const isDeviceNameOnly = patch.deviceName !== void 0 && patch.timeZone === void 0 && patch.osdFormat === void 0 && patch.timeFormat === void 0 && patch.language === void 0 && patch.loginLock === void 0 && patch.lockTime === void 0 && patch.allowedTimes === void 0 && patch.manualTime === void 0;
15957
+ if (isDeviceNameOnly) {
15958
+ parts.push("<year>0</year>");
15959
+ parts.push(`<deviceName>${xmlEscape(patch.deviceName)}</deviceName>`);
15960
+ parts.push("<deviceNameOnly>1</deviceNameOnly>");
15961
+ } else if (patch.manualTime !== void 0) {
15962
+ const mt = patch.manualTime;
15963
+ if (patch.timeZone !== void 0)
15964
+ parts.push(`<timeZone>${patch.timeZone}</timeZone>`);
15965
+ if (patch.osdFormat !== void 0)
15966
+ parts.push(`<osdFormat>${patch.osdFormat}</osdFormat>`);
15967
+ parts.push(`<year>${mt.year}</year>`);
15968
+ parts.push(`<month>${mt.month}</month>`);
15969
+ parts.push(`<day>${mt.day}</day>`);
15970
+ parts.push(`<hour>${mt.hour}</hour>`);
15971
+ parts.push(`<minute>${mt.minute}</minute>`);
15972
+ parts.push(`<second>${mt.second}</second>`);
15973
+ if (patch.timeFormat !== void 0)
15974
+ parts.push(`<timeFormat>${patch.timeFormat}</timeFormat>`);
15975
+ if (patch.language !== void 0)
15976
+ parts.push(`<language>${xmlEscape(patch.language)}</language>`);
15977
+ if (patch.deviceName !== void 0)
15978
+ parts.push(`<deviceName>${xmlEscape(patch.deviceName)}</deviceName>`);
15979
+ if (patch.loginLock !== void 0)
15980
+ parts.push(`<loginLock>${patch.loginLock}</loginLock>`);
15981
+ if (patch.lockTime !== void 0)
15982
+ parts.push(`<lockTime>${patch.lockTime}</lockTime>`);
15983
+ if (patch.allowedTimes !== void 0)
15984
+ parts.push(`<allowedTimes>${patch.allowedTimes}</allowedTimes>`);
15985
+ } else {
15986
+ if (patch.timeZone !== void 0)
15987
+ parts.push(`<timeZone>${patch.timeZone}</timeZone>`);
15988
+ if (patch.osdFormat !== void 0)
15989
+ parts.push(`<osdFormat>${patch.osdFormat}</osdFormat>`);
15990
+ if (patch.timeFormat !== void 0)
15991
+ parts.push(`<timeFormat>${patch.timeFormat}</timeFormat>`);
15992
+ if (patch.language !== void 0)
15993
+ parts.push(`<language>${xmlEscape(patch.language)}</language>`);
15994
+ if (patch.loginLock !== void 0)
15995
+ parts.push(`<loginLock>${patch.loginLock}</loginLock>`);
15996
+ if (patch.lockTime !== void 0)
15997
+ parts.push(`<lockTime>${patch.lockTime}</lockTime>`);
15998
+ if (patch.allowedTimes !== void 0)
15999
+ parts.push(`<allowedTimes>${patch.allowedTimes}</allowedTimes>`);
16000
+ parts.push("<year>0</year>");
16001
+ }
16002
+ return ensureXmlHeader(
16003
+ `<body><SystemGeneral version="1.1">${parts.join("")}</SystemGeneral></body>`
16004
+ );
16005
+ }
16006
+
15693
16007
  // src/reolink/baichuan/ReolinkBaichuanApi.ts
15694
16008
  var import_jimp = require("jimp");
15695
16009
  init_ReolinkCgiApi();
@@ -27713,7 +28027,26 @@ ${xml}`
27713
28027
  */
27714
28028
  async getEncOptions(channel, options) {
27715
28029
  const list = await this.getStreamInfoList(channel, options);
27716
- return buildEncOptions(list, channel);
28030
+ const built = buildEncOptions(list, channel);
28031
+ try {
28032
+ const enc = await this.getEnc(channel, options);
28033
+ const compression = enc.body?.Compression;
28034
+ const mergeGop = (target, stream) => {
28035
+ if (!target || !stream) return;
28036
+ const g = stream.gop;
28037
+ if (!g) return;
28038
+ target.gopRange = {
28039
+ ...g.cur !== void 0 ? { cur: g.cur } : {},
28040
+ ...g.min !== void 0 ? { min: g.min } : {},
28041
+ ...g.max !== void 0 ? { max: g.max } : {}
28042
+ };
28043
+ };
28044
+ mergeGop(built.mainStream, compression?.mainStream);
28045
+ mergeGop(built.subStream, compression?.subStream);
28046
+ mergeGop(built.thirdStream, compression?.thirdStream);
28047
+ } catch {
28048
+ }
28049
+ return built;
27717
28050
  }
27718
28051
  /**
27719
28052
  * Read the camera's `<VersionInfo>` block (cmd_id=80). Returns the
@@ -27818,7 +28151,279 @@ ${xml}`
27818
28151
  ...channel != null ? { channel } : {},
27819
28152
  ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
27820
28153
  });
27821
- return parseXmlFragmentToJson(xml);
28154
+ return parseEmailTaskFromXml(xml);
28155
+ }
28156
+ /**
28157
+ * SetEmailTask via Baichuan (cmdId=216). Updates the email alarm schedule
28158
+ * (per-event-type 7×24 valueTable + master enable).
28159
+ *
28160
+ * Reolink expects the FULL `typeScheduleList` — pass the array from a prior
28161
+ * GET and only flip the entries you care about. Slots you don't track must
28162
+ * be sent back unchanged to avoid the camera dropping them.
28163
+ */
28164
+ async setEmailTask(channel, task, options) {
28165
+ const ch = this.normalizeChannel(channel);
28166
+ const payloadXml = buildSetEmailTaskXml({ ...task, channelId: ch });
28167
+ await this.sendXml({
28168
+ cmdId: BC_CMD_ID_SET_EMAIL_TASK,
28169
+ channel: ch,
28170
+ payloadXml,
28171
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
28172
+ });
28173
+ }
28174
+ /**
28175
+ * Convenience wrapper that patches the schedule of one or more trigger
28176
+ * types on the camera's EmailTask without touching the others.
28177
+ *
28178
+ * Pass a high-level schedule spec (`always` / `never` / explicit windows)
28179
+ * and the trigger types it should apply to. The method:
28180
+ *
28181
+ * 1. Reads the current EmailTask via GET (so we keep every existing slot).
28182
+ * 2. Builds the new `valueTable` once from `schedule`.
28183
+ * 3. Replaces the `valueTable` of every matching `type` in the list.
28184
+ * 4. Appends entries for any requested type not already present.
28185
+ * 5. Writes the merged list back via SET.
28186
+ *
28187
+ * Returns the list of types that were actually touched.
28188
+ */
28189
+ async patchEmailSchedule(channel, spec, options) {
28190
+ const current = await this.getEmailTask(channel, options);
28191
+ const newValueTable = buildEmailScheduleValueTable(spec.schedule);
28192
+ const targetSet = new Set(spec.types);
28193
+ const touched = [];
28194
+ const updatedList = current.typeScheduleList.map((item) => {
28195
+ if (targetSet.has(item.type)) {
28196
+ touched.push(item.type);
28197
+ return { ...item, valueTable: newValueTable };
28198
+ }
28199
+ return item;
28200
+ });
28201
+ for (const t of spec.types) {
28202
+ if (!current.typeScheduleList.some((item) => item.type === t)) {
28203
+ updatedList.push({ type: t, valueTable: newValueTable });
28204
+ touched.push(t);
28205
+ }
28206
+ }
28207
+ await this.setEmailTask(
28208
+ channel,
28209
+ {
28210
+ channelId: current.channelId,
28211
+ enable: spec.enable ?? current.enable,
28212
+ typeScheduleList: updatedList
28213
+ },
28214
+ options
28215
+ );
28216
+ return { touchedTypes: touched };
28217
+ }
28218
+ // ====================================================================
28219
+ // Email server (cmdId 42/43/141), NTP (38/39), DST (106/107),
28220
+ // SystemGeneral SET (105), AutoReboot (100/101).
28221
+ // Schemas derived from Reolink Client pcap (2026-05-16).
28222
+ // ====================================================================
28223
+ /**
28224
+ * Read the SMTP email configuration (cmdId=42). Returns the full `<Email>`
28225
+ * block including capability hints (`senderMaxLen`, `pwdMaxLen`,
28226
+ * `emailAttachAbility`).
28227
+ */
28228
+ async getEmail(options) {
28229
+ const xml = await this.sendXml({
28230
+ cmdId: BC_CMD_ID_GET_EMAIL,
28231
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
28232
+ });
28233
+ return parseEmailConfigFromXml(xml);
28234
+ }
28235
+ /**
28236
+ * Patch the SMTP email configuration (cmdId=43). Reads the current config
28237
+ * first then merges the patch — Reolink rejects partial `<Email>` blocks.
28238
+ */
28239
+ async setEmail(patch, options) {
28240
+ const current = await this.getEmail(options);
28241
+ const payloadXml = buildSetEmailXml(current, patch);
28242
+ await this.sendXml({
28243
+ cmdId: BC_CMD_ID_SET_EMAIL,
28244
+ payloadXml,
28245
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
28246
+ });
28247
+ }
28248
+ /**
28249
+ * Send a test email using either the current config or an override patch
28250
+ * (cmdId=141). Returns true when the camera reports 200 (test succeeded),
28251
+ * false when it reports 482 (test failed — server unreachable / bad creds).
28252
+ * Other non-200 codes propagate as exceptions via `sendXml`.
28253
+ */
28254
+ async testEmail(patch, options) {
28255
+ const current = await this.getEmail(options);
28256
+ const payloadXml = buildSetEmailXml(current, patch ?? {});
28257
+ const timeoutMs = options?.timeoutMs ?? 6e4;
28258
+ try {
28259
+ await this.sendXml({
28260
+ cmdId: BC_CMD_ID_TEST_EMAIL,
28261
+ payloadXml,
28262
+ timeoutMs
28263
+ });
28264
+ return true;
28265
+ } catch (err) {
28266
+ const msg = err instanceof Error ? err.message : String(err);
28267
+ if (msg.includes("response_code 482") || msg.includes("response_code=482")) {
28268
+ return false;
28269
+ }
28270
+ throw err;
28271
+ }
28272
+ }
28273
+ /**
28274
+ * Read the NTP server configuration (cmdId=38).
28275
+ */
28276
+ async getNtp(options) {
28277
+ const xml = await this.sendXml({
28278
+ cmdId: BC_CMD_ID_GET_NTP,
28279
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
28280
+ });
28281
+ return parseNtpConfigFromXml(xml);
28282
+ }
28283
+ /**
28284
+ * Patch the NTP server configuration (cmdId=39). Reads the current state
28285
+ * first and merges the patch — Reolink rejects partial `<Ntp>` blocks.
28286
+ */
28287
+ async setNtp(patch, options) {
28288
+ const current = await this.getNtp(options);
28289
+ const payloadXml = buildSetNtpXml(current, patch);
28290
+ await this.sendXml({
28291
+ cmdId: BC_CMD_ID_SET_NTP,
28292
+ payloadXml,
28293
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
28294
+ });
28295
+ }
28296
+ /**
28297
+ * Patch SystemGeneral (cmdId=105). Supports partial payloads: include only
28298
+ * the fields you want to change. By default the builder emits `<year>0</year>`
28299
+ * as the "do not set manual clock" marker; pass `manualTime` to actually
28300
+ * set the date/time. Setting only `deviceName` automatically uses the
28301
+ * Reolink Client's `deviceNameOnly=1` shape.
28302
+ */
28303
+ async setSystemGeneral(patch, options) {
28304
+ const payloadXml = buildSetSystemGeneralXml(patch);
28305
+ await this.sendXml({
28306
+ cmdId: BC_CMD_ID_SET_SYSTEM_GENERAL,
28307
+ payloadXml,
28308
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
28309
+ });
28310
+ }
28311
+ /**
28312
+ * Read the Daylight Saving Time configuration (cmdId=106).
28313
+ */
28314
+ async getDst(options) {
28315
+ const xml = await this.sendXml({
28316
+ cmdId: BC_CMD_ID_GET_DST,
28317
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
28318
+ });
28319
+ return parseDstConfigFromXml(xml);
28320
+ }
28321
+ /**
28322
+ * Patch the DST configuration (cmdId=107). Reads the current state first
28323
+ * and merges the patch.
28324
+ */
28325
+ async setDst(patch, options) {
28326
+ const current = await this.getDst(options);
28327
+ const payloadXml = buildSetDstXml(current, patch);
28328
+ await this.sendXml({
28329
+ cmdId: BC_CMD_ID_SET_DST,
28330
+ payloadXml,
28331
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
28332
+ });
28333
+ }
28334
+ /**
28335
+ * Read the auto-reboot schedule (cmdId=101).
28336
+ */
28337
+ async getAutoReboot(options) {
28338
+ const xml = await this.sendXml({
28339
+ cmdId: BC_CMD_ID_GET_AUTO_REBOOT,
28340
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
28341
+ });
28342
+ return parseAutoRebootFromXml(xml);
28343
+ }
28344
+ /**
28345
+ * Patch the auto-reboot schedule (cmdId=100).
28346
+ */
28347
+ async setAutoReboot(patch, options) {
28348
+ const current = await this.getAutoReboot(options);
28349
+ const payloadXml = buildSetAutoRebootXml(current, patch);
28350
+ await this.sendXml({
28351
+ cmdId: BC_CMD_ID_SET_AUTO_REBOOT,
28352
+ payloadXml,
28353
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
28354
+ });
28355
+ }
28356
+ /**
28357
+ * High-level helper that configures the camera to deliver motion alerts via
28358
+ * SMTP to the local nodelink manager. Orchestrates `setEmail` + `setEmailTask`
28359
+ * in a single call so UI code can offer "auto-configure" without juggling
28360
+ * the underlying commands.
28361
+ *
28362
+ * Pass `runTest: true` to also send a test email (cmdId=141). Returns a
28363
+ * structured result describing each leg of the flow so the caller can show
28364
+ * granular feedback.
28365
+ *
28366
+ * @param params Auto-configuration parameters
28367
+ * @param channel Logical channel (default 0). Used for the EmailTask SET.
28368
+ */
28369
+ async setupEmailPushToManager(params, channel, options) {
28370
+ const port = params.managerPort ?? 2525;
28371
+ const domain = params.domain ?? "nodelink.local";
28372
+ const recipient = `${params.recipientLocalPart}@${domain}`;
28373
+ const triggers = params.triggerTypes ?? ["MD", "people", "vehicle"];
28374
+ const attachmentType = params.attachmentType ?? "picture";
28375
+ const interval = params.interval ?? 30;
28376
+ const emailPatch = {
28377
+ smtpServer: params.managerHost,
28378
+ smtpPort: port,
28379
+ userName: params.authUsername ?? recipient,
28380
+ password: params.authPassword ?? "",
28381
+ address1: recipient,
28382
+ address2: "",
28383
+ address3: "",
28384
+ sendNickname: params.sendNickname ?? params.recipientLocalPart,
28385
+ attachment: attachmentType === "none" ? 0 : 1,
28386
+ attachmentType,
28387
+ textType: "withText",
28388
+ ssl: 0,
28389
+ interval
28390
+ };
28391
+ await this.setEmail(emailPatch, options);
28392
+ const fullWeekOn = "1".repeat(168);
28393
+ const current = await this.getEmailTask(channel, options);
28394
+ const triggerSet = new Set(triggers);
28395
+ const touched = [];
28396
+ const updatedList = current.typeScheduleList.map((item) => {
28397
+ if (triggerSet.has(item.type)) {
28398
+ touched.push(item.type);
28399
+ return { ...item, valueTable: fullWeekOn };
28400
+ }
28401
+ return item;
28402
+ });
28403
+ for (const t of triggers) {
28404
+ if (!current.typeScheduleList.some((item) => item.type === t)) {
28405
+ updatedList.push({ type: t, valueTable: fullWeekOn });
28406
+ touched.push(t);
28407
+ }
28408
+ }
28409
+ await this.setEmailTask(
28410
+ channel,
28411
+ {
28412
+ channelId: current.channelId,
28413
+ enable: 1,
28414
+ typeScheduleList: updatedList
28415
+ },
28416
+ options
28417
+ );
28418
+ const result = {
28419
+ setEmail: { applied: true },
28420
+ setEmailTask: { applied: true, touchedTypes: touched }
28421
+ };
28422
+ if (params.runTest) {
28423
+ const ok = await this.testEmail(emailPatch, options);
28424
+ result.testEmail = { success: ok };
28425
+ }
28426
+ return result;
27822
28427
  }
27823
28428
  /**
27824
28429
  * Get siren-on-motion state via AudioTask (cmdId=232).
@@ -28087,7 +28692,7 @@ ${xml}`
28087
28692
  cmdId: BC_CMD_ID_GET_SYSTEM_GENERAL,
28088
28693
  ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
28089
28694
  });
28090
- return parseXmlFragmentToJson(xml);
28695
+ return parseSystemGeneralFromXml(xml);
28091
28696
  }
28092
28697
  /**
28093
28698
  * Get device support/capability flags.