@apocaliss92/nodelink-js 0.4.8 → 0.4.11

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_GET_RECORD_CFG, BC_CMD_ID_GET_ABILITY_SUPPORT, BC_CMD_ID_GET_FTP_TASK, 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_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_GET_RECORD_CFG, BC_CMD_ID_GET_ABILITY_SUPPORT, BC_CMD_ID_GET_FTP_TASK, 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;
64
64
  var init_constants = __esm({
65
65
  "src/protocol/constants.ts"() {
66
66
  "use strict";
@@ -154,6 +154,17 @@ var init_constants = __esm({
154
154
  BC_CMD_ID_SET_AI_CFG = 300;
155
155
  BC_CMD_ID_GET_SIREN_STATUS = 547;
156
156
  BC_CMD_ID_SET_AUDIO_TASK = 231;
157
+ BC_CMD_ID_SET_VIDEO_INPUT = 25;
158
+ BC_CMD_ID_SET_DAY_NIGHT_THRESHOLD = 297;
159
+ BC_CMD_ID_GET_ENC = 56;
160
+ BC_CMD_ID_SET_ENC = 57;
161
+ BC_CMD_ID_GET_PRIVACY_MASK = 52;
162
+ BC_CMD_ID_SET_PRIVACY_MASK = 53;
163
+ BC_CMD_ID_SET_AI_DENOISE = 440;
164
+ BC_CMD_ID_SET_LED_STATE = 209;
165
+ BC_CMD_ID_SET_AUDIO_CFG = 265;
166
+ BC_CMD_ID_GET_AUTO_FOCUS = 224;
167
+ BC_CMD_ID_SET_AUTO_FOCUS = 225;
157
168
  BC_CMD_ID_CMD_123 = 123;
158
169
  BC_CMD_ID_CMD_209 = 209;
159
170
  BC_CMD_ID_CMD_265 = 265;
@@ -2691,6 +2702,59 @@ function buildFloodlightManualXml(channelId, status, durationSeconds = 180) {
2691
2702
  </FloodlightManual>
2692
2703
  </body>`;
2693
2704
  }
2705
+ function ensureXmlHeader(xml) {
2706
+ const trimmed = xml.trimStart();
2707
+ if (trimmed.startsWith("<?xml")) return xml;
2708
+ return `${XML_HEADER}
2709
+ ${xml}`;
2710
+ }
2711
+ function applyXmlTagPatch(xml, tag, value) {
2712
+ if (value === void 0) return xml;
2713
+ const v = typeof value === "boolean" ? value ? 1 : 0 : value;
2714
+ const re = new RegExp(`<${tag}>[^<]*</${tag}>`);
2715
+ return xml.replace(re, `<${tag}>${v}</${tag}>`);
2716
+ }
2717
+ function patchNestedTag(xml, parent, child, value) {
2718
+ if (value === void 0) return xml;
2719
+ const v = typeof value === "boolean" ? value ? 1 : 0 : value;
2720
+ const re = new RegExp(
2721
+ `(<${parent}[^>]*>[\\s\\S]*?<${child}>)[^<]*(</${child}>[\\s\\S]*?</${parent}>)`
2722
+ );
2723
+ return xml.replace(re, `$1${v}$2`);
2724
+ }
2725
+ function applyStreamPatch(xml, streamTag, patch) {
2726
+ if (!patch) return xml;
2727
+ const re = new RegExp(
2728
+ `(<${streamTag}[^>]*>)([\\s\\S]*?)(</${streamTag}>)`
2729
+ );
2730
+ return xml.replace(re, (_match, open, body, close) => {
2731
+ let next = body;
2732
+ if (patch.bitRate !== void 0) {
2733
+ next = applyXmlTagPatch(next, "bitRate", patch.bitRate);
2734
+ }
2735
+ if (patch.frameRate !== void 0) {
2736
+ next = applyXmlTagPatch(next, "frameRate", patch.frameRate);
2737
+ next = applyXmlTagPatch(next, "frame", patch.frameRate);
2738
+ }
2739
+ if (patch.videoEncType !== void 0) {
2740
+ const intVal = patch.videoEncType === "h265" ? 1 : 0;
2741
+ next = applyXmlTagPatch(next, "videoEncType", intVal);
2742
+ }
2743
+ return `${open}${next}${close}`;
2744
+ });
2745
+ }
2746
+ function normalizeDayNightMode(input) {
2747
+ const stripped = String(input).replace(/&/g, "And");
2748
+ if (!stripped) return stripped;
2749
+ const first = stripped[0];
2750
+ if (first === void 0) return stripped;
2751
+ return first.toLowerCase() + stripped.slice(1);
2752
+ }
2753
+ function normalizeOpenClose(input) {
2754
+ const v = String(input).toLowerCase();
2755
+ if (v === "on" || v === "open" || v === "1" || v === "true") return "open";
2756
+ return "close";
2757
+ }
2694
2758
  function buildAbilityInfoExtensionXml(username) {
2695
2759
  return `<?xml version="1.0" encoding="UTF-8" ?>
2696
2760
  <Extension version="1.1">
@@ -2698,9 +2762,11 @@ function buildAbilityInfoExtensionXml(username) {
2698
2762
  <token>system, streaming, PTZ, IO, security, replay, disk, network, alarm, record, video, image</token>
2699
2763
  </Extension>`;
2700
2764
  }
2765
+ var XML_HEADER;
2701
2766
  var init_xml = __esm({
2702
2767
  "src/protocol/xml.ts"() {
2703
2768
  "use strict";
2769
+ XML_HEADER = `<?xml version="1.0" encoding="UTF-8" ?>`;
2704
2770
  }
2705
2771
  });
2706
2772
 
@@ -3520,6 +3586,137 @@ var init_ReolinkCgiApi = __esm({
3520
3586
  const param = channel == null ? {} : { channel };
3521
3587
  return await this.call("GetPtzPreset", param, 1);
3522
3588
  }
3589
+ // ── Isp / Image (colour, flip, day-night, exposure) ──────────────
3590
+ async GetIsp(channel) {
3591
+ const param = channel == null ? {} : { channel };
3592
+ return await this.call("GetIsp", param, 1);
3593
+ }
3594
+ async SetIsp(isp) {
3595
+ return await this.call("SetIsp", isp, 0);
3596
+ }
3597
+ async GetImage(channel) {
3598
+ const param = channel == null ? {} : { channel };
3599
+ return await this.call("GetImage", param, 1);
3600
+ }
3601
+ async SetImage(image) {
3602
+ return await this.call("SetImage", image, 0);
3603
+ }
3604
+ // ── AudioCfg (mute / volume) ─────────────────────────────────────
3605
+ async GetAudioCfg(channel) {
3606
+ const param = channel == null ? {} : { channel };
3607
+ return await this.call("GetAudioCfg", param, 1);
3608
+ }
3609
+ async SetAudioCfg(audio) {
3610
+ return await this.call("SetAudioCfg", audio, 0);
3611
+ }
3612
+ // ── Enc setter (Get already exists above) ────────────────────────
3613
+ async SetEnc(enc) {
3614
+ return await this.call("SetEnc", enc, 0);
3615
+ }
3616
+ // ── MdAlarm (motion detection sensitivity / regions) ─────────────
3617
+ async GetMdAlarm(channel) {
3618
+ const param = channel == null ? {} : { channel };
3619
+ return await this.call("GetMdAlarm", param, 1);
3620
+ }
3621
+ async SetMdAlarm(md) {
3622
+ return await this.call("SetMdAlarm", md, 0);
3623
+ }
3624
+ // ── IrLights ─────────────────────────────────────────────────────
3625
+ async GetIrLights(channel) {
3626
+ const param = channel == null ? {} : { channel };
3627
+ return await this.call("GetIrLights", param, 1);
3628
+ }
3629
+ async SetIrLights(ir) {
3630
+ return await this.call("SetIrLights", ir, 0);
3631
+ }
3632
+ // ── AiCfg (smart-detection enable + class filter) ────────────────
3633
+ async GetAiCfg(channel) {
3634
+ const param = channel == null ? {} : { channel };
3635
+ return await this.call("GetAiCfg", param, 1);
3636
+ }
3637
+ async SetAiCfg(ai) {
3638
+ return await this.call("SetAiCfg", ai, 0);
3639
+ }
3640
+ // ── Mask (privacy-mask zones) ────────────────────────────────────
3641
+ async GetMask(channel) {
3642
+ return await this.call("GetMask", { channel }, 1);
3643
+ }
3644
+ async SetMask(mask) {
3645
+ return await this.call("SetMask", mask, 0);
3646
+ }
3647
+ // ── AudioNoise (input noise reduction) ───────────────────────────
3648
+ async GetAudioNoise(channel) {
3649
+ return await this.call("GetAudioNoise", { channel }, 1);
3650
+ }
3651
+ async SetAudioNoise(noise) {
3652
+ return await this.call("SetAudioNoise", noise, 0);
3653
+ }
3654
+ // ── Rec / RecV20 (recording schedule) ────────────────────────────
3655
+ async GetRec(channel) {
3656
+ return await this.call("GetRec", { channel }, 1);
3657
+ }
3658
+ async SetRec(rec) {
3659
+ return await this.call("SetRec", rec, 0);
3660
+ }
3661
+ /** Newer firmwares advertise `GetRecV20` / `SetRecV20` with the
3662
+ * weekly-schedule `table` field. Same payload shape as `Rec`. */
3663
+ async GetRecV20(channel) {
3664
+ return await this.call("GetRecV20", { channel }, 1);
3665
+ }
3666
+ async SetRecV20(rec) {
3667
+ return await this.call("SetRecV20", rec, 0);
3668
+ }
3669
+ // ── Email (SMTP alert) ───────────────────────────────────────────
3670
+ async GetEmail(channel) {
3671
+ return await this.call("GetEmail", { channel }, 1);
3672
+ }
3673
+ async SetEmail(email) {
3674
+ return await this.call("SetEmail", email, 0);
3675
+ }
3676
+ /** V20 variant on newer firmwares with weekly-schedule `table`. */
3677
+ async GetEmailV20(channel) {
3678
+ return await this.call("GetEmailV20", { channel }, 1);
3679
+ }
3680
+ async SetEmailV20(email) {
3681
+ return await this.call("SetEmailV20", email, 0);
3682
+ }
3683
+ // ── Push (Reolink-cloud push notifications) ──────────────────────
3684
+ async GetPush(channel) {
3685
+ return await this.call("GetPush", { channel }, 1);
3686
+ }
3687
+ async SetPush(push) {
3688
+ return await this.call("SetPush", push, 0);
3689
+ }
3690
+ async GetPushV20(channel) {
3691
+ return await this.call("GetPushV20", { channel }, 1);
3692
+ }
3693
+ async SetPushV20(push) {
3694
+ return await this.call("SetPushV20", push, 0);
3695
+ }
3696
+ // ── AudioAlarm (siren-on-event) ──────────────────────────────────
3697
+ async GetAudioAlarm(channel) {
3698
+ return await this.call("GetAudioAlarm", { channel }, 1);
3699
+ }
3700
+ async SetAudioAlarm(audio) {
3701
+ return await this.call("SetAudioAlarm", audio, 0);
3702
+ }
3703
+ async SetAudioAlarmV20(audio) {
3704
+ return await this.call("SetAudioAlarmV20", audio, 0);
3705
+ }
3706
+ // ── AutoFocus (PTZ AF) ───────────────────────────────────────────
3707
+ async GetAutoFocus(channel) {
3708
+ return await this.call("GetAutoFocus", { channel }, 1);
3709
+ }
3710
+ async SetAutoFocus(af) {
3711
+ return await this.call("SetAutoFocus", af, 0);
3712
+ }
3713
+ // ── AiAlarm (per-class smart-detection thresholds) ───────────────
3714
+ async GetAiAlarm(channel, aiType) {
3715
+ return await this.call("GetAiAlarm", { channel, ai_type: aiType }, 1);
3716
+ }
3717
+ async SetAiAlarm(ai) {
3718
+ return await this.call("SetAiAlarm", ai, 0);
3719
+ }
3523
3720
  async GetAudioAlarmV20(channel) {
3524
3721
  const param = channel == null ? {} : { channel };
3525
3722
  return await this.call("GetAudioAlarmV20", param, 0);
@@ -25859,6 +26056,373 @@ ${xml}`
25859
26056
  await this.cgiApi.login();
25860
26057
  return await this.cgiApi.getAllChannelsEvents(options);
25861
26058
  }
26059
+ // ====================================================================
26060
+ // Native Baichuan tunable-settings setters
26061
+ //
26062
+ // Replace the CGI passthroughs above with on-wire Baichuan binary
26063
+ // calls. Mirrors the @http_cmd-decorated methods in reolink_aio's
26064
+ // baichuan.py — every command has a documented `cmd_id` (read) and
26065
+ // `cmd_id` (write) pair. The pattern is:
26066
+ //
26067
+ // 1. read XML via `sendXml({ cmdId: GET, channel })`
26068
+ // 2. patch fields via regex (camera firmware is XML-strict; using
26069
+ // the parser would force us to rebuild the document and risk
26070
+ // losing unmodified attributes / element order).
26071
+ // 3. write back via `sendXml({ cmdId: SET, channel, payloadXml })`
26072
+ //
26073
+ // All getters parse via `parseXmlFragmentToJson` so the consumer gets
26074
+ // a clean JSON object instead of XML.
26075
+ // ====================================================================
26076
+ /**
26077
+ * GetEnc via Baichuan (cmdId=56). Returns the `<Compression>` block:
26078
+ * per-stream `mainStream` / `subStream` / `thirdStream` with `audio`
26079
+ * flag, `width`, `height`, `frame` (NOT `frameRate`), `bitRate`,
26080
+ * `videoEncType` (0=h264, 1=h265), `encoderProfile`, `gop`. Mirrors
26081
+ * reolink_aio's `GetEnc` — note the wire payload wraps everything
26082
+ * in `Compression`, not `Enc`.
26083
+ */
26084
+ async getEnc(channel, options) {
26085
+ const xml = await this.sendPcapDerivedSettingsGetXml({
26086
+ cmdId: BC_CMD_ID_GET_ENC,
26087
+ ...channel != null ? { channel } : {},
26088
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
26089
+ });
26090
+ return parseXmlFragmentToJson(xml);
26091
+ }
26092
+ /**
26093
+ * SetEnc via Baichuan (cmdId=57). Read-modify-write — preserves
26094
+ * unspecified fields. Mirrors reolink_aio's `SetEnc`.
26095
+ *
26096
+ * @param channel - Channel number (0-based)
26097
+ * @param patch - Fields to update on `mainStream` and/or `subStream`,
26098
+ * plus a top-level `audio` toggle (0/1). Pass only what you want
26099
+ * to change.
26100
+ */
26101
+ async setEnc(channel, patch, options) {
26102
+ const ch = this.normalizeChannel(channel);
26103
+ let xml = await this.sendXml({
26104
+ cmdId: BC_CMD_ID_GET_ENC,
26105
+ channel: ch,
26106
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
26107
+ });
26108
+ if (patch.audio !== void 0) {
26109
+ xml = xml.replace(
26110
+ /<audio>[^<]*<\/audio>/g,
26111
+ `<audio>${patch.audio}</audio>`
26112
+ );
26113
+ }
26114
+ xml = applyStreamPatch(xml, "mainStream", patch.mainStream);
26115
+ xml = applyStreamPatch(xml, "subStream", patch.subStream);
26116
+ await this.sendXml({
26117
+ cmdId: BC_CMD_ID_SET_ENC,
26118
+ channel: ch,
26119
+ payloadXml: ensureXmlHeader(xml),
26120
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
26121
+ });
26122
+ }
26123
+ /**
26124
+ * SetImage via Baichuan (cmdId=25, read via cmdId=26). Patches the
26125
+ * `<VideoInput>` block: bright / contrast / saturation / hue /
26126
+ * sharpen. Mirrors reolink_aio's `SetImage`.
26127
+ */
26128
+ async setImage(channel, patch, options) {
26129
+ const ch = this.normalizeChannel(channel);
26130
+ let xml = await this.sendXml({
26131
+ cmdId: BC_CMD_ID_GET_VIDEO_INPUT,
26132
+ channel: ch,
26133
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
26134
+ });
26135
+ xml = applyXmlTagPatch(xml, "bright", patch.bright);
26136
+ xml = applyXmlTagPatch(xml, "contrast", patch.contrast);
26137
+ xml = applyXmlTagPatch(xml, "saturation", patch.saturation);
26138
+ xml = applyXmlTagPatch(xml, "hue", patch.hue);
26139
+ xml = applyXmlTagPatch(xml, "sharpen", patch.sharpen);
26140
+ await this.sendXml({
26141
+ cmdId: BC_CMD_ID_SET_VIDEO_INPUT,
26142
+ channel: ch,
26143
+ payloadXml: ensureXmlHeader(xml),
26144
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
26145
+ });
26146
+ }
26147
+ /**
26148
+ * SetIsp via Baichuan (cmdId=25 for image side, cmdId=297 for
26149
+ * dayNightThreshold). Patches the `<InputAdvanceCfg>` block:
26150
+ * `DayNight/mode`, `Exposure/mode`, `binning_mode`, `hdrSwitch`.
26151
+ * Mirrors reolink_aio's `SetIsp`.
26152
+ *
26153
+ * @param channel - Channel number (0-based)
26154
+ * @param patch - Fields to update. `dayNight` accepts the camera's
26155
+ * raw enum (`color`, `auto`, `blackAndWhite`, …) — pass it as the
26156
+ * camera reports it (PascalCase / dotted forms get normalized
26157
+ * server-side).
26158
+ */
26159
+ async setIsp(channel, patch, options) {
26160
+ const ch = this.normalizeChannel(channel);
26161
+ const timeoutOpts = options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {};
26162
+ const wantsImageWrite = patch.dayNight !== void 0 || patch.exposure !== void 0 || patch.binningMode !== void 0 || patch.hdr !== void 0;
26163
+ if (wantsImageWrite) {
26164
+ let xml = await this.sendXml({
26165
+ cmdId: BC_CMD_ID_GET_VIDEO_INPUT,
26166
+ channel: ch,
26167
+ ...timeoutOpts
26168
+ });
26169
+ if (patch.dayNight !== void 0) {
26170
+ const normalized = normalizeDayNightMode(patch.dayNight);
26171
+ xml = patchNestedTag(xml, "DayNight", "mode", normalized);
26172
+ }
26173
+ if (patch.exposure !== void 0) {
26174
+ xml = patchNestedTag(
26175
+ xml,
26176
+ "Exposure",
26177
+ "mode",
26178
+ patch.exposure.toLowerCase()
26179
+ );
26180
+ }
26181
+ if (patch.binningMode !== void 0) {
26182
+ xml = applyXmlTagPatch(xml, "binning_mode", patch.binningMode);
26183
+ }
26184
+ if (patch.hdr !== void 0) {
26185
+ xml = applyXmlTagPatch(xml, "hdrSwitch", patch.hdr);
26186
+ }
26187
+ await this.sendXml({
26188
+ cmdId: BC_CMD_ID_SET_VIDEO_INPUT,
26189
+ channel: ch,
26190
+ payloadXml: ensureXmlHeader(xml),
26191
+ ...timeoutOpts
26192
+ });
26193
+ }
26194
+ if (patch.dayNightThreshold !== void 0) {
26195
+ let xml = await this.sendXml({
26196
+ cmdId: BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD,
26197
+ channel: ch,
26198
+ ...timeoutOpts
26199
+ });
26200
+ xml = applyXmlTagPatch(xml, "cur", patch.dayNightThreshold);
26201
+ await this.sendXml({
26202
+ cmdId: BC_CMD_ID_SET_DAY_NIGHT_THRESHOLD,
26203
+ channel: ch,
26204
+ payloadXml: ensureXmlHeader(xml),
26205
+ ...timeoutOpts
26206
+ });
26207
+ }
26208
+ }
26209
+ /**
26210
+ * GetIsp via Baichuan (cmdId=26). Convenience alias of
26211
+ * `getVideoInput()` so callers that switched from CGI keep the
26212
+ * familiar name. Both return the merged VideoInput +
26213
+ * InputAdvanceCfg blob.
26214
+ */
26215
+ async getIsp(channel, options) {
26216
+ return this.getVideoInput(channel, options);
26217
+ }
26218
+ /** GetImage via Baichuan (cmdId=26). Same payload as `getIsp` —
26219
+ * Reolink merged VideoInput + InputAdvanceCfg under one cmdId. */
26220
+ async getImage(channel, options) {
26221
+ return this.getVideoInput(channel, options);
26222
+ }
26223
+ /**
26224
+ * GetIrLights via Baichuan (cmdId=208). Returns LedState block:
26225
+ * `IRLedBrightness`, `state` (ir on/off), `lightState` (status LED
26226
+ * open/close), `doorbellLightState`. Mirrors reolink_aio's
26227
+ * `get_status_led`.
26228
+ */
26229
+ async getIrLights(channel, options) {
26230
+ const xml = await this.sendPcapDerivedSettingsGetXml({
26231
+ cmdId: BC_CMD_ID_GET_LED_STATE,
26232
+ ...channel != null ? { channel } : {},
26233
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
26234
+ });
26235
+ return parseXmlFragmentToJson(xml);
26236
+ }
26237
+ /**
26238
+ * SetIrLights via Baichuan (cmdId=209, read via cmdId=208). Patches
26239
+ * IR LED + status LED + doorbell LED + IR brightness. Mirrors
26240
+ * reolink_aio's `set_status_led`.
26241
+ *
26242
+ * @param channel - Channel number (0-based)
26243
+ * @param patch - `irState` ("On" | "Off" | "Auto"), `lightState`
26244
+ * (status LED), `doorbellLightState`, `irBrightness` (0..255).
26245
+ * Camera-side accepts lowercase strings (`open`/`close`); the
26246
+ * helper normalizes from the friendly variants.
26247
+ */
26248
+ async setIrLights(channel, patch, options) {
26249
+ const ch = this.normalizeChannel(channel);
26250
+ const timeoutOpts = options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {};
26251
+ let xml = await this.sendXml({
26252
+ cmdId: BC_CMD_ID_GET_LED_STATE,
26253
+ channel: ch,
26254
+ ...timeoutOpts
26255
+ });
26256
+ if (patch.lightState !== void 0) {
26257
+ xml = applyXmlTagPatch(
26258
+ xml,
26259
+ "lightState",
26260
+ patch.lightState === "On" ? "open" : "close"
26261
+ );
26262
+ }
26263
+ if (patch.doorbellLightState !== void 0) {
26264
+ xml = applyXmlTagPatch(
26265
+ xml,
26266
+ "doorbellLightState",
26267
+ normalizeOpenClose(patch.doorbellLightState)
26268
+ );
26269
+ }
26270
+ if (patch.irState !== void 0) {
26271
+ const v = String(patch.irState);
26272
+ const out = v === "Off" ? "close" : v.toLowerCase();
26273
+ xml = applyXmlTagPatch(xml, "state", out);
26274
+ }
26275
+ if (patch.irBrightness !== void 0) {
26276
+ xml = applyXmlTagPatch(xml, "IRLedBrightness", patch.irBrightness);
26277
+ }
26278
+ await this.sendXml({
26279
+ cmdId: BC_CMD_ID_SET_LED_STATE,
26280
+ channel: ch,
26281
+ payloadXml: ensureXmlHeader(xml),
26282
+ ...timeoutOpts
26283
+ });
26284
+ }
26285
+ /**
26286
+ * SetAudioCfg via Baichuan (cmdId=265, read via cmdId=264). Patches
26287
+ * volume / talk-and-reply / visitor settings. Mirrors reolink_aio's
26288
+ * `SetAudioCfg`.
26289
+ */
26290
+ async setAudioCfg(channel, patch, options) {
26291
+ const ch = this.normalizeChannel(channel);
26292
+ const timeoutOpts = options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {};
26293
+ let xml = await this.sendXml({
26294
+ cmdId: BC_CMD_ID_GET_AUDIO_CFG,
26295
+ channel: ch,
26296
+ ...timeoutOpts
26297
+ });
26298
+ xml = applyXmlTagPatch(xml, "volume", patch.volume);
26299
+ xml = applyXmlTagPatch(
26300
+ xml,
26301
+ "talkAndReplyVolume",
26302
+ patch.talkAndReplyVolume
26303
+ );
26304
+ xml = applyXmlTagPatch(xml, "visitorVolume", patch.visitorVolume);
26305
+ xml = applyXmlTagPatch(xml, "visitorLoudspeaker", patch.visitorLoudspeaker);
26306
+ await this.sendXml({
26307
+ cmdId: BC_CMD_ID_SET_AUDIO_CFG,
26308
+ channel: ch,
26309
+ payloadXml: ensureXmlHeader(xml),
26310
+ ...timeoutOpts
26311
+ });
26312
+ }
26313
+ /**
26314
+ * GetMask (privacy mask) via Baichuan (cmdId=52). Returns the
26315
+ * `<Shelter>` block — `enable` flag + `shelterList`. Mirrors
26316
+ * reolink_aio's `GetMask`.
26317
+ */
26318
+ async getMask(channel, options) {
26319
+ const xml = await this.sendPcapDerivedSettingsGetXml({
26320
+ cmdId: BC_CMD_ID_GET_PRIVACY_MASK,
26321
+ ...channel != null ? { channel } : {},
26322
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
26323
+ });
26324
+ return parseXmlFragmentToJson(xml);
26325
+ }
26326
+ /**
26327
+ * SetMask (privacy mask) via Baichuan (cmdId=53, read via cmdId=52).
26328
+ * Toggles the `<Shelter><enable>` flag. Mirrors reolink_aio's
26329
+ * `SetMask` (which only touches enable too — shelter zone editing
26330
+ * goes through a separate flow).
26331
+ */
26332
+ async setMask(channel, patch, options) {
26333
+ const ch = this.normalizeChannel(channel);
26334
+ const timeoutOpts = options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {};
26335
+ let xml = await this.sendXml({
26336
+ cmdId: BC_CMD_ID_GET_PRIVACY_MASK,
26337
+ channel: ch,
26338
+ ...timeoutOpts
26339
+ });
26340
+ if (patch.enable !== void 0) {
26341
+ xml = applyXmlTagPatch(xml, "enable", patch.enable ? 1 : 0);
26342
+ }
26343
+ await this.sendXml({
26344
+ cmdId: BC_CMD_ID_SET_PRIVACY_MASK,
26345
+ channel: ch,
26346
+ payloadXml: ensureXmlHeader(xml),
26347
+ ...timeoutOpts
26348
+ });
26349
+ }
26350
+ /**
26351
+ * GetAudioNoise via Baichuan (cmdId=439). Reads `enable` + `level`
26352
+ * from the aiDenoise block. Mirrors reolink_aio's `GetAudioNoise`.
26353
+ *
26354
+ * Note: `getAiDenoise` already returns the same payload typed as
26355
+ * `AiDenoiseConfig`. This getter exists for naming parity with
26356
+ * reolink_aio + the reolink CGI.
26357
+ */
26358
+ async getAudioNoise(channel, options) {
26359
+ const xml = await this.sendPcapDerivedSettingsGetXml({
26360
+ cmdId: BC_CMD_ID_GET_AI_DENOISE,
26361
+ ...channel != null ? { channel } : {},
26362
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
26363
+ });
26364
+ return parseXmlFragmentToJson(xml);
26365
+ }
26366
+ /**
26367
+ * SetAudioNoise via Baichuan (cmdId=440, read via cmdId=439).
26368
+ * Mirrors reolink_aio's `SetAudioNoise` — `level <= 0` flips the
26369
+ * enable flag off; positive values turn it on and update the level.
26370
+ */
26371
+ async setAudioNoise(channel, level, options) {
26372
+ const ch = this.normalizeChannel(channel);
26373
+ const timeoutOpts = options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {};
26374
+ let xml = await this.sendXml({
26375
+ cmdId: BC_CMD_ID_GET_AI_DENOISE,
26376
+ channel: ch,
26377
+ ...timeoutOpts
26378
+ });
26379
+ xml = applyXmlTagPatch(xml, "enable", level > 0 ? 1 : 0);
26380
+ if (level > 0) {
26381
+ xml = applyXmlTagPatch(xml, "level", level);
26382
+ }
26383
+ await this.sendXml({
26384
+ cmdId: BC_CMD_ID_SET_AI_DENOISE,
26385
+ channel: ch,
26386
+ payloadXml: ensureXmlHeader(xml),
26387
+ ...timeoutOpts
26388
+ });
26389
+ }
26390
+ /**
26391
+ * GetAutoFocus via Baichuan (cmdId=224). Returns the `<AutoFocus>`
26392
+ * block — only `disable` (0 = AF on, 1 = AF off). Mirrors
26393
+ * reolink_aio's `GetAutoFocus`.
26394
+ */
26395
+ async getAutoFocus(channel, options) {
26396
+ const ch = this.normalizeChannel(channel);
26397
+ const xml = await this.sendPcapDerivedSettingsGetXml({
26398
+ cmdId: BC_CMD_ID_GET_AUTO_FOCUS,
26399
+ channel: ch,
26400
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
26401
+ });
26402
+ return parseXmlFragmentToJson(xml);
26403
+ }
26404
+ /**
26405
+ * SetAutoFocus via Baichuan (cmdId=225). Mirrors reolink_aio's
26406
+ * `SetAutoFocus`. Note: write-only command — the payload is built
26407
+ * from scratch (no read-modify-write needed).
26408
+ */
26409
+ async setAutoFocus(channel, disable, options) {
26410
+ const ch = this.normalizeChannel(channel);
26411
+ const disableVal = disable ? 1 : 0;
26412
+ const payloadXml = `<?xml version="1.0" encoding="UTF-8" ?>
26413
+ <body>
26414
+ <AutoFocus version="1.1">
26415
+ <channelId>${ch}</channelId>
26416
+ <disable>${disableVal}</disable>
26417
+ </AutoFocus>
26418
+ </body>`;
26419
+ await this.sendXml({
26420
+ cmdId: BC_CMD_ID_SET_AUTO_FOCUS,
26421
+ channel: ch,
26422
+ payloadXml,
26423
+ ...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
26424
+ });
26425
+ }
25862
26426
  /**
25863
26427
  * Passthrough to ReolinkCgiApi.getAllChannelsBatteryInfo.
25864
26428
  * Fetches battery info for all channels via CGI (merged with channel status sleep flag).