@camstack/addon-provider-reolink 1.2.139 → 1.2.141
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BaichuanVideoStream-2M4UO7MB-CB-tIHMA.mjs +2 -0
- package/dist/BaichuanVideoStream-2M4UO7MB-Cqjt1oxu.js +3 -0
- package/dist/addon.js +582 -279
- package/dist/addon.mjs +432 -129
- package/dist/{chunk-KWWUCNIY-Ccq-OAGJ.mjs → chunk-ZQFQRCLQ-BNqCZtSS.mjs} +1 -1
- package/dist/{chunk-KWWUCNIY-BtO6U6BT.js → chunk-ZQFQRCLQ-CR7OOtpp.js} +1 -1
- package/package.json +2 -2
- package/dist/BaichuanVideoStream-PBJL3EB5-D-etWPiW.mjs +0 -2
- package/dist/BaichuanVideoStream-PBJL3EB5-qv_JKCPo.js +0 -3
- /package/dist/{DiagnosticsTools-TID5M22B-9NV95vRN.mjs → DiagnosticsTools-GOFDNGAY-9NV95vRN.mjs} +0 -0
- /package/dist/{DiagnosticsTools-TID5M22B-CD4rRF8a.js → DiagnosticsTools-GOFDNGAY-CD4rRF8a.js} +0 -0
package/dist/addon.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM, r as __exportAll, t as __commonJSMin } from "./chunk-CNf5ZN-e.mjs";
|
|
2
|
-
import { A as splitAnnexBToNalPayloads2, C as extractVpsFromAnnexB, D as normalizeDebugOptions, E as md5StrModern, M as traceLog, O as recordingsTraceLog, S as extractSpsFromAnnexB, T as isH265Irap, _ as convertToAnnexB2, a as BC_MAGIC, b as eventTraceLog, c as BaichuanVideoStream, d as aesDecrypt, f as aesEncrypt, g as convertToAnnexB, h as bcHeaderHasPayloadOffset, i as BC_CLASS_MODERN_24, j as talkTraceLog, k as splitAnnexBToNalPayloads, l as BcMediaAnnexBDecoder, m as bcEncrypt, n as BC_CLASS_LEGACY, o as BC_MAGIC_REV, p as bcDecrypt, r as BC_CLASS_MODERN_20, t as BC_CLASS_FILE_DOWNLOAD, u as __require$1, v as debugLog, w as getH265NalType, x as extractPpsFromAnnexB, y as deriveAesKey } from "./chunk-
|
|
2
|
+
import { A as splitAnnexBToNalPayloads2, C as extractVpsFromAnnexB, D as normalizeDebugOptions, E as md5StrModern, M as traceLog, O as recordingsTraceLog, S as extractSpsFromAnnexB, T as isH265Irap, _ as convertToAnnexB2, a as BC_MAGIC, b as eventTraceLog, c as BaichuanVideoStream, d as aesDecrypt, f as aesEncrypt, g as convertToAnnexB, h as bcHeaderHasPayloadOffset, i as BC_CLASS_MODERN_24, j as talkTraceLog, k as splitAnnexBToNalPayloads, l as BcMediaAnnexBDecoder, m as bcEncrypt, n as BC_CLASS_LEGACY, o as BC_MAGIC_REV, p as bcDecrypt, r as BC_CLASS_MODERN_20, t as BC_CLASS_FILE_DOWNLOAD, u as __require$1, v as debugLog, w as getH265NalType, x as extractPpsFromAnnexB, y as deriveAesKey } from "./chunk-ZQFQRCLQ-BNqCZtSS.mjs";
|
|
3
3
|
import { createHash, randomBytes } from "node:crypto";
|
|
4
4
|
import { EventEmitter } from "events";
|
|
5
5
|
import * as fs2 from "fs";
|
|
@@ -5381,7 +5381,7 @@ var ZodIssueCode = {
|
|
|
5381
5381
|
var ZodFirstPartyTypeKind;
|
|
5382
5382
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5383
5383
|
//#endregion
|
|
5384
|
-
//#region ../types/dist/sleep-
|
|
5384
|
+
//#region ../types/dist/sleep-Dk-BdnBf.mjs
|
|
5385
5385
|
/**
|
|
5386
5386
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5387
5387
|
* window to float samples (D455).
|
|
@@ -31815,6 +31815,26 @@ var PtzHomePresetSchema = object({
|
|
|
31815
31815
|
"none"
|
|
31816
31816
|
])
|
|
31817
31817
|
});
|
|
31818
|
+
/**
|
|
31819
|
+
* The camera's own idle-return behaviour: whether it goes home by itself, and
|
|
31820
|
+
* after how long.
|
|
31821
|
+
*
|
|
31822
|
+
* ONE shape, not two scalars, because on the camera they are ONE fact —
|
|
31823
|
+
* Reolink's guard point carries `benable` and `timeout` in the same element,
|
|
31824
|
+
* written by the same command. Reading them apart means two round-trips that
|
|
31825
|
+
* can disagree.
|
|
31826
|
+
*
|
|
31827
|
+
* `enabled` and `seconds` are INDEPENDENT: a camera can hold a 68 s delay with
|
|
31828
|
+
* the behaviour switched off, which is exactly what the vendor app leaves when
|
|
31829
|
+
* you turn the guard point off without clearing it. The first version of this
|
|
31830
|
+
* cap reported only the delay, so that state was invisible — a camera could be
|
|
31831
|
+
* configured to return after 68 seconds with no way to say whether it should
|
|
31832
|
+
* return at all.
|
|
31833
|
+
*/
|
|
31834
|
+
var PtzHomeReturnSchema = object({
|
|
31835
|
+
enabled: boolean(),
|
|
31836
|
+
seconds: number().int()
|
|
31837
|
+
});
|
|
31818
31838
|
var ptzCapability = {
|
|
31819
31839
|
name: "ptz",
|
|
31820
31840
|
scope: "device",
|
|
@@ -31874,6 +31894,54 @@ var ptzCapability = {
|
|
|
31874
31894
|
auth: "admin"
|
|
31875
31895
|
}),
|
|
31876
31896
|
/**
|
|
31897
|
+
* Make WHERE THE HEAD IS POINTING RIGHT NOW this camera's home.
|
|
31898
|
+
*
|
|
31899
|
+
* `setHomePreset` takes a preset id, and a preset id is the wrong shape for
|
|
31900
|
+
* the most natural thing an operator does: aim the camera, then say "here".
|
|
31901
|
+
* It is also the only shape a camera whose home is NOT a preset can accept
|
|
31902
|
+
* -- Reolink's home is a guard point, a stored position with no id at all,
|
|
31903
|
+
* and hiding the control for that case (as the UI briefly did) left the one
|
|
31904
|
+
* vendor with a native home unable to set it.
|
|
31905
|
+
*
|
|
31906
|
+
* Each provider does it its own way:
|
|
31907
|
+
* reolink `setGrd` with `needSetPos`, pinning the guard point here
|
|
31908
|
+
* hikvision / amcrest save a preset, then pin it as home
|
|
31909
|
+
* onvif REFUSES -- its home is the PTZ origin and cannot be moved
|
|
31910
|
+
*
|
|
31911
|
+
* A provider that cannot honour it throws rather than silently doing
|
|
31912
|
+
* nothing: an operator who pressed "set home here" and saw no error would
|
|
31913
|
+
* believe it took.
|
|
31914
|
+
*/
|
|
31915
|
+
captureHomeHere: method(object({ deviceId: number() }), _void(), {
|
|
31916
|
+
kind: "mutation",
|
|
31917
|
+
auth: "admin"
|
|
31918
|
+
}),
|
|
31919
|
+
/**
|
|
31920
|
+
* Whether the camera returns home on its own when left idle, and after how
|
|
31921
|
+
* long. `null` when it has no such behaviour at all.
|
|
31922
|
+
*
|
|
31923
|
+
* Reolink's guard point carries one (`timeout`, freely settable in
|
|
31924
|
+
* seconds). It is a real function of the camera that we did not expose at
|
|
31925
|
+
* all, and it belongs next to the home it governs rather than in a vendor
|
|
31926
|
+
* settings page nobody associates with Home.
|
|
31927
|
+
*/
|
|
31928
|
+
getHomeReturn: method(object({ deviceId: number() }), PtzHomeReturnSchema.nullable()),
|
|
31929
|
+
/**
|
|
31930
|
+
* Set both together. Refused by a camera with no idle-return behaviour.
|
|
31931
|
+
*
|
|
31932
|
+
* They travel together for the same reason they are read together: the
|
|
31933
|
+
* underlying write carries both, so setting one alone means reading the
|
|
31934
|
+
* other back first and racing whoever else is writing.
|
|
31935
|
+
*/
|
|
31936
|
+
setHomeReturn: method(object({
|
|
31937
|
+
deviceId: number(),
|
|
31938
|
+
enabled: boolean(),
|
|
31939
|
+
seconds: number().int().min(0).max(3600)
|
|
31940
|
+
}), _void(), {
|
|
31941
|
+
kind: "mutation",
|
|
31942
|
+
auth: "admin"
|
|
31943
|
+
}),
|
|
31944
|
+
/**
|
|
31877
31945
|
* Pull the current PTZ position. Redundant with the auto-injected
|
|
31878
31946
|
* `getStatus` method (see `status` below); kept for callers that
|
|
31879
31947
|
* haven't migrated. Will be folded into `getStatus` in a future
|
|
@@ -41531,6 +41599,12 @@ Object.freeze({
|
|
|
41531
41599
|
addonId: null,
|
|
41532
41600
|
access: "create"
|
|
41533
41601
|
},
|
|
41602
|
+
"ptz.captureHomeHere": {
|
|
41603
|
+
capName: "ptz",
|
|
41604
|
+
capScope: "device",
|
|
41605
|
+
addonId: null,
|
|
41606
|
+
access: "create"
|
|
41607
|
+
},
|
|
41534
41608
|
"ptz.continuousMove": {
|
|
41535
41609
|
capName: "ptz",
|
|
41536
41610
|
capScope: "device",
|
|
@@ -41549,6 +41623,12 @@ Object.freeze({
|
|
|
41549
41623
|
addonId: null,
|
|
41550
41624
|
access: "view"
|
|
41551
41625
|
},
|
|
41626
|
+
"ptz.getHomeReturn": {
|
|
41627
|
+
capName: "ptz",
|
|
41628
|
+
capScope: "device",
|
|
41629
|
+
addonId: null,
|
|
41630
|
+
access: "view"
|
|
41631
|
+
},
|
|
41552
41632
|
"ptz.getOptions": {
|
|
41553
41633
|
capName: "ptz",
|
|
41554
41634
|
capScope: "device",
|
|
@@ -41603,6 +41683,12 @@ Object.freeze({
|
|
|
41603
41683
|
addonId: null,
|
|
41604
41684
|
access: "create"
|
|
41605
41685
|
},
|
|
41686
|
+
"ptz.setHomeReturn": {
|
|
41687
|
+
capName: "ptz",
|
|
41688
|
+
capScope: "device",
|
|
41689
|
+
addonId: null,
|
|
41690
|
+
access: "create"
|
|
41691
|
+
},
|
|
41606
41692
|
"ptz.stop": {
|
|
41607
41693
|
capName: "ptz",
|
|
41608
41694
|
capScope: "device",
|
|
@@ -44766,6 +44852,11 @@ Object.freeze({
|
|
|
44766
44852
|
form: "single",
|
|
44767
44853
|
optional: false
|
|
44768
44854
|
}],
|
|
44855
|
+
"ptz.captureHomeHere": [{
|
|
44856
|
+
name: "deviceId",
|
|
44857
|
+
form: "single",
|
|
44858
|
+
optional: false
|
|
44859
|
+
}],
|
|
44769
44860
|
"ptz.continuousMove": [{
|
|
44770
44861
|
name: "deviceId",
|
|
44771
44862
|
form: "single",
|
|
@@ -44781,6 +44872,11 @@ Object.freeze({
|
|
|
44781
44872
|
form: "single",
|
|
44782
44873
|
optional: false
|
|
44783
44874
|
}],
|
|
44875
|
+
"ptz.getHomeReturn": [{
|
|
44876
|
+
name: "deviceId",
|
|
44877
|
+
form: "single",
|
|
44878
|
+
optional: false
|
|
44879
|
+
}],
|
|
44784
44880
|
"ptz.getOptions": [{
|
|
44785
44881
|
name: "deviceId",
|
|
44786
44882
|
form: "single",
|
|
@@ -44826,6 +44922,11 @@ Object.freeze({
|
|
|
44826
44922
|
form: "single",
|
|
44827
44923
|
optional: false
|
|
44828
44924
|
}],
|
|
44925
|
+
"ptz.setHomeReturn": [{
|
|
44926
|
+
name: "deviceId",
|
|
44927
|
+
form: "single",
|
|
44928
|
+
optional: false
|
|
44929
|
+
}],
|
|
44829
44930
|
"ptz.stop": [{
|
|
44830
44931
|
name: "deviceId",
|
|
44831
44932
|
form: "single",
|
|
@@ -68510,7 +68611,7 @@ var require_yazl = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
68510
68611
|
}
|
|
68511
68612
|
}));
|
|
68512
68613
|
//#endregion
|
|
68513
|
-
//#region node_modules/@apocaliss92/nodelink-js/dist/chunk-
|
|
68614
|
+
//#region node_modules/@apocaliss92/nodelink-js/dist/chunk-KWQVN7H5.js
|
|
68514
68615
|
var import_undici = require_undici();
|
|
68515
68616
|
var import_yazl = /* @__PURE__ */ __toESM(require_yazl(), 1);
|
|
68516
68617
|
var FLAGS_CAM_V2 = {
|
|
@@ -74927,7 +75028,7 @@ var require_lz4 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
74927
75028
|
};
|
|
74928
75029
|
}));
|
|
74929
75030
|
//#endregion
|
|
74930
|
-
//#region node_modules/@apocaliss92/nodelink-js/dist/chunk-
|
|
75031
|
+
//#region node_modules/@apocaliss92/nodelink-js/dist/chunk-CE77DTET.js
|
|
74931
75032
|
var import_fxp = require_fxp();
|
|
74932
75033
|
var import_lz4 = /* @__PURE__ */ __toESM(require_lz4(), 1);
|
|
74933
75034
|
function encodeHeader(h) {
|
|
@@ -75246,8 +75347,8 @@ function buildC2mQ(params) {
|
|
|
75246
75347
|
const os = params.os ?? "MAC";
|
|
75247
75348
|
return buildP2pXml(`<C2M_Q><uid>${xmlEscape(params.uid)}</uid><p>${xmlEscape(os)}</p></C2M_Q>`);
|
|
75248
75349
|
}
|
|
75249
|
-
function parseIpPortBlock(
|
|
75250
|
-
const m = new RegExp(`<${
|
|
75350
|
+
function parseIpPortBlock(tag2, body) {
|
|
75351
|
+
const m = new RegExp(`<${tag2}>([\\s\\S]*?)</${tag2}>`, "i").exec(body);
|
|
75251
75352
|
if (!m) return void 0;
|
|
75252
75353
|
const block = m[1] ?? "";
|
|
75253
75354
|
const ip = /<ip>([^<]+)<\/ip>/i.exec(block)?.[1];
|
|
@@ -75292,8 +75393,8 @@ function parseR2cCr(xml) {
|
|
|
75292
75393
|
if (!Number.isFinite(rsp)) return void 0;
|
|
75293
75394
|
const sidStr = /<sid>(-?\d+)<\/sid>/i.exec(body)?.[1];
|
|
75294
75395
|
const sid = sidStr != null ? Number(sidStr) : void 0;
|
|
75295
|
-
const parseCustom = (
|
|
75296
|
-
const mm = new RegExp(`<${
|
|
75396
|
+
const parseCustom = (tag2) => {
|
|
75397
|
+
const mm = new RegExp(`<${tag2}>([\\s\\S]*?)</${tag2}>`, "i").exec(body);
|
|
75297
75398
|
if (!mm) return void 0;
|
|
75298
75399
|
const block = mm[1] ?? "";
|
|
75299
75400
|
const ip = /<ip>([^<]+)<\/ip>/i.exec(block)?.[1];
|
|
@@ -76853,12 +76954,12 @@ function asLogger(logger) {
|
|
|
76853
76954
|
error: resolveMethod(base, "error"),
|
|
76854
76955
|
debug: resolveMethod(base, "debug")
|
|
76855
76956
|
};
|
|
76856
|
-
out.child = (
|
|
76957
|
+
out.child = (tag2) => createTaggedLogger(out, tag2);
|
|
76857
76958
|
return out;
|
|
76858
76959
|
}
|
|
76859
|
-
function createTaggedLogger(base,
|
|
76960
|
+
function createTaggedLogger(base, tag2) {
|
|
76860
76961
|
const b = base.log ? base : asLogger(base);
|
|
76861
|
-
const prefix = `[${
|
|
76962
|
+
const prefix = `[${tag2}] `;
|
|
76862
76963
|
const wrap = (fn) => {
|
|
76863
76964
|
return (message, ...optionalParams) => {
|
|
76864
76965
|
if (typeof message === "string") fn(`${prefix}${message}`, ...optionalParams);
|
|
@@ -76885,7 +76986,7 @@ function createDebugGateLogger(base, enabled = false) {
|
|
|
76885
76986
|
error: b.error,
|
|
76886
76987
|
debug: enabled ? b.debug : noop
|
|
76887
76988
|
};
|
|
76888
|
-
out.child = (
|
|
76989
|
+
out.child = (tag2) => createDebugGateLogger(createTaggedLogger(out, tag2), enabled);
|
|
76889
76990
|
return out;
|
|
76890
76991
|
}
|
|
76891
76992
|
function isTalkCmd(cmdId) {
|
|
@@ -82579,29 +82680,29 @@ function parseSupportXml(xml) {
|
|
|
82579
82680
|
if (!match) return void 0;
|
|
82580
82681
|
const supportXml = match[1] ?? "";
|
|
82581
82682
|
const ptzMode = supportXml.match(/<ptzMode>([^<]*)<\/ptzMode>/i)?.[1];
|
|
82582
|
-
const topLevelInt = (
|
|
82583
|
-
return toNumberOrUndefined(supportXml.match(new RegExp(`<${
|
|
82683
|
+
const topLevelInt = (tag2) => {
|
|
82684
|
+
return toNumberOrUndefined(supportXml.match(new RegExp(`<${tag2}>([^<]*)<\\/${tag2}>`, "i"))?.[1]);
|
|
82584
82685
|
};
|
|
82585
|
-
const topLevelString = (
|
|
82586
|
-
return supportXml.match(new RegExp(`<${
|
|
82686
|
+
const topLevelString = (tag2) => {
|
|
82687
|
+
return supportXml.match(new RegExp(`<${tag2}>([^<]*)<\\/${tag2}>`, "i"))?.[1];
|
|
82587
82688
|
};
|
|
82588
82689
|
const items = [];
|
|
82589
82690
|
for (const itemMatch of supportXml.matchAll(/<item[^>]*>([\s\S]*?)<\/item>/gi)) {
|
|
82590
82691
|
const itemXml = itemMatch[1] ?? "";
|
|
82591
82692
|
const item = { chnID: toNumberOrUndefined(itemXml.match(/<chnID>([^<]*)<\/chnID>/i)?.[1]) ?? 0 };
|
|
82592
82693
|
for (const tagMatch of itemXml.matchAll(/<([A-Za-z0-9_]+)>([^<]*)<\/\1>/g)) {
|
|
82593
|
-
const
|
|
82694
|
+
const tag2 = tagMatch[1];
|
|
82594
82695
|
const value = tagMatch[2];
|
|
82595
|
-
if (!
|
|
82596
|
-
if (
|
|
82597
|
-
item[
|
|
82696
|
+
if (!tag2) continue;
|
|
82697
|
+
if (tag2 === "chnID") continue;
|
|
82698
|
+
item[tag2] = toNumberOrUndefined(value) ?? value;
|
|
82598
82699
|
}
|
|
82599
82700
|
items.push(item);
|
|
82600
82701
|
}
|
|
82601
82702
|
const support = { items };
|
|
82602
82703
|
if (ptzMode !== void 0) support.ptzMode = ptzMode;
|
|
82603
|
-
const assignInt = (key,
|
|
82604
|
-
const v = topLevelInt(
|
|
82704
|
+
const assignInt = (key, tag2) => {
|
|
82705
|
+
const v = topLevelInt(tag2);
|
|
82605
82706
|
if (v !== void 0) support[key] = v;
|
|
82606
82707
|
};
|
|
82607
82708
|
assignInt("IOInputPortNum", "IOInputPortNum");
|
|
@@ -82828,15 +82929,15 @@ function extractCanvasFromShelterXml(xml) {
|
|
|
82828
82929
|
const yMatch = xml.match(/<topLeftY>(\d+)<\/topLeftY>/g) ?? [];
|
|
82829
82930
|
let canvasX = 0;
|
|
82830
82931
|
let canvasY = 0;
|
|
82831
|
-
for (const
|
|
82832
|
-
const v = Number(
|
|
82932
|
+
for (const tag2 of xMatch) {
|
|
82933
|
+
const v = Number(tag2.replace(/<\/?topLeftX>/g, "")) | 0;
|
|
82833
82934
|
if (v > 0) {
|
|
82834
82935
|
canvasX = v & 65535;
|
|
82835
82936
|
break;
|
|
82836
82937
|
}
|
|
82837
82938
|
}
|
|
82838
|
-
for (const
|
|
82839
|
-
const v = Number(
|
|
82939
|
+
for (const tag2 of yMatch) {
|
|
82940
|
+
const v = Number(tag2.replace(/<\/?topLeftY>/g, "")) | 0;
|
|
82840
82941
|
if (v > 0) {
|
|
82841
82942
|
canvasY = v & 65535;
|
|
82842
82943
|
break;
|
|
@@ -82849,9 +82950,9 @@ function extractCanvasFromShelterXml(xml) {
|
|
|
82849
82950
|
}
|
|
82850
82951
|
function parseRectTags(block) {
|
|
82851
82952
|
const out = {};
|
|
82852
|
-
const grab = (
|
|
82853
|
-
const m = block.match(new RegExp(`<${
|
|
82854
|
-
if (m && m[1] !== void 0) out[
|
|
82953
|
+
const grab = (tag2) => {
|
|
82954
|
+
const m = block.match(new RegExp(`<${tag2}>([^<]*)<\\/${tag2}>`));
|
|
82955
|
+
if (m && m[1] !== void 0) out[tag2] = m[1];
|
|
82855
82956
|
};
|
|
82856
82957
|
grab("id");
|
|
82857
82958
|
grab("name");
|
|
@@ -83096,9 +83197,9 @@ var buildDetectionClasses = (parsed, recordType) => {
|
|
|
83096
83197
|
};
|
|
83097
83198
|
var getXmlTexts = (xml, tags) => {
|
|
83098
83199
|
const out = {};
|
|
83099
|
-
for (const
|
|
83100
|
-
const v = getXmlText(xml,
|
|
83101
|
-
if (v !== void 0) out[
|
|
83200
|
+
for (const tag2 of tags) {
|
|
83201
|
+
const v = getXmlText(xml, tag2);
|
|
83202
|
+
if (v !== void 0) out[tag2] = v;
|
|
83102
83203
|
}
|
|
83103
83204
|
return out;
|
|
83104
83205
|
};
|
|
@@ -83360,11 +83461,11 @@ function patchBlock(xml, block, fields) {
|
|
|
83360
83461
|
const end = xml.indexOf(`</${block}>`, start);
|
|
83361
83462
|
if (end < 0) return xml;
|
|
83362
83463
|
let body = xml.slice(start, end);
|
|
83363
|
-
for (const [
|
|
83464
|
+
for (const [tag2, value] of Object.entries(fields)) {
|
|
83364
83465
|
if (value === void 0) continue;
|
|
83365
83466
|
const escaped = escapeXmlText(typeof value === "boolean" ? value ? "1" : "0" : String(value));
|
|
83366
|
-
if (body.includes(`<${
|
|
83367
|
-
else body += `<${
|
|
83467
|
+
if (body.includes(`<${tag2}>`)) body = body.replace(new RegExp(`<${tag2}>[^<]*<\\/${tag2}>`), `<${tag2}>${escaped}</${tag2}>`);
|
|
83468
|
+
else body += `<${tag2}>${escaped}</${tag2}>`;
|
|
83368
83469
|
}
|
|
83369
83470
|
return xml.slice(0, start) + body + xml.slice(end);
|
|
83370
83471
|
}
|
|
@@ -83432,6 +83533,66 @@ var OSD_CORNER_LABELS = {
|
|
|
83432
83533
|
"bottom-left": "Bottom left",
|
|
83433
83534
|
"bottom-right": "Bottom right"
|
|
83434
83535
|
};
|
|
83536
|
+
function wireFloat(value) {
|
|
83537
|
+
return value.toExponential(6).replace(/e([+-])(\d)$/, "e$10$2");
|
|
83538
|
+
}
|
|
83539
|
+
function buildPtzGuardExtensionXml(channelId, direction) {
|
|
83540
|
+
return `<?xml version="1.0" encoding="UTF-8" ?>
|
|
83541
|
+
<Extension version="1.1">
|
|
83542
|
+
<channelId>${channelId}</channelId>${direction === "read" ? `
|
|
83543
|
+
<chnType>0</chnType>` : ""}
|
|
83544
|
+
</Extension>
|
|
83545
|
+
`;
|
|
83546
|
+
}
|
|
83547
|
+
function guardBody(channelId, command, fields) {
|
|
83548
|
+
return [
|
|
83549
|
+
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>",
|
|
83550
|
+
"<body>",
|
|
83551
|
+
"<PtzGuard version=\"1.1\">",
|
|
83552
|
+
`<channelId>${channelId}</channelId>`,
|
|
83553
|
+
...fields,
|
|
83554
|
+
`<command>${command}</command>`,
|
|
83555
|
+
"<imageName></imageName>",
|
|
83556
|
+
`<xpos>${wireFloat(0)}</xpos>`,
|
|
83557
|
+
`<ypos>${wireFloat(0)}</ypos>`,
|
|
83558
|
+
`<height>${wireFloat(0)}</height>`,
|
|
83559
|
+
`<width>${wireFloat(0)}</width>`,
|
|
83560
|
+
"<mode>global</mode>",
|
|
83561
|
+
"</PtzGuard>",
|
|
83562
|
+
"</body>",
|
|
83563
|
+
""
|
|
83564
|
+
].join("\n");
|
|
83565
|
+
}
|
|
83566
|
+
function buildPtzGuardGoXml(channelId) {
|
|
83567
|
+
return guardBody(channelId, "toGrd", ["<benable>0</benable>", "<timeout>68</timeout>"]);
|
|
83568
|
+
}
|
|
83569
|
+
function buildPtzGuardSetXml(input) {
|
|
83570
|
+
return guardBody(input.channelId, "setGrd", [
|
|
83571
|
+
`<benable>${input.enabled ? 1 : 0}</benable>`,
|
|
83572
|
+
`<timeout>${Math.round(input.timeoutSeconds)}</timeout>`,
|
|
83573
|
+
...input.setPosition ? ["<needSetPos>1</needSetPos>"] : []
|
|
83574
|
+
]);
|
|
83575
|
+
}
|
|
83576
|
+
function tag(xml, name) {
|
|
83577
|
+
const m = new RegExp(`<${name}>([\\s\\S]*?)</${name}>`).exec(xml);
|
|
83578
|
+
return m === null ? null : (m[1] ?? "").trim();
|
|
83579
|
+
}
|
|
83580
|
+
function parsePtzGuardXml(xml) {
|
|
83581
|
+
if (!xml.includes("<PtzGuard")) return null;
|
|
83582
|
+
const enabled = tag(xml, "benable");
|
|
83583
|
+
const valid = tag(xml, "bvalid");
|
|
83584
|
+
const timeout = tag(xml, "timeout");
|
|
83585
|
+
if (enabled === null || valid === null || timeout === null) return null;
|
|
83586
|
+
const timeoutSeconds = Number.parseInt(timeout, 10);
|
|
83587
|
+
if (!Number.isFinite(timeoutSeconds)) return null;
|
|
83588
|
+
return {
|
|
83589
|
+
enabled: enabled === "1",
|
|
83590
|
+
valid: valid === "1",
|
|
83591
|
+
timeoutSeconds,
|
|
83592
|
+
mode: tag(xml, "mode") ?? "global",
|
|
83593
|
+
imageName: tag(xml, "imageName") ?? ""
|
|
83594
|
+
};
|
|
83595
|
+
}
|
|
83435
83596
|
var emitter = new EventEmitter();
|
|
83436
83597
|
var lastEventByCamera = /* @__PURE__ */ new Map();
|
|
83437
83598
|
var MAX_GLOBAL_EVENTS = 300;
|
|
@@ -84644,8 +84805,8 @@ var buildFileInfoListStopXml = (params) => {
|
|
|
84644
84805
|
</body>`;
|
|
84645
84806
|
};
|
|
84646
84807
|
var sleepMs = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
84647
|
-
var xmlDateTimePayload = (
|
|
84648
|
-
return `<${
|
|
84808
|
+
var xmlDateTimePayload = (tag2, d) => {
|
|
84809
|
+
return `<${tag2}><year>${d.getFullYear()}</year><month>${d.getMonth() + 1}</month><day>${d.getDate()}</day><hour>${d.getHours()}</hour><minute>${d.getMinutes()}</minute><second>${d.getSeconds()}</second></${tag2}>`;
|
|
84649
84810
|
};
|
|
84650
84811
|
var buildFileInfoListOpenXml = (params) => {
|
|
84651
84812
|
return `<?xml version="1.0" encoding="UTF-8" ?>
|
|
@@ -84833,10 +84994,10 @@ var isPlausibleStream = (s) => {
|
|
|
84833
84994
|
return s.width > 0 && s.height > 0 && (s.frameRate > 0 || s.bitRate > 0);
|
|
84834
84995
|
};
|
|
84835
84996
|
var logDebugStreamBlock = (params) => {
|
|
84836
|
-
const { logger, traceNativeStream, channel, tag, blockXml } = params;
|
|
84997
|
+
const { logger, traceNativeStream, channel, tag: tag2, blockXml } = params;
|
|
84837
84998
|
if (!traceNativeStream) return;
|
|
84838
84999
|
if (!blockXml) {
|
|
84839
|
-
(logger.warn ?? logger.log).call(logger, `[ReolinkBaichuanApi] getStreamMetadata(traceNativeStream): channel=${channel} tag=<${
|
|
85000
|
+
(logger.warn ?? logger.log).call(logger, `[ReolinkBaichuanApi] getStreamMetadata(traceNativeStream): channel=${channel} tag=<${tag2}> missing`);
|
|
84840
85001
|
return;
|
|
84841
85002
|
}
|
|
84842
85003
|
const raw = blockXml;
|
|
@@ -84863,7 +85024,7 @@ var logDebugStreamBlock = (params) => {
|
|
|
84863
85024
|
const previewMax = 1400;
|
|
84864
85025
|
const xmlPreview = raw.length <= previewMax ? raw : raw.slice(0, previewMax) + `
|
|
84865
85026
|
...truncated (+${raw.length - previewMax} chars)`;
|
|
84866
|
-
(logger.warn ?? logger.log).call(logger, `[ReolinkBaichuanApi] getStreamMetadata(traceNativeStream): channel=${channel} tag=<${
|
|
85027
|
+
(logger.warn ?? logger.log).call(logger, `[ReolinkBaichuanApi] getStreamMetadata(traceNativeStream): channel=${channel} tag=<${tag2}> enabled=${isEnabled} plausible=${plausible} width=${width} height=${height} frame=${frameRate} bitRate=${bitRate} audio=${audio} videoEncType=${videoEncTypeText ?? "?"} rawFields=${JSON.stringify({
|
|
84867
85028
|
widthText,
|
|
84868
85029
|
heightText,
|
|
84869
85030
|
frameText,
|
|
@@ -84956,20 +85117,20 @@ var parseChannelStreamMetadataFromGetEncXml = (params) => {
|
|
|
84956
85117
|
audioEnabled = audioEnabled && s.audio === 1;
|
|
84957
85118
|
}
|
|
84958
85119
|
}
|
|
84959
|
-
for (const
|
|
85120
|
+
for (const tag2 of [
|
|
84960
85121
|
"extStream",
|
|
84961
85122
|
"thirdStream",
|
|
84962
85123
|
"externStream",
|
|
84963
85124
|
"extraStream"
|
|
84964
85125
|
]) {
|
|
84965
|
-
const extMatch = xml.match(new RegExp(`<${
|
|
85126
|
+
const extMatch = xml.match(new RegExp(`<${tag2}[^>]*>([\\s\\S]*?)<\\/${tag2}>`));
|
|
84966
85127
|
if (!extMatch) continue;
|
|
84967
85128
|
const extXml = extMatch[1] ?? "";
|
|
84968
85129
|
logDebugStreamBlock({
|
|
84969
85130
|
logger,
|
|
84970
85131
|
traceNativeStream,
|
|
84971
85132
|
channel,
|
|
84972
|
-
tag,
|
|
85133
|
+
tag: tag2,
|
|
84973
85134
|
blockXml: extXml
|
|
84974
85135
|
});
|
|
84975
85136
|
const s = buildStream({
|
|
@@ -86562,7 +86723,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86562
86723
|
* @param logger - Optional logger for debug output
|
|
86563
86724
|
* @returns The socket and a release function
|
|
86564
86725
|
*/
|
|
86565
|
-
async acquirePooledSocket(
|
|
86726
|
+
async acquirePooledSocket(tag2, logger) {
|
|
86566
86727
|
const log = logger ?? this.logger;
|
|
86567
86728
|
const now = Date.now();
|
|
86568
86729
|
const cooldownEntry = this.socketPoolCooldowns.get(this.host);
|
|
@@ -86573,12 +86734,12 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86573
86734
|
} else if (now < cooldownEntry.cooldownUntil) {
|
|
86574
86735
|
const remainingMs = cooldownEntry.cooldownUntil - now;
|
|
86575
86736
|
const reason = this.lastD2cDiscAtMs > 0 && now - this.lastD2cDiscAtMs < 12e4 ? "D2C_DISC (camera sleeping)" : "repeated login failures";
|
|
86576
|
-
const error = /* @__PURE__ */ new Error(`[SocketPool] Host ${this.host} is in cooldown for ${Math.ceil(remainingMs / 1e3)}s due to ${reason}. tag=${
|
|
86737
|
+
const error = /* @__PURE__ */ new Error(`[SocketPool] Host ${this.host} is in cooldown for ${Math.ceil(remainingMs / 1e3)}s due to ${reason}. tag=${tag2}`);
|
|
86577
86738
|
log?.debug?.(error.message);
|
|
86578
86739
|
throw error;
|
|
86579
86740
|
}
|
|
86580
86741
|
}
|
|
86581
|
-
const existing = this.socketPool.get(
|
|
86742
|
+
const existing = this.socketPool.get(tag2);
|
|
86582
86743
|
if (existing) {
|
|
86583
86744
|
if (existing.idleCloseTimer) {
|
|
86584
86745
|
clearTimeout(existing.idleCloseTimer);
|
|
@@ -86588,35 +86749,35 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86588
86749
|
const client2 = await existing.pendingPromise;
|
|
86589
86750
|
existing.refCount++;
|
|
86590
86751
|
existing.lastUsedAt = Date.now();
|
|
86591
|
-
log?.debug?.(`[SocketPool] Waited for pending socket creation for tag=${
|
|
86752
|
+
log?.debug?.(`[SocketPool] Waited for pending socket creation for tag=${tag2} (refCount=${existing.refCount})`);
|
|
86592
86753
|
return {
|
|
86593
86754
|
client: client2,
|
|
86594
|
-
release: () => this.releasePooledSocket(
|
|
86755
|
+
release: () => this.releasePooledSocket(tag2, logger)
|
|
86595
86756
|
};
|
|
86596
86757
|
}
|
|
86597
86758
|
if (existing.refCount === 0) {
|
|
86598
86759
|
existing.refCount = 1;
|
|
86599
86760
|
existing.lastUsedAt = Date.now();
|
|
86600
|
-
log?.debug?.(`[SocketPool] Reusing idle socket for tag=${
|
|
86761
|
+
log?.debug?.(`[SocketPool] Reusing idle socket for tag=${tag2}`);
|
|
86601
86762
|
try {
|
|
86602
86763
|
if (!existing.client.loggedIn) await existing.client.login();
|
|
86603
86764
|
} catch {}
|
|
86604
86765
|
if (existing.client.loggedIn) return {
|
|
86605
86766
|
client: existing.client,
|
|
86606
|
-
release: () => this.releasePooledSocket(
|
|
86767
|
+
release: () => this.releasePooledSocket(tag2, logger)
|
|
86607
86768
|
};
|
|
86608
|
-
} else if (
|
|
86769
|
+
} else if (tag2.startsWith("replay:")) log?.debug?.(`[SocketPool] Preempting active replay socket for tag=${tag2}`);
|
|
86609
86770
|
else {
|
|
86610
86771
|
existing.refCount++;
|
|
86611
86772
|
existing.lastUsedAt = Date.now();
|
|
86612
|
-
log?.debug?.(`[SocketPool] Reusing active socket for tag=${
|
|
86773
|
+
log?.debug?.(`[SocketPool] Reusing active socket for tag=${tag2} (refCount=${existing.refCount})`);
|
|
86613
86774
|
return {
|
|
86614
86775
|
client: existing.client,
|
|
86615
|
-
release: () => this.releasePooledSocket(
|
|
86776
|
+
release: () => this.releasePooledSocket(tag2, logger)
|
|
86616
86777
|
};
|
|
86617
86778
|
}
|
|
86618
|
-
log?.debug?.(`[SocketPool] Closing existing socket for tag=${
|
|
86619
|
-
this.socketPool.delete(
|
|
86779
|
+
log?.debug?.(`[SocketPool] Closing existing socket for tag=${tag2} (recreating)`);
|
|
86780
|
+
this.socketPool.delete(tag2);
|
|
86620
86781
|
if (existing.generalPermitRelease) {
|
|
86621
86782
|
try {
|
|
86622
86783
|
existing.generalPermitRelease();
|
|
@@ -86629,10 +86790,10 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86629
86790
|
skipLogout: true
|
|
86630
86791
|
});
|
|
86631
86792
|
} catch (e) {
|
|
86632
|
-
log?.warn?.(`[SocketPool] Error closing old socket for tag=${
|
|
86793
|
+
log?.warn?.(`[SocketPool] Error closing old socket for tag=${tag2}: ${e}`);
|
|
86633
86794
|
}
|
|
86634
86795
|
}
|
|
86635
|
-
log?.log?.(`[SocketPool] Creating new socket for tag=${
|
|
86796
|
+
log?.log?.(`[SocketPool] Creating new socket for tag=${tag2}`);
|
|
86636
86797
|
const entry = {
|
|
86637
86798
|
client: void 0,
|
|
86638
86799
|
refCount: 0,
|
|
@@ -86649,7 +86810,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86649
86810
|
} : this.clientOptions);
|
|
86650
86811
|
this.attachD2cDiscListener(newClient);
|
|
86651
86812
|
newClient.on("error", (err) => {
|
|
86652
|
-
log?.debug?.(`[SocketPool] tag=${
|
|
86813
|
+
log?.debug?.(`[SocketPool] tag=${tag2} client error: ${err?.message ?? err}`);
|
|
86653
86814
|
});
|
|
86654
86815
|
await newClient.login();
|
|
86655
86816
|
const existingCooldown = this.socketPoolCooldowns.get(this.host);
|
|
@@ -86661,10 +86822,10 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86661
86822
|
entry.refCount = 1;
|
|
86662
86823
|
entry.lastUsedAt = Date.now();
|
|
86663
86824
|
delete entry.pendingPromise;
|
|
86664
|
-
log?.log?.(`[SocketPool] Socket connected for tag=${
|
|
86665
|
-
if (
|
|
86825
|
+
log?.log?.(`[SocketPool] Socket connected for tag=${tag2}`);
|
|
86826
|
+
if (tag2 !== "general") try {
|
|
86666
86827
|
const generalEntry = this.socketPool.get("general");
|
|
86667
|
-
if (generalEntry?.client) entry.generalPermitRelease = generalEntry.client.acquirePermit(0, `streaming-peer:${
|
|
86828
|
+
if (generalEntry?.client) entry.generalPermitRelease = generalEntry.client.acquirePermit(0, `streaming-peer:${tag2}`);
|
|
86668
86829
|
} catch {}
|
|
86669
86830
|
if (this.sessionGuardEnabled) this.maybeRebootOnTooManySessions();
|
|
86670
86831
|
return newClient;
|
|
@@ -86675,21 +86836,21 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86675
86836
|
if (failureCount >= _ReolinkBaichuanApi.SOCKET_POOL_FAILURE_THRESHOLD) {
|
|
86676
86837
|
const backoffMs = Math.min(_ReolinkBaichuanApi.SOCKET_POOL_BASE_COOLDOWN_MS * Math.pow(2, failureCount - _ReolinkBaichuanApi.SOCKET_POOL_FAILURE_THRESHOLD), _ReolinkBaichuanApi.SOCKET_POOL_MAX_COOLDOWN_MS);
|
|
86677
86838
|
cooldownUntil = now2 + backoffMs;
|
|
86678
|
-
log?.warn?.(`[SocketPool] Login failed for host=${this.host} (failure #${failureCount}). Entering cooldown for ${Math.ceil(backoffMs / 1e3)}s. tag=${
|
|
86679
|
-
} else log?.warn?.(`[SocketPool] Login failed for host=${this.host} (failure #${failureCount}/${_ReolinkBaichuanApi.SOCKET_POOL_FAILURE_THRESHOLD} before cooldown). tag=${
|
|
86839
|
+
log?.warn?.(`[SocketPool] Login failed for host=${this.host} (failure #${failureCount}). Entering cooldown for ${Math.ceil(backoffMs / 1e3)}s. tag=${tag2}`);
|
|
86840
|
+
} else log?.warn?.(`[SocketPool] Login failed for host=${this.host} (failure #${failureCount}/${_ReolinkBaichuanApi.SOCKET_POOL_FAILURE_THRESHOLD} before cooldown). tag=${tag2}`);
|
|
86680
86841
|
this.socketPoolCooldowns.set(this.host, {
|
|
86681
86842
|
failureCount,
|
|
86682
86843
|
lastFailureAt: now2,
|
|
86683
86844
|
cooldownUntil
|
|
86684
86845
|
});
|
|
86685
|
-
this.socketPool.delete(
|
|
86846
|
+
this.socketPool.delete(tag2);
|
|
86686
86847
|
throw loginError;
|
|
86687
86848
|
}
|
|
86688
86849
|
})();
|
|
86689
|
-
this.socketPool.set(
|
|
86850
|
+
this.socketPool.set(tag2, entry);
|
|
86690
86851
|
return {
|
|
86691
86852
|
client: await entry.pendingPromise,
|
|
86692
|
-
release: () => this.releasePooledSocket(
|
|
86853
|
+
release: () => this.releasePooledSocket(tag2, logger)
|
|
86693
86854
|
};
|
|
86694
86855
|
}
|
|
86695
86856
|
/**
|
|
@@ -86697,31 +86858,31 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86697
86858
|
* For shared sockets (general, streaming), just decrements refCount.
|
|
86698
86859
|
* For replay sockets, schedules idle close.
|
|
86699
86860
|
*/
|
|
86700
|
-
async releasePooledSocket(
|
|
86861
|
+
async releasePooledSocket(tag2, logger) {
|
|
86701
86862
|
const log = logger ?? this.logger;
|
|
86702
|
-
const entry = this.socketPool.get(
|
|
86863
|
+
const entry = this.socketPool.get(tag2);
|
|
86703
86864
|
if (!entry) return;
|
|
86704
86865
|
entry.refCount = Math.max(0, entry.refCount - 1);
|
|
86705
86866
|
entry.lastUsedAt = Date.now();
|
|
86706
|
-
log?.debug?.(`[SocketPool] Released socket for tag=${
|
|
86867
|
+
log?.debug?.(`[SocketPool] Released socket for tag=${tag2} (refCount=${entry.refCount})`);
|
|
86707
86868
|
if (entry.refCount > 0) return;
|
|
86708
|
-
const isReplayTag =
|
|
86709
|
-
const isStreamingTag =
|
|
86710
|
-
if (
|
|
86869
|
+
const isReplayTag = tag2.startsWith("replay:");
|
|
86870
|
+
const isStreamingTag = tag2.startsWith("streaming:");
|
|
86871
|
+
if (tag2 === "general") return;
|
|
86711
86872
|
if (isStreamingTag) {
|
|
86712
86873
|
if (entry.idleCloseTimer) return;
|
|
86713
86874
|
entry.idleCloseTimer = setTimeout(async () => {
|
|
86714
|
-
const current = this.socketPool.get(
|
|
86875
|
+
const current = this.socketPool.get(tag2);
|
|
86715
86876
|
if (!current) return;
|
|
86716
86877
|
if (current.refCount > 0) return;
|
|
86717
|
-
this.socketPool.delete(
|
|
86878
|
+
this.socketPool.delete(tag2);
|
|
86718
86879
|
if (current.generalPermitRelease) {
|
|
86719
86880
|
try {
|
|
86720
86881
|
current.generalPermitRelease();
|
|
86721
86882
|
} catch {}
|
|
86722
86883
|
current.generalPermitRelease = void 0;
|
|
86723
86884
|
}
|
|
86724
|
-
log?.log?.(`[SocketPool] Closing idle streaming socket for tag=${
|
|
86885
|
+
log?.log?.(`[SocketPool] Closing idle streaming socket for tag=${tag2}`);
|
|
86725
86886
|
try {
|
|
86726
86887
|
await current.client.close({
|
|
86727
86888
|
reason: "streaming idle close",
|
|
@@ -86734,11 +86895,11 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86734
86895
|
if (isReplayTag) {
|
|
86735
86896
|
if (entry.idleCloseTimer) return;
|
|
86736
86897
|
entry.idleCloseTimer = setTimeout(async () => {
|
|
86737
|
-
const current = this.socketPool.get(
|
|
86898
|
+
const current = this.socketPool.get(tag2);
|
|
86738
86899
|
if (!current) return;
|
|
86739
86900
|
if (current.refCount > 0) return;
|
|
86740
|
-
this.socketPool.delete(
|
|
86741
|
-
log?.debug?.(`[SocketPool] Closing idle replay socket for tag=${
|
|
86901
|
+
this.socketPool.delete(tag2);
|
|
86902
|
+
log?.debug?.(`[SocketPool] Closing idle replay socket for tag=${tag2} (keepalive expired)`);
|
|
86742
86903
|
try {
|
|
86743
86904
|
await current.client.close({
|
|
86744
86905
|
reason: "replay idle keepalive expired",
|
|
@@ -86748,24 +86909,24 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86748
86909
|
}, _ReolinkBaichuanApi.SOCKET_POOL_KEEPALIVE_MS);
|
|
86749
86910
|
return;
|
|
86750
86911
|
}
|
|
86751
|
-
this.socketPool.delete(
|
|
86912
|
+
this.socketPool.delete(tag2);
|
|
86752
86913
|
try {
|
|
86753
86914
|
await entry.client.close({
|
|
86754
86915
|
reason: "socket pool release",
|
|
86755
86916
|
skipLogout: true
|
|
86756
86917
|
});
|
|
86757
|
-
log?.log?.(`[SocketPool] Closed socket for tag=${
|
|
86918
|
+
log?.log?.(`[SocketPool] Closed socket for tag=${tag2}`);
|
|
86758
86919
|
} catch (e) {
|
|
86759
|
-
log?.warn?.(`[SocketPool] Error closing socket for tag=${
|
|
86920
|
+
log?.warn?.(`[SocketPool] Error closing socket for tag=${tag2}: ${e}`);
|
|
86760
86921
|
}
|
|
86761
86922
|
}
|
|
86762
86923
|
/**
|
|
86763
86924
|
* Force-close a socket by tag.
|
|
86764
86925
|
* Used to preempt existing connections before acquiring a new one.
|
|
86765
86926
|
*/
|
|
86766
|
-
async forceClosePooledSocket(
|
|
86927
|
+
async forceClosePooledSocket(tag2, logger) {
|
|
86767
86928
|
const log = logger ?? this.logger;
|
|
86768
|
-
const entry = this.socketPool.get(
|
|
86929
|
+
const entry = this.socketPool.get(tag2);
|
|
86769
86930
|
if (!entry) return false;
|
|
86770
86931
|
if (entry.idleCloseTimer) {
|
|
86771
86932
|
clearTimeout(entry.idleCloseTimer);
|
|
@@ -86777,16 +86938,16 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86777
86938
|
} catch {}
|
|
86778
86939
|
entry.generalPermitRelease = void 0;
|
|
86779
86940
|
}
|
|
86780
|
-
log?.debug?.(`[SocketPool] Force-closing socket for tag=${
|
|
86781
|
-
this.socketPool.delete(
|
|
86941
|
+
log?.debug?.(`[SocketPool] Force-closing socket for tag=${tag2}`);
|
|
86942
|
+
this.socketPool.delete(tag2);
|
|
86782
86943
|
try {
|
|
86783
86944
|
await entry.client.close({
|
|
86784
86945
|
reason: "force closed",
|
|
86785
86946
|
skipLogout: true
|
|
86786
86947
|
});
|
|
86787
|
-
log?.log?.(`[SocketPool] Force-closed socket for tag=${
|
|
86948
|
+
log?.log?.(`[SocketPool] Force-closed socket for tag=${tag2}`);
|
|
86788
86949
|
} catch (e) {
|
|
86789
|
-
log?.warn?.(`[SocketPool] Error during force-close for tag=${
|
|
86950
|
+
log?.warn?.(`[SocketPool] Error during force-close for tag=${tag2}: ${e}`);
|
|
86790
86951
|
}
|
|
86791
86952
|
return true;
|
|
86792
86953
|
}
|
|
@@ -86796,7 +86957,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86796
86957
|
async cleanupSocketPool() {
|
|
86797
86958
|
const entries = Array.from(this.socketPool.entries());
|
|
86798
86959
|
this.socketPool.clear();
|
|
86799
|
-
await Promise.allSettled(entries.map(async ([
|
|
86960
|
+
await Promise.allSettled(entries.map(async ([tag2, entry]) => {
|
|
86800
86961
|
try {
|
|
86801
86962
|
if (entry.idleCloseTimer) clearTimeout(entry.idleCloseTimer);
|
|
86802
86963
|
if (entry.generalPermitRelease) {
|
|
@@ -86805,7 +86966,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86805
86966
|
} catch {}
|
|
86806
86967
|
entry.generalPermitRelease = void 0;
|
|
86807
86968
|
}
|
|
86808
|
-
this.logger?.debug?.(`[SocketPool] Cleanup: closing tag=${
|
|
86969
|
+
this.logger?.debug?.(`[SocketPool] Cleanup: closing tag=${tag2}`);
|
|
86809
86970
|
await entry.client.close({
|
|
86810
86971
|
reason: "API cleanup",
|
|
86811
86972
|
skipLogout: true
|
|
@@ -86838,17 +86999,17 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86838
86999
|
* ```
|
|
86839
87000
|
*/
|
|
86840
87001
|
async createDedicatedSession(sessionKey, logger) {
|
|
86841
|
-
const
|
|
86842
|
-
(logger ?? this.logger)?.debug?.(`[SocketPool] createDedicatedSession sessionKey=${sessionKey} \u2192 tag=${
|
|
86843
|
-
return await this.acquirePooledSocket(
|
|
87002
|
+
const tag2 = this.resolveSocketTag(sessionKey);
|
|
87003
|
+
(logger ?? this.logger)?.debug?.(`[SocketPool] createDedicatedSession sessionKey=${sessionKey} \u2192 tag=${tag2}`);
|
|
87004
|
+
return await this.acquirePooledSocket(tag2, logger);
|
|
86844
87005
|
}
|
|
86845
87006
|
/**
|
|
86846
87007
|
* @deprecated Use forceClosePooledSocket via createDedicatedSession instead.
|
|
86847
87008
|
* Force-close a dedicated client if it exists.
|
|
86848
87009
|
*/
|
|
86849
87010
|
async forceCloseDedicatedClient(sessionKey, logger) {
|
|
86850
|
-
const
|
|
86851
|
-
return await this.forceClosePooledSocket(
|
|
87011
|
+
const tag2 = this.resolveSocketTag(sessionKey);
|
|
87012
|
+
return await this.forceClosePooledSocket(tag2, logger);
|
|
86852
87013
|
}
|
|
86853
87014
|
/**
|
|
86854
87015
|
* @deprecated Cleanup handled by cleanupSocketPool now.
|
|
@@ -86863,8 +87024,8 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86863
87024
|
const debugCfg = this.client.getDebugConfig?.();
|
|
86864
87025
|
if (debugCfg) {
|
|
86865
87026
|
const sid = this.client.getSocketSessionId?.();
|
|
86866
|
-
const
|
|
86867
|
-
eventTraceLog(debugCfg, this.logger,
|
|
87027
|
+
const tag2 = sid ? `ReolinkSimpleEvent sid=${sid}` : "ReolinkSimpleEvent";
|
|
87028
|
+
eventTraceLog(debugCfg, this.logger, tag2, `dispatch type=${evt.type} channel=${evt.channel} timestamp=${evt.timestamp}`);
|
|
86868
87029
|
}
|
|
86869
87030
|
for (const cb of this.simpleEventListeners) try {
|
|
86870
87031
|
Promise.resolve(cb(evt)).catch((e) => {
|
|
@@ -87462,7 +87623,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
87462
87623
|
return;
|
|
87463
87624
|
}
|
|
87464
87625
|
entry.startInFlight = (async () => {
|
|
87465
|
-
const { BaichuanVideoStream: BaichuanVideoStream2 } = await import("./BaichuanVideoStream-
|
|
87626
|
+
const { BaichuanVideoStream: BaichuanVideoStream2 } = await import("./BaichuanVideoStream-2M4UO7MB-CB-tIHMA.mjs");
|
|
87466
87627
|
const sessionKey = `live:object-detections:ch${entry.channel}:${entry.profile}`;
|
|
87467
87628
|
const dedicated = await this.createDedicatedSession(sessionKey);
|
|
87468
87629
|
const stream = new BaichuanVideoStream2({
|
|
@@ -88029,8 +88190,8 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
88029
88190
|
const idleTimeoutMs = options?.idleTimeoutMs ?? 3e4;
|
|
88030
88191
|
const sessionKey = `talk:${options?.deviceId ?? "unknown"}:ch${channel}:${Date.now()}`;
|
|
88031
88192
|
logger?.info?.(`[DedicatedTalk] Creating session: ${sessionKey} (idleTimeout=${idleTimeoutMs}ms)`);
|
|
88032
|
-
const
|
|
88033
|
-
const { client: dedicatedClient, release } = await this.acquirePooledSocket(
|
|
88193
|
+
const tag2 = this.resolveSocketTag(sessionKey);
|
|
88194
|
+
const { client: dedicatedClient, release } = await this.acquirePooledSocket(tag2, logger);
|
|
88034
88195
|
const summary = this.getSocketPoolSummary();
|
|
88035
88196
|
logger?.info?.(`[DedicatedTalk] Session created [sessions: ${summary.count} active${summary.count > 0 ? ` (${summary.tags.join(", ")})` : ""}]`);
|
|
88036
88197
|
try {
|
|
@@ -88143,8 +88304,8 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
88143
88304
|
}
|
|
88144
88305
|
/** SetNetPort via Baichuan: cmd_id 36 (enable/disable rtsp/rtmp/onvif/http/https) */
|
|
88145
88306
|
async setPortEnabled(params) {
|
|
88146
|
-
const
|
|
88147
|
-
const xml = `<?xml version="1.0" encoding="UTF-8" ?><body><${
|
|
88307
|
+
const tag2 = `${params.port[0].toUpperCase()}${params.port.slice(1)}Port`;
|
|
88308
|
+
const xml = `<?xml version="1.0" encoding="UTF-8" ?><body><${tag2} version="1.1"><enable>${params.enable ? 1 : 0}</enable></${tag2}></body>`;
|
|
88148
88309
|
await this.sendXml({
|
|
88149
88310
|
cmdId: 36,
|
|
88150
88311
|
payloadXml: xml
|
|
@@ -88175,13 +88336,13 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
88175
88336
|
*/
|
|
88176
88337
|
async setPortConfig(patch) {
|
|
88177
88338
|
const blocks = [];
|
|
88178
|
-
const append = (
|
|
88339
|
+
const append = (tag2, portField, cfg) => {
|
|
88179
88340
|
if (!cfg) return;
|
|
88180
88341
|
if (cfg.port === void 0 && cfg.enable === void 0) return;
|
|
88181
88342
|
const inner = [];
|
|
88182
88343
|
if (cfg.port !== void 0) inner.push(`<${portField}>${cfg.port}</${portField}>`);
|
|
88183
88344
|
if (cfg.enable !== void 0) inner.push(`<enable>${cfg.enable ? 1 : 0}</enable>`);
|
|
88184
|
-
blocks.push(`<${
|
|
88345
|
+
blocks.push(`<${tag2} version="1.1">${inner.join("")}</${tag2}>`);
|
|
88185
88346
|
};
|
|
88186
88347
|
append("ServerPort", "serverPort", patch.server);
|
|
88187
88348
|
append("HttpPort", "httpPort", patch.http);
|
|
@@ -88695,8 +88856,8 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
88695
88856
|
];
|
|
88696
88857
|
const current = await this.getEncXml(ch);
|
|
88697
88858
|
let updated = null;
|
|
88698
|
-
for (const
|
|
88699
|
-
const sectionRe = new RegExp(`(<${
|
|
88859
|
+
for (const tag2 of candidateTags) {
|
|
88860
|
+
const sectionRe = new RegExp(`(<${tag2}[^>]*>[\\s\\S]*?<videoEncType>)(\\d+)(</videoEncType>)`);
|
|
88700
88861
|
if (!sectionRe.test(current)) continue;
|
|
88701
88862
|
const next = current.replace(sectionRe, `$1${desired}$3`);
|
|
88702
88863
|
if (next !== current) {
|
|
@@ -89226,9 +89387,9 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
89226
89387
|
const streamType = params.streamType;
|
|
89227
89388
|
const logger = params.logger ?? this.logger;
|
|
89228
89389
|
const sessionKey = params.deviceId ? `replay:${params.deviceId}:ch${channel}` : `replay:standalone:ch${channel}:${Date.now()}`;
|
|
89229
|
-
const
|
|
89230
|
-
logger?.debug?.(`[startRecordingReplayStreamStandalone] sessionKey=${sessionKey} -> tag=${
|
|
89231
|
-
const { client: dedicatedClient, release: releaseDedicatedClient } = await this.acquirePooledSocket(
|
|
89390
|
+
const tag2 = this.resolveSocketTag(sessionKey);
|
|
89391
|
+
logger?.debug?.(`[startRecordingReplayStreamStandalone] sessionKey=${sessionKey} -> tag=${tag2}`);
|
|
89392
|
+
const { client: dedicatedClient, release: releaseDedicatedClient } = await this.acquirePooledSocket(tag2, logger);
|
|
89232
89393
|
const uid = await this.ensureUidForRecordings(channel, void 0);
|
|
89233
89394
|
const payloadXml = buildFileInfoListReplayByNameXml({
|
|
89234
89395
|
channel,
|
|
@@ -89331,8 +89492,8 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
89331
89492
|
const streamType = params.streamType;
|
|
89332
89493
|
const logger = params.logger ?? this.logger;
|
|
89333
89494
|
const sessionKey = params.deviceId ? `replay:${params.deviceId}:ch${channel}` : `replay:nvr:ch${channel}:${Date.now()}`;
|
|
89334
|
-
const
|
|
89335
|
-
const { client: dedicatedClient, release: releaseDedicatedClient } = await this.acquirePooledSocket(
|
|
89495
|
+
const tag2 = this.resolveSocketTag(sessionKey);
|
|
89496
|
+
const { client: dedicatedClient, release: releaseDedicatedClient } = await this.acquirePooledSocket(tag2, logger);
|
|
89336
89497
|
let uid;
|
|
89337
89498
|
try {
|
|
89338
89499
|
uid = await this.ensureUidForRecordings(channel, void 0);
|
|
@@ -91559,6 +91720,73 @@ ${stderr}`));
|
|
|
91559
91720
|
return result;
|
|
91560
91721
|
}
|
|
91561
91722
|
/**
|
|
91723
|
+
* Read the PTZ guard point ("monitoring point" in the app).
|
|
91724
|
+
*
|
|
91725
|
+
* cmd_id: 332
|
|
91726
|
+
*
|
|
91727
|
+
* Returns `null` when the camera has no readable guard configuration — never
|
|
91728
|
+
* a fabricated "none", because that answer is what a consumer uses to decide
|
|
91729
|
+
* whether to move the head.
|
|
91730
|
+
*/
|
|
91731
|
+
async getPtzGuard(channel) {
|
|
91732
|
+
const ch = this.normalizeChannel(channel);
|
|
91733
|
+
return parsePtzGuardXml(await this.sendXml({
|
|
91734
|
+
cmdId: 332,
|
|
91735
|
+
channel: ch,
|
|
91736
|
+
channelIdOverride: ch,
|
|
91737
|
+
extensionXml: buildPtzGuardExtensionXml(ch, "read"),
|
|
91738
|
+
messageClass: BC_CLASS_MODERN_24,
|
|
91739
|
+
streamType: 0
|
|
91740
|
+
}));
|
|
91741
|
+
}
|
|
91742
|
+
/**
|
|
91743
|
+
* Configure the PTZ guard point.
|
|
91744
|
+
*
|
|
91745
|
+
* cmd_id: 331 with `<command>setGrd</command>`
|
|
91746
|
+
*
|
|
91747
|
+
* `setPosition` pins the guard point to the head's CURRENT position. Leave it
|
|
91748
|
+
* false when only changing the timeout or the enable flag: re-sending it
|
|
91749
|
+
* would silently move a point the operator had already placed.
|
|
91750
|
+
*/
|
|
91751
|
+
async setPtzGuard(options, channel) {
|
|
91752
|
+
const ch = this.normalizeChannel(channel);
|
|
91753
|
+
await this.sendXml({
|
|
91754
|
+
cmdId: 331,
|
|
91755
|
+
channel: ch,
|
|
91756
|
+
channelIdOverride: ch,
|
|
91757
|
+
extensionXml: buildPtzGuardExtensionXml(ch, "write"),
|
|
91758
|
+
payloadXml: buildPtzGuardSetXml({
|
|
91759
|
+
channelId: ch,
|
|
91760
|
+
enabled: options.enabled,
|
|
91761
|
+
timeoutSeconds: options.timeoutSeconds,
|
|
91762
|
+
setPosition: options.setPosition === true
|
|
91763
|
+
}),
|
|
91764
|
+
messageClass: BC_CLASS_MODERN_24,
|
|
91765
|
+
streamType: 0
|
|
91766
|
+
});
|
|
91767
|
+
}
|
|
91768
|
+
/**
|
|
91769
|
+
* Send the head to the guard point NOW.
|
|
91770
|
+
*
|
|
91771
|
+
* cmd_id: 331 with `<command>toGrd</command>`
|
|
91772
|
+
*
|
|
91773
|
+
* This is the closest thing Baichuan has to a "go home": the guard point is
|
|
91774
|
+
* an operator-placed position, unlike preset slot 0 which merely happens to
|
|
91775
|
+
* be first.
|
|
91776
|
+
*/
|
|
91777
|
+
async goToPtzGuard(channel) {
|
|
91778
|
+
const ch = this.normalizeChannel(channel);
|
|
91779
|
+
await this.sendXml({
|
|
91780
|
+
cmdId: 331,
|
|
91781
|
+
channel: ch,
|
|
91782
|
+
channelIdOverride: ch,
|
|
91783
|
+
extensionXml: buildPtzGuardExtensionXml(ch, "write"),
|
|
91784
|
+
payloadXml: buildPtzGuardGoXml(ch),
|
|
91785
|
+
messageClass: BC_CLASS_MODERN_24,
|
|
91786
|
+
streamType: 0
|
|
91787
|
+
});
|
|
91788
|
+
}
|
|
91789
|
+
/**
|
|
91562
91790
|
* Read zoom/focus min/max/current positions.
|
|
91563
91791
|
* cmd_id: 294 (MSG_ID_GET_ZOOM_FOCUS)
|
|
91564
91792
|
*/
|
|
@@ -91651,10 +91879,10 @@ ${stderr}`));
|
|
|
91651
91879
|
notifyD2cDisc() {
|
|
91652
91880
|
const now = Date.now();
|
|
91653
91881
|
this.lastD2cDiscAtMs = now;
|
|
91654
|
-
const streamingTags = Array.from(this.socketPool.keys()).filter((
|
|
91882
|
+
const streamingTags = Array.from(this.socketPool.keys()).filter((tag2) => tag2.startsWith("streaming:"));
|
|
91655
91883
|
if (streamingTags.length > 0) {
|
|
91656
91884
|
this.logger?.log?.(`[D2C_DISC] Force-closing ${streamingTags.length} streaming socket(s): ${streamingTags.join(", ")}`);
|
|
91657
|
-
for (const
|
|
91885
|
+
for (const tag2 of streamingTags) this.forceClosePooledSocket(tag2, this.logger).catch(() => {});
|
|
91658
91886
|
}
|
|
91659
91887
|
const immediateCooldownUntil = now + _ReolinkBaichuanApi.D2C_DISC_IMMEDIATE_COOLDOWN_MS;
|
|
91660
91888
|
const existing = this.socketPoolCooldowns.get(this.host);
|
|
@@ -91687,15 +91915,15 @@ ${stderr}`));
|
|
|
91687
91915
|
* Returns undefined if the client is not in the pool (e.g. it's the general socket used directly).
|
|
91688
91916
|
*/
|
|
91689
91917
|
findSocketTagForClient(client) {
|
|
91690
|
-
for (const [
|
|
91918
|
+
for (const [tag2, entry] of this.socketPool) if (entry.client === client) return tag2;
|
|
91691
91919
|
}
|
|
91692
91920
|
/**
|
|
91693
91921
|
* Reset the consecutive stream-start timeout counter for a streaming socket.
|
|
91694
91922
|
* Called on successful stream start.
|
|
91695
91923
|
*/
|
|
91696
91924
|
resetStreamTimeoutCounter(client) {
|
|
91697
|
-
const
|
|
91698
|
-
if (
|
|
91925
|
+
const tag2 = this.findSocketTagForClient(client);
|
|
91926
|
+
if (tag2) this.consecutiveStreamTimeouts.delete(tag2);
|
|
91699
91927
|
}
|
|
91700
91928
|
/**
|
|
91701
91929
|
* Track a stream-start timeout on a streaming socket.
|
|
@@ -91703,14 +91931,14 @@ ${stderr}`));
|
|
|
91703
91931
|
* socket so the next attempt creates a fresh connection.
|
|
91704
91932
|
*/
|
|
91705
91933
|
trackStreamTimeout(client) {
|
|
91706
|
-
const
|
|
91707
|
-
if (!
|
|
91708
|
-
const count = (this.consecutiveStreamTimeouts.get(
|
|
91709
|
-
this.consecutiveStreamTimeouts.set(
|
|
91934
|
+
const tag2 = this.findSocketTagForClient(client);
|
|
91935
|
+
if (!tag2 || !tag2.startsWith("streaming:")) return;
|
|
91936
|
+
const count = (this.consecutiveStreamTimeouts.get(tag2) ?? 0) + 1;
|
|
91937
|
+
this.consecutiveStreamTimeouts.set(tag2, count);
|
|
91710
91938
|
if (count >= _ReolinkBaichuanApi.MAX_CONSECUTIVE_STREAM_TIMEOUTS) {
|
|
91711
|
-
this.logger?.warn?.(`[SocketPool] ${count} consecutive stream timeouts on tag=${
|
|
91712
|
-
this.consecutiveStreamTimeouts.delete(
|
|
91713
|
-
this.forceClosePooledSocket(
|
|
91939
|
+
this.logger?.warn?.(`[SocketPool] ${count} consecutive stream timeouts on tag=${tag2}, force-closing socket`);
|
|
91940
|
+
this.consecutiveStreamTimeouts.delete(tag2);
|
|
91941
|
+
this.forceClosePooledSocket(tag2, this.logger).catch(() => {});
|
|
91714
91942
|
}
|
|
91715
91943
|
}
|
|
91716
91944
|
/**
|
|
@@ -92577,8 +92805,20 @@ ${xml}`);
|
|
|
92577
92805
|
const support = supportResult.status === "fulfilled" ? supportResult.value : void 0;
|
|
92578
92806
|
const abilities = abilitiesResult.status === "fulfilled" ? abilitiesResult.value : void 0;
|
|
92579
92807
|
const supportItem = getSupportItemForChannel(support, ch);
|
|
92808
|
+
let model;
|
|
92809
|
+
try {
|
|
92810
|
+
const info = await this.getInfo(void 0, { timeoutMs: 5e3 });
|
|
92811
|
+
model = typeof info?.type === "string" ? info.type : void 0;
|
|
92812
|
+
} catch (e) {
|
|
92813
|
+
this.logger.debug("[ReolinkBaichuanApi] getDeviceCapabilities: getInfo(type) failed", {
|
|
92814
|
+
host: this.host,
|
|
92815
|
+
channel: ch,
|
|
92816
|
+
err: e instanceof Error ? e.message : String(e)
|
|
92817
|
+
});
|
|
92818
|
+
}
|
|
92580
92819
|
const capabilities = computeDeviceCapabilities({
|
|
92581
92820
|
channel: ch,
|
|
92821
|
+
...model != null && { model },
|
|
92582
92822
|
...support != null && { support },
|
|
92583
92823
|
...abilities != null && { abilities }
|
|
92584
92824
|
});
|
|
@@ -93347,7 +93587,7 @@ ${xml}`);
|
|
|
93347
93587
|
* @returns Test results for all stream types and profiles
|
|
93348
93588
|
*/
|
|
93349
93589
|
async testChannelStreams(channel, logger) {
|
|
93350
|
-
const { testChannelStreams } = await import("./DiagnosticsTools-
|
|
93590
|
+
const { testChannelStreams } = await import("./DiagnosticsTools-GOFDNGAY-9NV95vRN.mjs");
|
|
93351
93591
|
return await testChannelStreams({
|
|
93352
93592
|
api: this,
|
|
93353
93593
|
channel: this.normalizeChannel(channel),
|
|
@@ -93363,7 +93603,7 @@ ${xml}`);
|
|
|
93363
93603
|
* @returns Complete diagnostics for all channels and streams
|
|
93364
93604
|
*/
|
|
93365
93605
|
async collectMultifocalDiagnostics(logger) {
|
|
93366
|
-
const { collectMultifocalDiagnostics } = await import("./DiagnosticsTools-
|
|
93606
|
+
const { collectMultifocalDiagnostics } = await import("./DiagnosticsTools-GOFDNGAY-9NV95vRN.mjs");
|
|
93367
93607
|
return await collectMultifocalDiagnostics({
|
|
93368
93608
|
api: this,
|
|
93369
93609
|
logger
|
|
@@ -150155,15 +150395,39 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
150155
150395
|
*/
|
|
150156
150396
|
async resolveHomePreset() {
|
|
150157
150397
|
const chosen = this.config.get("homePresetId") ?? "";
|
|
150158
|
-
if (chosen
|
|
150398
|
+
if (chosen !== "") return {
|
|
150399
|
+
presetId: chosen,
|
|
150400
|
+
source: "operator"
|
|
150401
|
+
};
|
|
150402
|
+
if ((await this.readGuardStatus())?.valid === true) return {
|
|
150159
150403
|
presetId: null,
|
|
150160
|
-
source: "
|
|
150404
|
+
source: "native"
|
|
150161
150405
|
};
|
|
150162
150406
|
return {
|
|
150163
|
-
presetId:
|
|
150164
|
-
source: "
|
|
150407
|
+
presetId: null,
|
|
150408
|
+
source: "none"
|
|
150165
150409
|
};
|
|
150166
150410
|
}
|
|
150411
|
+
/**
|
|
150412
|
+
* The camera's guard-point configuration, or `null` when it cannot be read.
|
|
150413
|
+
*
|
|
150414
|
+
* Never a fabricated "no guard configured": that answer decides whether
|
|
150415
|
+
* `goHome` moves the head, and an unreadable camera is not evidence that no
|
|
150416
|
+
* guard point exists (D393).
|
|
150417
|
+
*/
|
|
150418
|
+
async readGuardStatus() {
|
|
150419
|
+
try {
|
|
150420
|
+
const api = await this.ensureApi();
|
|
150421
|
+
const channel = this.getChannel();
|
|
150422
|
+
return await api.getPtzGuard(channel);
|
|
150423
|
+
} catch (err) {
|
|
150424
|
+
this.ctx.logger.warn("reolink guard point unreadable — goHome will fall back", {
|
|
150425
|
+
tags: { deviceId: this.id },
|
|
150426
|
+
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
150427
|
+
});
|
|
150428
|
+
return null;
|
|
150429
|
+
}
|
|
150430
|
+
}
|
|
150167
150431
|
getChannel() {
|
|
150168
150432
|
const ch = this.config.get("channel");
|
|
150169
150433
|
if (typeof ch === "number" && ch >= 0) return ch;
|
|
@@ -153616,7 +153880,11 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153616
153880
|
goHome: async ({ deviceId }) => {
|
|
153617
153881
|
if (deviceId !== this.id) return;
|
|
153618
153882
|
const home = await this.resolveHomePreset();
|
|
153619
|
-
if (home.source === "
|
|
153883
|
+
if (home.source === "native") {
|
|
153884
|
+
await (await this.ensureApi()).goToPtzGuard(this.getChannel());
|
|
153885
|
+
return;
|
|
153886
|
+
}
|
|
153887
|
+
if (home.source === "none" || home.presetId === null) throw new Error("No home preset configured for this camera, and it has no guard point set — set one in the PTZ settings, or place a guard point on the camera");
|
|
153620
153888
|
const id = Number(home.presetId);
|
|
153621
153889
|
if (!Number.isFinite(id)) throw new Error(`Configured home preset is not a Baichuan id: ${home.presetId}`);
|
|
153622
153890
|
const api = await this.ensureApi();
|
|
@@ -153630,6 +153898,41 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153630
153898
|
};
|
|
153631
153899
|
return this.resolveHomePreset();
|
|
153632
153900
|
},
|
|
153901
|
+
captureHomeHere: async ({ deviceId }) => {
|
|
153902
|
+
if (deviceId !== this.id) return;
|
|
153903
|
+
const current = await this.readGuardStatus();
|
|
153904
|
+
const api = await this.ensureApi();
|
|
153905
|
+
const channel = this.getChannel();
|
|
153906
|
+
await api.setPtzGuard({
|
|
153907
|
+
enabled: current?.enabled ?? false,
|
|
153908
|
+
timeoutSeconds: current?.timeoutSeconds ?? 60,
|
|
153909
|
+
setPosition: true
|
|
153910
|
+
}, channel);
|
|
153911
|
+
this.ctx.logger.info("reolink: guard point pinned to the current position", { tags: { deviceId: this.id } });
|
|
153912
|
+
},
|
|
153913
|
+
getHomeReturn: async ({ deviceId }) => {
|
|
153914
|
+
if (deviceId !== this.id) return null;
|
|
153915
|
+
const guard = await this.readGuardStatus();
|
|
153916
|
+
return guard === null ? null : {
|
|
153917
|
+
enabled: guard.enabled,
|
|
153918
|
+
seconds: guard.timeoutSeconds
|
|
153919
|
+
};
|
|
153920
|
+
},
|
|
153921
|
+
setHomeReturn: async ({ deviceId, enabled, seconds }) => {
|
|
153922
|
+
if (deviceId !== this.id) return;
|
|
153923
|
+
await (await this.ensureApi()).setPtzGuard({
|
|
153924
|
+
enabled,
|
|
153925
|
+
timeoutSeconds: seconds,
|
|
153926
|
+
setPosition: false
|
|
153927
|
+
}, this.getChannel());
|
|
153928
|
+
this.ctx.logger.info("reolink: guard point idle-return updated", {
|
|
153929
|
+
tags: { deviceId: this.id },
|
|
153930
|
+
meta: {
|
|
153931
|
+
enabled,
|
|
153932
|
+
seconds
|
|
153933
|
+
}
|
|
153934
|
+
});
|
|
153935
|
+
},
|
|
153633
153936
|
setHomePreset: async ({ deviceId, presetId }) => {
|
|
153634
153937
|
if (deviceId !== this.id) return;
|
|
153635
153938
|
await this.config.setAll({ homePresetId: presetId ?? "" });
|