@camstack/addon-provider-reolink 1.2.138 → 1.2.140
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 +807 -302
- package/dist/addon.mjs +657 -152
- 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-vl6nBE8d.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).
|
|
@@ -31652,14 +31652,81 @@ function summarisePrivacyAudio(profiles) {
|
|
|
31652
31652
|
if (profiles.length === 0) return null;
|
|
31653
31653
|
return profiles.some((p) => p.audioEnabled);
|
|
31654
31654
|
}
|
|
31655
|
+
/**
|
|
31656
|
+
* What a preset id DOES on this camera.
|
|
31657
|
+
*
|
|
31658
|
+
* On several PTZ firmwares the preset namespace is shared: low ids are stored
|
|
31659
|
+
* positions, a reserved band triggers camera FUNCTIONS. The dispensa camera
|
|
31660
|
+
* (Hikvision) returns its own reserved band in the preset list, named by the
|
|
31661
|
+
* firmware:
|
|
31662
|
+
*
|
|
31663
|
+
* 34 Back to origin · 39 Day mode · 40 Night mode · 46 Day/Night Auto Mode
|
|
31664
|
+
* 92 Set manual limits · 93 Save manual limits · 94 Remote reboot
|
|
31665
|
+
* 95 Call OSD menu
|
|
31666
|
+
*
|
|
31667
|
+
* We used to hand all of these to the UI as ordinary presets. Saving over one
|
|
31668
|
+
* is what produced `PUT /ISAPI/PTZCtrl/channels/1/presets/34 -> HTTP 500`: the
|
|
31669
|
+
* camera refusing to let "Back to origin" be overwritten, surfaced to the
|
|
31670
|
+
* operator as a transport failure.
|
|
31671
|
+
*
|
|
31672
|
+
* A function preset stays fully usable as a DESTINATION -- `goToPreset('34')`
|
|
31673
|
+
* is the single most useful thing that camera can do. Only `savePreset` and
|
|
31674
|
+
* `deletePreset` are gated.
|
|
31675
|
+
*/
|
|
31676
|
+
var PtzPresetFunctionSchema = _enum([
|
|
31677
|
+
"home",
|
|
31678
|
+
"day-mode",
|
|
31679
|
+
"night-mode",
|
|
31680
|
+
"day-night-auto",
|
|
31681
|
+
"set-limits",
|
|
31682
|
+
"save-limits",
|
|
31683
|
+
"reboot",
|
|
31684
|
+
"osd-menu"
|
|
31685
|
+
]);
|
|
31655
31686
|
var PtzPresetSchema = object({
|
|
31656
31687
|
id: string(),
|
|
31657
|
-
name: string()
|
|
31688
|
+
name: string(),
|
|
31689
|
+
/**
|
|
31690
|
+
* The firmware function this id performs, when the provider KNOWS the id is
|
|
31691
|
+
* reserved. `null` = an ordinary stored position, OR a reserved id this
|
|
31692
|
+
* provider does not recognise -- the two are told apart by `writable`.
|
|
31693
|
+
*/
|
|
31694
|
+
fn: PtzPresetFunctionSchema.nullable(),
|
|
31695
|
+
/**
|
|
31696
|
+
* Whether `savePreset` / `deletePreset` may target this id. A provider sets
|
|
31697
|
+
* it false only for a band it KNOWS is reserved; where the firmware is not
|
|
31698
|
+
* documented it stays true and a refusal comes from the camera, named.
|
|
31699
|
+
*/
|
|
31700
|
+
writable: boolean()
|
|
31658
31701
|
});
|
|
31702
|
+
/**
|
|
31703
|
+
* Where the head is pointing -- per axis, and `null` when the driver cannot
|
|
31704
|
+
* read it.
|
|
31705
|
+
*
|
|
31706
|
+
* It used to be three REQUIRED numbers, which left a provider that cannot read
|
|
31707
|
+
* position no way to say so. All four said `0`:
|
|
31708
|
+
*
|
|
31709
|
+
* reolink `return { pan: 0, tilt: 0, zoom: 0 }` "until a position-query
|
|
31710
|
+
* path lands upstream"
|
|
31711
|
+
* hikvision stub, "firmware-dependent and frequently absent"
|
|
31712
|
+
* amcrest stub, "no generic absolute-position read for this model family"
|
|
31713
|
+
* onvif throws
|
|
31714
|
+
*
|
|
31715
|
+
* So every consumer asking where a camera points was told "perfectly centred"
|
|
31716
|
+
* by four cameras that had never looked. That is D393 -- a measurement that
|
|
31717
|
+
* FAILED is `null`, never `0`, and the type says so all the way to the
|
|
31718
|
+
* decision -- the same shape as the unreadable `statfs` folded into "0 bytes of
|
|
31719
|
+
* headroom", which evacuated a healthy disk.
|
|
31720
|
+
*
|
|
31721
|
+
* D393 also says to check for a narrower structural TWIN of the result type.
|
|
31722
|
+
* There is one: `PtzStatusSchema` extends this, so the lie had already
|
|
31723
|
+
* propagated into `getStatus`, which is the surface `getPosition`'s own comment
|
|
31724
|
+
* tells callers to migrate to.
|
|
31725
|
+
*/
|
|
31659
31726
|
var PtzPositionSchema = object({
|
|
31660
|
-
pan: number(),
|
|
31661
|
-
tilt: number(),
|
|
31662
|
-
zoom: number()
|
|
31727
|
+
pan: number().nullable(),
|
|
31728
|
+
tilt: number().nullable(),
|
|
31729
|
+
zoom: number().nullable()
|
|
31663
31730
|
});
|
|
31664
31731
|
var PtzMoveCommandSchema = object({
|
|
31665
31732
|
pan: number().optional(),
|
|
@@ -31681,7 +31748,72 @@ var PtzOptionsSchema = object({
|
|
|
31681
31748
|
maxPresets: number().optional(),
|
|
31682
31749
|
/** Whether the camera exposes a controllable autofocus toggle
|
|
31683
31750
|
* (boolean `hasX` per the getOptions availability convention). */
|
|
31684
|
-
hasAutofocus: boolean()
|
|
31751
|
+
hasAutofocus: boolean(),
|
|
31752
|
+
/**
|
|
31753
|
+
* How many distinct speeds this camera's NATIVE scale offers.
|
|
31754
|
+
*
|
|
31755
|
+
* The cap's `speed` is normalized 0..1 and each provider maps it down --
|
|
31756
|
+
* Baichuan 1..63, Dahua 1..8, ISAPI's percentage -100..100. The normalized
|
|
31757
|
+
* value hides how coarse that really is: asking an amcrest for `0.37` is
|
|
31758
|
+
* meaningless, because it has eight steps and three of them round to the
|
|
31759
|
+
* same one. A loop that tunes its own gain has to know the granularity of
|
|
31760
|
+
* the knob it is turning.
|
|
31761
|
+
*
|
|
31762
|
+
* `null` = the driver does not know, or the scale is continuous (ONVIF takes
|
|
31763
|
+
* a float). Never a made-up number.
|
|
31764
|
+
*/
|
|
31765
|
+
speedSteps: number().int().positive().nullable(),
|
|
31766
|
+
/**
|
|
31767
|
+
* How long ONE `move` pulse runs on this driver, ms.
|
|
31768
|
+
*
|
|
31769
|
+
* `move` is a self-terminating burst everywhere, but every provider hardcoded
|
|
31770
|
+
* its own duration in private -- 200 reolink, 350 amcrest, 500 hikvision,
|
|
31771
|
+
* 1000 onvif -- so no caller could read it. It is the DENOMINATOR of "how far
|
|
31772
|
+
* did the head travel per pulse": without it a measured displacement has no
|
|
31773
|
+
* gain to be divided into.
|
|
31774
|
+
*
|
|
31775
|
+
* `null` = the driver cannot say.
|
|
31776
|
+
*/
|
|
31777
|
+
moveImpulseMs: number().int().positive().nullable()
|
|
31778
|
+
});
|
|
31779
|
+
/**
|
|
31780
|
+
* Which preset `goHome` goes to, and WHO decided.
|
|
31781
|
+
*
|
|
31782
|
+
* `goHome` used to be three vendor "conventions" written in three comments --
|
|
31783
|
+
* Reolink preset 0, Hikvision preset '1', Dahua preset 1 -- and on this fleet
|
|
31784
|
+
* not one of the three cameras honours its own:
|
|
31785
|
+
*
|
|
31786
|
+
* 592 Videocamera camera Daniel (reolink) preset 0 holds "stanza"
|
|
31787
|
+
* 1438 Videocamera dispensa (hikvision) preset 1 holds "Credenza"
|
|
31788
|
+
* 3836 Videocamera studio (amcrest) preset 1 holds "Preset1"
|
|
31789
|
+
*
|
|
31790
|
+
* So `goHome` meant "go wherever the operator happened to save in slot 0 or 1",
|
|
31791
|
+
* and two of the three swallowed the failure in a bare `catch`. For autotrack
|
|
31792
|
+
* this is the HOTTEST path -- it runs every time a subject is released -- so it
|
|
31793
|
+
* needs an answer that is true per camera and audible when it is missing.
|
|
31794
|
+
*
|
|
31795
|
+
* `source` is what makes it honest:
|
|
31796
|
+
* - `operator` -- picked in the UI, stored by the provider.
|
|
31797
|
+
* - `firmware` -- the camera itself named a preset `fn: 'home'` (Hikvision's
|
|
31798
|
+
* "Back to origin"), adopted as the default with nothing to configure.
|
|
31799
|
+
* - `native` -- the driver homes WITHOUT a preset at all. ONVIF does:
|
|
31800
|
+
* `goHome` is `ptzAbsoluteMove({x:0, y:0, zoom:0})`. `presetId` is null and
|
|
31801
|
+
* that is not a failure -- there is nothing to pick, and the UI must offer
|
|
31802
|
+
* no picker.
|
|
31803
|
+
* - `none` -- nothing is configured and the camera names nothing.
|
|
31804
|
+
* `goHome` REFUSES rather than moving the head somewhere arbitrary.
|
|
31805
|
+
*
|
|
31806
|
+
* `source === 'none'` is the refusal condition, NOT `presetId === null`: the
|
|
31807
|
+
* native case has no preset and homes perfectly well.
|
|
31808
|
+
*/
|
|
31809
|
+
var PtzHomePresetSchema = object({
|
|
31810
|
+
presetId: string().nullable(),
|
|
31811
|
+
source: _enum([
|
|
31812
|
+
"operator",
|
|
31813
|
+
"firmware",
|
|
31814
|
+
"native",
|
|
31815
|
+
"none"
|
|
31816
|
+
])
|
|
31685
31817
|
});
|
|
31686
31818
|
var ptzCapability = {
|
|
31687
31819
|
name: "ptz",
|
|
@@ -31722,7 +31854,66 @@ var ptzCapability = {
|
|
|
31722
31854
|
auth: "admin"
|
|
31723
31855
|
}),
|
|
31724
31856
|
getOptions: method(object({ deviceId: number() }), PtzOptionsSchema),
|
|
31857
|
+
/**
|
|
31858
|
+
* Move to the camera's configured home preset. THROWS when
|
|
31859
|
+
* `getHomePreset()` resolves to `none` -- a head that did not move must
|
|
31860
|
+
* never look like a head that went home.
|
|
31861
|
+
*/
|
|
31725
31862
|
goHome: method(object({ deviceId: number() }), _void(), { kind: "mutation" }),
|
|
31863
|
+
/** Which preset `goHome` targets, and who decided it. */
|
|
31864
|
+
getHomePreset: method(object({ deviceId: number() }), PtzHomePresetSchema),
|
|
31865
|
+
/**
|
|
31866
|
+
* Pin the home preset for this camera. `null` clears the operator's choice
|
|
31867
|
+
* and falls back to whatever the firmware names, or to `none`.
|
|
31868
|
+
*/
|
|
31869
|
+
setHomePreset: method(object({
|
|
31870
|
+
deviceId: number(),
|
|
31871
|
+
presetId: string().nullable()
|
|
31872
|
+
}), _void(), {
|
|
31873
|
+
kind: "mutation",
|
|
31874
|
+
auth: "admin"
|
|
31875
|
+
}),
|
|
31876
|
+
/**
|
|
31877
|
+
* Make WHERE THE HEAD IS POINTING RIGHT NOW this camera's home.
|
|
31878
|
+
*
|
|
31879
|
+
* `setHomePreset` takes a preset id, and a preset id is the wrong shape for
|
|
31880
|
+
* the most natural thing an operator does: aim the camera, then say "here".
|
|
31881
|
+
* It is also the only shape a camera whose home is NOT a preset can accept
|
|
31882
|
+
* -- Reolink's home is a guard point, a stored position with no id at all,
|
|
31883
|
+
* and hiding the control for that case (as the UI briefly did) left the one
|
|
31884
|
+
* vendor with a native home unable to set it.
|
|
31885
|
+
*
|
|
31886
|
+
* Each provider does it its own way:
|
|
31887
|
+
* reolink `setGrd` with `needSetPos`, pinning the guard point here
|
|
31888
|
+
* hikvision / amcrest save a preset, then pin it as home
|
|
31889
|
+
* onvif REFUSES -- its home is the PTZ origin and cannot be moved
|
|
31890
|
+
*
|
|
31891
|
+
* A provider that cannot honour it throws rather than silently doing
|
|
31892
|
+
* nothing: an operator who pressed "set home here" and saw no error would
|
|
31893
|
+
* believe it took.
|
|
31894
|
+
*/
|
|
31895
|
+
captureHomeHere: method(object({ deviceId: number() }), _void(), {
|
|
31896
|
+
kind: "mutation",
|
|
31897
|
+
auth: "admin"
|
|
31898
|
+
}),
|
|
31899
|
+
/**
|
|
31900
|
+
* How long the camera waits, idle, before returning home on its own --
|
|
31901
|
+
* `null` when it has no such behaviour.
|
|
31902
|
+
*
|
|
31903
|
+
* Reolink's guard point carries one (`timeout`, freely settable in
|
|
31904
|
+
* seconds). It is a real function of the camera that we did not expose at
|
|
31905
|
+
* all, and it belongs next to the home it governs rather than in a vendor
|
|
31906
|
+
* settings page nobody associates with Home.
|
|
31907
|
+
*/
|
|
31908
|
+
getHomeReturnSeconds: method(object({ deviceId: number() }), number().int().nullable()),
|
|
31909
|
+
/** Set the idle-return delay. Refused by a camera that has no such timer. */
|
|
31910
|
+
setHomeReturnSeconds: method(object({
|
|
31911
|
+
deviceId: number(),
|
|
31912
|
+
seconds: number().int().min(0).max(3600)
|
|
31913
|
+
}), _void(), {
|
|
31914
|
+
kind: "mutation",
|
|
31915
|
+
auth: "admin"
|
|
31916
|
+
}),
|
|
31726
31917
|
/**
|
|
31727
31918
|
* Pull the current PTZ position. Redundant with the auto-injected
|
|
31728
31919
|
* `getStatus` method (see `status` below); kept for callers that
|
|
@@ -41381,6 +41572,12 @@ Object.freeze({
|
|
|
41381
41572
|
addonId: null,
|
|
41382
41573
|
access: "create"
|
|
41383
41574
|
},
|
|
41575
|
+
"ptz.captureHomeHere": {
|
|
41576
|
+
capName: "ptz",
|
|
41577
|
+
capScope: "device",
|
|
41578
|
+
addonId: null,
|
|
41579
|
+
access: "create"
|
|
41580
|
+
},
|
|
41384
41581
|
"ptz.continuousMove": {
|
|
41385
41582
|
capName: "ptz",
|
|
41386
41583
|
capScope: "device",
|
|
@@ -41393,6 +41590,18 @@ Object.freeze({
|
|
|
41393
41590
|
addonId: null,
|
|
41394
41591
|
access: "delete"
|
|
41395
41592
|
},
|
|
41593
|
+
"ptz.getHomePreset": {
|
|
41594
|
+
capName: "ptz",
|
|
41595
|
+
capScope: "device",
|
|
41596
|
+
addonId: null,
|
|
41597
|
+
access: "view"
|
|
41598
|
+
},
|
|
41599
|
+
"ptz.getHomeReturnSeconds": {
|
|
41600
|
+
capName: "ptz",
|
|
41601
|
+
capScope: "device",
|
|
41602
|
+
addonId: null,
|
|
41603
|
+
access: "view"
|
|
41604
|
+
},
|
|
41396
41605
|
"ptz.getOptions": {
|
|
41397
41606
|
capName: "ptz",
|
|
41398
41607
|
capScope: "device",
|
|
@@ -41441,6 +41650,18 @@ Object.freeze({
|
|
|
41441
41650
|
addonId: null,
|
|
41442
41651
|
access: "create"
|
|
41443
41652
|
},
|
|
41653
|
+
"ptz.setHomePreset": {
|
|
41654
|
+
capName: "ptz",
|
|
41655
|
+
capScope: "device",
|
|
41656
|
+
addonId: null,
|
|
41657
|
+
access: "create"
|
|
41658
|
+
},
|
|
41659
|
+
"ptz.setHomeReturnSeconds": {
|
|
41660
|
+
capName: "ptz",
|
|
41661
|
+
capScope: "device",
|
|
41662
|
+
addonId: null,
|
|
41663
|
+
access: "create"
|
|
41664
|
+
},
|
|
41444
41665
|
"ptz.stop": {
|
|
41445
41666
|
capName: "ptz",
|
|
41446
41667
|
capScope: "device",
|
|
@@ -44604,6 +44825,11 @@ Object.freeze({
|
|
|
44604
44825
|
form: "single",
|
|
44605
44826
|
optional: false
|
|
44606
44827
|
}],
|
|
44828
|
+
"ptz.captureHomeHere": [{
|
|
44829
|
+
name: "deviceId",
|
|
44830
|
+
form: "single",
|
|
44831
|
+
optional: false
|
|
44832
|
+
}],
|
|
44607
44833
|
"ptz.continuousMove": [{
|
|
44608
44834
|
name: "deviceId",
|
|
44609
44835
|
form: "single",
|
|
@@ -44614,6 +44840,16 @@ Object.freeze({
|
|
|
44614
44840
|
form: "single",
|
|
44615
44841
|
optional: false
|
|
44616
44842
|
}],
|
|
44843
|
+
"ptz.getHomePreset": [{
|
|
44844
|
+
name: "deviceId",
|
|
44845
|
+
form: "single",
|
|
44846
|
+
optional: false
|
|
44847
|
+
}],
|
|
44848
|
+
"ptz.getHomeReturnSeconds": [{
|
|
44849
|
+
name: "deviceId",
|
|
44850
|
+
form: "single",
|
|
44851
|
+
optional: false
|
|
44852
|
+
}],
|
|
44617
44853
|
"ptz.getOptions": [{
|
|
44618
44854
|
name: "deviceId",
|
|
44619
44855
|
form: "single",
|
|
@@ -44654,6 +44890,16 @@ Object.freeze({
|
|
|
44654
44890
|
form: "single",
|
|
44655
44891
|
optional: false
|
|
44656
44892
|
}],
|
|
44893
|
+
"ptz.setHomePreset": [{
|
|
44894
|
+
name: "deviceId",
|
|
44895
|
+
form: "single",
|
|
44896
|
+
optional: false
|
|
44897
|
+
}],
|
|
44898
|
+
"ptz.setHomeReturnSeconds": [{
|
|
44899
|
+
name: "deviceId",
|
|
44900
|
+
form: "single",
|
|
44901
|
+
optional: false
|
|
44902
|
+
}],
|
|
44657
44903
|
"ptz.stop": [{
|
|
44658
44904
|
name: "deviceId",
|
|
44659
44905
|
form: "single",
|
|
@@ -68338,7 +68584,7 @@ var require_yazl = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
68338
68584
|
}
|
|
68339
68585
|
}));
|
|
68340
68586
|
//#endregion
|
|
68341
|
-
//#region node_modules/@apocaliss92/nodelink-js/dist/chunk-
|
|
68587
|
+
//#region node_modules/@apocaliss92/nodelink-js/dist/chunk-KWQVN7H5.js
|
|
68342
68588
|
var import_undici = require_undici();
|
|
68343
68589
|
var import_yazl = /* @__PURE__ */ __toESM(require_yazl(), 1);
|
|
68344
68590
|
var FLAGS_CAM_V2 = {
|
|
@@ -74755,7 +75001,7 @@ var require_lz4 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
74755
75001
|
};
|
|
74756
75002
|
}));
|
|
74757
75003
|
//#endregion
|
|
74758
|
-
//#region node_modules/@apocaliss92/nodelink-js/dist/chunk-
|
|
75004
|
+
//#region node_modules/@apocaliss92/nodelink-js/dist/chunk-CE77DTET.js
|
|
74759
75005
|
var import_fxp = require_fxp();
|
|
74760
75006
|
var import_lz4 = /* @__PURE__ */ __toESM(require_lz4(), 1);
|
|
74761
75007
|
function encodeHeader(h) {
|
|
@@ -75074,8 +75320,8 @@ function buildC2mQ(params) {
|
|
|
75074
75320
|
const os = params.os ?? "MAC";
|
|
75075
75321
|
return buildP2pXml(`<C2M_Q><uid>${xmlEscape(params.uid)}</uid><p>${xmlEscape(os)}</p></C2M_Q>`);
|
|
75076
75322
|
}
|
|
75077
|
-
function parseIpPortBlock(
|
|
75078
|
-
const m = new RegExp(`<${
|
|
75323
|
+
function parseIpPortBlock(tag2, body) {
|
|
75324
|
+
const m = new RegExp(`<${tag2}>([\\s\\S]*?)</${tag2}>`, "i").exec(body);
|
|
75079
75325
|
if (!m) return void 0;
|
|
75080
75326
|
const block = m[1] ?? "";
|
|
75081
75327
|
const ip = /<ip>([^<]+)<\/ip>/i.exec(block)?.[1];
|
|
@@ -75120,8 +75366,8 @@ function parseR2cCr(xml) {
|
|
|
75120
75366
|
if (!Number.isFinite(rsp)) return void 0;
|
|
75121
75367
|
const sidStr = /<sid>(-?\d+)<\/sid>/i.exec(body)?.[1];
|
|
75122
75368
|
const sid = sidStr != null ? Number(sidStr) : void 0;
|
|
75123
|
-
const parseCustom = (
|
|
75124
|
-
const mm = new RegExp(`<${
|
|
75369
|
+
const parseCustom = (tag2) => {
|
|
75370
|
+
const mm = new RegExp(`<${tag2}>([\\s\\S]*?)</${tag2}>`, "i").exec(body);
|
|
75125
75371
|
if (!mm) return void 0;
|
|
75126
75372
|
const block = mm[1] ?? "";
|
|
75127
75373
|
const ip = /<ip>([^<]+)<\/ip>/i.exec(block)?.[1];
|
|
@@ -76681,12 +76927,12 @@ function asLogger(logger) {
|
|
|
76681
76927
|
error: resolveMethod(base, "error"),
|
|
76682
76928
|
debug: resolveMethod(base, "debug")
|
|
76683
76929
|
};
|
|
76684
|
-
out.child = (
|
|
76930
|
+
out.child = (tag2) => createTaggedLogger(out, tag2);
|
|
76685
76931
|
return out;
|
|
76686
76932
|
}
|
|
76687
|
-
function createTaggedLogger(base,
|
|
76933
|
+
function createTaggedLogger(base, tag2) {
|
|
76688
76934
|
const b = base.log ? base : asLogger(base);
|
|
76689
|
-
const prefix = `[${
|
|
76935
|
+
const prefix = `[${tag2}] `;
|
|
76690
76936
|
const wrap = (fn) => {
|
|
76691
76937
|
return (message, ...optionalParams) => {
|
|
76692
76938
|
if (typeof message === "string") fn(`${prefix}${message}`, ...optionalParams);
|
|
@@ -76713,7 +76959,7 @@ function createDebugGateLogger(base, enabled = false) {
|
|
|
76713
76959
|
error: b.error,
|
|
76714
76960
|
debug: enabled ? b.debug : noop
|
|
76715
76961
|
};
|
|
76716
|
-
out.child = (
|
|
76962
|
+
out.child = (tag2) => createDebugGateLogger(createTaggedLogger(out, tag2), enabled);
|
|
76717
76963
|
return out;
|
|
76718
76964
|
}
|
|
76719
76965
|
function isTalkCmd(cmdId) {
|
|
@@ -82407,29 +82653,29 @@ function parseSupportXml(xml) {
|
|
|
82407
82653
|
if (!match) return void 0;
|
|
82408
82654
|
const supportXml = match[1] ?? "";
|
|
82409
82655
|
const ptzMode = supportXml.match(/<ptzMode>([^<]*)<\/ptzMode>/i)?.[1];
|
|
82410
|
-
const topLevelInt = (
|
|
82411
|
-
return toNumberOrUndefined(supportXml.match(new RegExp(`<${
|
|
82656
|
+
const topLevelInt = (tag2) => {
|
|
82657
|
+
return toNumberOrUndefined(supportXml.match(new RegExp(`<${tag2}>([^<]*)<\\/${tag2}>`, "i"))?.[1]);
|
|
82412
82658
|
};
|
|
82413
|
-
const topLevelString = (
|
|
82414
|
-
return supportXml.match(new RegExp(`<${
|
|
82659
|
+
const topLevelString = (tag2) => {
|
|
82660
|
+
return supportXml.match(new RegExp(`<${tag2}>([^<]*)<\\/${tag2}>`, "i"))?.[1];
|
|
82415
82661
|
};
|
|
82416
82662
|
const items = [];
|
|
82417
82663
|
for (const itemMatch of supportXml.matchAll(/<item[^>]*>([\s\S]*?)<\/item>/gi)) {
|
|
82418
82664
|
const itemXml = itemMatch[1] ?? "";
|
|
82419
82665
|
const item = { chnID: toNumberOrUndefined(itemXml.match(/<chnID>([^<]*)<\/chnID>/i)?.[1]) ?? 0 };
|
|
82420
82666
|
for (const tagMatch of itemXml.matchAll(/<([A-Za-z0-9_]+)>([^<]*)<\/\1>/g)) {
|
|
82421
|
-
const
|
|
82667
|
+
const tag2 = tagMatch[1];
|
|
82422
82668
|
const value = tagMatch[2];
|
|
82423
|
-
if (!
|
|
82424
|
-
if (
|
|
82425
|
-
item[
|
|
82669
|
+
if (!tag2) continue;
|
|
82670
|
+
if (tag2 === "chnID") continue;
|
|
82671
|
+
item[tag2] = toNumberOrUndefined(value) ?? value;
|
|
82426
82672
|
}
|
|
82427
82673
|
items.push(item);
|
|
82428
82674
|
}
|
|
82429
82675
|
const support = { items };
|
|
82430
82676
|
if (ptzMode !== void 0) support.ptzMode = ptzMode;
|
|
82431
|
-
const assignInt = (key,
|
|
82432
|
-
const v = topLevelInt(
|
|
82677
|
+
const assignInt = (key, tag2) => {
|
|
82678
|
+
const v = topLevelInt(tag2);
|
|
82433
82679
|
if (v !== void 0) support[key] = v;
|
|
82434
82680
|
};
|
|
82435
82681
|
assignInt("IOInputPortNum", "IOInputPortNum");
|
|
@@ -82656,15 +82902,15 @@ function extractCanvasFromShelterXml(xml) {
|
|
|
82656
82902
|
const yMatch = xml.match(/<topLeftY>(\d+)<\/topLeftY>/g) ?? [];
|
|
82657
82903
|
let canvasX = 0;
|
|
82658
82904
|
let canvasY = 0;
|
|
82659
|
-
for (const
|
|
82660
|
-
const v = Number(
|
|
82905
|
+
for (const tag2 of xMatch) {
|
|
82906
|
+
const v = Number(tag2.replace(/<\/?topLeftX>/g, "")) | 0;
|
|
82661
82907
|
if (v > 0) {
|
|
82662
82908
|
canvasX = v & 65535;
|
|
82663
82909
|
break;
|
|
82664
82910
|
}
|
|
82665
82911
|
}
|
|
82666
|
-
for (const
|
|
82667
|
-
const v = Number(
|
|
82912
|
+
for (const tag2 of yMatch) {
|
|
82913
|
+
const v = Number(tag2.replace(/<\/?topLeftY>/g, "")) | 0;
|
|
82668
82914
|
if (v > 0) {
|
|
82669
82915
|
canvasY = v & 65535;
|
|
82670
82916
|
break;
|
|
@@ -82677,9 +82923,9 @@ function extractCanvasFromShelterXml(xml) {
|
|
|
82677
82923
|
}
|
|
82678
82924
|
function parseRectTags(block) {
|
|
82679
82925
|
const out = {};
|
|
82680
|
-
const grab = (
|
|
82681
|
-
const m = block.match(new RegExp(`<${
|
|
82682
|
-
if (m && m[1] !== void 0) out[
|
|
82926
|
+
const grab = (tag2) => {
|
|
82927
|
+
const m = block.match(new RegExp(`<${tag2}>([^<]*)<\\/${tag2}>`));
|
|
82928
|
+
if (m && m[1] !== void 0) out[tag2] = m[1];
|
|
82683
82929
|
};
|
|
82684
82930
|
grab("id");
|
|
82685
82931
|
grab("name");
|
|
@@ -82924,9 +83170,9 @@ var buildDetectionClasses = (parsed, recordType) => {
|
|
|
82924
83170
|
};
|
|
82925
83171
|
var getXmlTexts = (xml, tags) => {
|
|
82926
83172
|
const out = {};
|
|
82927
|
-
for (const
|
|
82928
|
-
const v = getXmlText(xml,
|
|
82929
|
-
if (v !== void 0) out[
|
|
83173
|
+
for (const tag2 of tags) {
|
|
83174
|
+
const v = getXmlText(xml, tag2);
|
|
83175
|
+
if (v !== void 0) out[tag2] = v;
|
|
82930
83176
|
}
|
|
82931
83177
|
return out;
|
|
82932
83178
|
};
|
|
@@ -83188,11 +83434,11 @@ function patchBlock(xml, block, fields) {
|
|
|
83188
83434
|
const end = xml.indexOf(`</${block}>`, start);
|
|
83189
83435
|
if (end < 0) return xml;
|
|
83190
83436
|
let body = xml.slice(start, end);
|
|
83191
|
-
for (const [
|
|
83437
|
+
for (const [tag2, value] of Object.entries(fields)) {
|
|
83192
83438
|
if (value === void 0) continue;
|
|
83193
83439
|
const escaped = escapeXmlText(typeof value === "boolean" ? value ? "1" : "0" : String(value));
|
|
83194
|
-
if (body.includes(`<${
|
|
83195
|
-
else body += `<${
|
|
83440
|
+
if (body.includes(`<${tag2}>`)) body = body.replace(new RegExp(`<${tag2}>[^<]*<\\/${tag2}>`), `<${tag2}>${escaped}</${tag2}>`);
|
|
83441
|
+
else body += `<${tag2}>${escaped}</${tag2}>`;
|
|
83196
83442
|
}
|
|
83197
83443
|
return xml.slice(0, start) + body + xml.slice(end);
|
|
83198
83444
|
}
|
|
@@ -83260,6 +83506,66 @@ var OSD_CORNER_LABELS = {
|
|
|
83260
83506
|
"bottom-left": "Bottom left",
|
|
83261
83507
|
"bottom-right": "Bottom right"
|
|
83262
83508
|
};
|
|
83509
|
+
function wireFloat(value) {
|
|
83510
|
+
return value.toExponential(6).replace(/e([+-])(\d)$/, "e$10$2");
|
|
83511
|
+
}
|
|
83512
|
+
function buildPtzGuardExtensionXml(channelId, direction) {
|
|
83513
|
+
return `<?xml version="1.0" encoding="UTF-8" ?>
|
|
83514
|
+
<Extension version="1.1">
|
|
83515
|
+
<channelId>${channelId}</channelId>${direction === "read" ? `
|
|
83516
|
+
<chnType>0</chnType>` : ""}
|
|
83517
|
+
</Extension>
|
|
83518
|
+
`;
|
|
83519
|
+
}
|
|
83520
|
+
function guardBody(channelId, command, fields) {
|
|
83521
|
+
return [
|
|
83522
|
+
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>",
|
|
83523
|
+
"<body>",
|
|
83524
|
+
"<PtzGuard version=\"1.1\">",
|
|
83525
|
+
`<channelId>${channelId}</channelId>`,
|
|
83526
|
+
...fields,
|
|
83527
|
+
`<command>${command}</command>`,
|
|
83528
|
+
"<imageName></imageName>",
|
|
83529
|
+
`<xpos>${wireFloat(0)}</xpos>`,
|
|
83530
|
+
`<ypos>${wireFloat(0)}</ypos>`,
|
|
83531
|
+
`<height>${wireFloat(0)}</height>`,
|
|
83532
|
+
`<width>${wireFloat(0)}</width>`,
|
|
83533
|
+
"<mode>global</mode>",
|
|
83534
|
+
"</PtzGuard>",
|
|
83535
|
+
"</body>",
|
|
83536
|
+
""
|
|
83537
|
+
].join("\n");
|
|
83538
|
+
}
|
|
83539
|
+
function buildPtzGuardGoXml(channelId) {
|
|
83540
|
+
return guardBody(channelId, "toGrd", ["<benable>0</benable>", "<timeout>68</timeout>"]);
|
|
83541
|
+
}
|
|
83542
|
+
function buildPtzGuardSetXml(input) {
|
|
83543
|
+
return guardBody(input.channelId, "setGrd", [
|
|
83544
|
+
`<benable>${input.enabled ? 1 : 0}</benable>`,
|
|
83545
|
+
`<timeout>${Math.round(input.timeoutSeconds)}</timeout>`,
|
|
83546
|
+
...input.setPosition ? ["<needSetPos>1</needSetPos>"] : []
|
|
83547
|
+
]);
|
|
83548
|
+
}
|
|
83549
|
+
function tag(xml, name) {
|
|
83550
|
+
const m = new RegExp(`<${name}>([\\s\\S]*?)</${name}>`).exec(xml);
|
|
83551
|
+
return m === null ? null : (m[1] ?? "").trim();
|
|
83552
|
+
}
|
|
83553
|
+
function parsePtzGuardXml(xml) {
|
|
83554
|
+
if (!xml.includes("<PtzGuard")) return null;
|
|
83555
|
+
const enabled = tag(xml, "benable");
|
|
83556
|
+
const valid = tag(xml, "bvalid");
|
|
83557
|
+
const timeout = tag(xml, "timeout");
|
|
83558
|
+
if (enabled === null || valid === null || timeout === null) return null;
|
|
83559
|
+
const timeoutSeconds = Number.parseInt(timeout, 10);
|
|
83560
|
+
if (!Number.isFinite(timeoutSeconds)) return null;
|
|
83561
|
+
return {
|
|
83562
|
+
enabled: enabled === "1",
|
|
83563
|
+
valid: valid === "1",
|
|
83564
|
+
timeoutSeconds,
|
|
83565
|
+
mode: tag(xml, "mode") ?? "global",
|
|
83566
|
+
imageName: tag(xml, "imageName") ?? ""
|
|
83567
|
+
};
|
|
83568
|
+
}
|
|
83263
83569
|
var emitter = new EventEmitter();
|
|
83264
83570
|
var lastEventByCamera = /* @__PURE__ */ new Map();
|
|
83265
83571
|
var MAX_GLOBAL_EVENTS = 300;
|
|
@@ -84472,8 +84778,8 @@ var buildFileInfoListStopXml = (params) => {
|
|
|
84472
84778
|
</body>`;
|
|
84473
84779
|
};
|
|
84474
84780
|
var sleepMs = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
84475
|
-
var xmlDateTimePayload = (
|
|
84476
|
-
return `<${
|
|
84781
|
+
var xmlDateTimePayload = (tag2, d) => {
|
|
84782
|
+
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}>`;
|
|
84477
84783
|
};
|
|
84478
84784
|
var buildFileInfoListOpenXml = (params) => {
|
|
84479
84785
|
return `<?xml version="1.0" encoding="UTF-8" ?>
|
|
@@ -84661,10 +84967,10 @@ var isPlausibleStream = (s) => {
|
|
|
84661
84967
|
return s.width > 0 && s.height > 0 && (s.frameRate > 0 || s.bitRate > 0);
|
|
84662
84968
|
};
|
|
84663
84969
|
var logDebugStreamBlock = (params) => {
|
|
84664
|
-
const { logger, traceNativeStream, channel, tag, blockXml } = params;
|
|
84970
|
+
const { logger, traceNativeStream, channel, tag: tag2, blockXml } = params;
|
|
84665
84971
|
if (!traceNativeStream) return;
|
|
84666
84972
|
if (!blockXml) {
|
|
84667
|
-
(logger.warn ?? logger.log).call(logger, `[ReolinkBaichuanApi] getStreamMetadata(traceNativeStream): channel=${channel} tag=<${
|
|
84973
|
+
(logger.warn ?? logger.log).call(logger, `[ReolinkBaichuanApi] getStreamMetadata(traceNativeStream): channel=${channel} tag=<${tag2}> missing`);
|
|
84668
84974
|
return;
|
|
84669
84975
|
}
|
|
84670
84976
|
const raw = blockXml;
|
|
@@ -84691,7 +84997,7 @@ var logDebugStreamBlock = (params) => {
|
|
|
84691
84997
|
const previewMax = 1400;
|
|
84692
84998
|
const xmlPreview = raw.length <= previewMax ? raw : raw.slice(0, previewMax) + `
|
|
84693
84999
|
...truncated (+${raw.length - previewMax} chars)`;
|
|
84694
|
-
(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}> enabled=${isEnabled} plausible=${plausible} width=${width} height=${height} frame=${frameRate} bitRate=${bitRate} audio=${audio} videoEncType=${videoEncTypeText ?? "?"} rawFields=${JSON.stringify({
|
|
84695
85001
|
widthText,
|
|
84696
85002
|
heightText,
|
|
84697
85003
|
frameText,
|
|
@@ -84784,20 +85090,20 @@ var parseChannelStreamMetadataFromGetEncXml = (params) => {
|
|
|
84784
85090
|
audioEnabled = audioEnabled && s.audio === 1;
|
|
84785
85091
|
}
|
|
84786
85092
|
}
|
|
84787
|
-
for (const
|
|
85093
|
+
for (const tag2 of [
|
|
84788
85094
|
"extStream",
|
|
84789
85095
|
"thirdStream",
|
|
84790
85096
|
"externStream",
|
|
84791
85097
|
"extraStream"
|
|
84792
85098
|
]) {
|
|
84793
|
-
const extMatch = xml.match(new RegExp(`<${
|
|
85099
|
+
const extMatch = xml.match(new RegExp(`<${tag2}[^>]*>([\\s\\S]*?)<\\/${tag2}>`));
|
|
84794
85100
|
if (!extMatch) continue;
|
|
84795
85101
|
const extXml = extMatch[1] ?? "";
|
|
84796
85102
|
logDebugStreamBlock({
|
|
84797
85103
|
logger,
|
|
84798
85104
|
traceNativeStream,
|
|
84799
85105
|
channel,
|
|
84800
|
-
tag,
|
|
85106
|
+
tag: tag2,
|
|
84801
85107
|
blockXml: extXml
|
|
84802
85108
|
});
|
|
84803
85109
|
const s = buildStream({
|
|
@@ -86390,7 +86696,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86390
86696
|
* @param logger - Optional logger for debug output
|
|
86391
86697
|
* @returns The socket and a release function
|
|
86392
86698
|
*/
|
|
86393
|
-
async acquirePooledSocket(
|
|
86699
|
+
async acquirePooledSocket(tag2, logger) {
|
|
86394
86700
|
const log = logger ?? this.logger;
|
|
86395
86701
|
const now = Date.now();
|
|
86396
86702
|
const cooldownEntry = this.socketPoolCooldowns.get(this.host);
|
|
@@ -86401,12 +86707,12 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86401
86707
|
} else if (now < cooldownEntry.cooldownUntil) {
|
|
86402
86708
|
const remainingMs = cooldownEntry.cooldownUntil - now;
|
|
86403
86709
|
const reason = this.lastD2cDiscAtMs > 0 && now - this.lastD2cDiscAtMs < 12e4 ? "D2C_DISC (camera sleeping)" : "repeated login failures";
|
|
86404
|
-
const error = /* @__PURE__ */ new Error(`[SocketPool] Host ${this.host} is in cooldown for ${Math.ceil(remainingMs / 1e3)}s due to ${reason}. tag=${
|
|
86710
|
+
const error = /* @__PURE__ */ new Error(`[SocketPool] Host ${this.host} is in cooldown for ${Math.ceil(remainingMs / 1e3)}s due to ${reason}. tag=${tag2}`);
|
|
86405
86711
|
log?.debug?.(error.message);
|
|
86406
86712
|
throw error;
|
|
86407
86713
|
}
|
|
86408
86714
|
}
|
|
86409
|
-
const existing = this.socketPool.get(
|
|
86715
|
+
const existing = this.socketPool.get(tag2);
|
|
86410
86716
|
if (existing) {
|
|
86411
86717
|
if (existing.idleCloseTimer) {
|
|
86412
86718
|
clearTimeout(existing.idleCloseTimer);
|
|
@@ -86416,35 +86722,35 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86416
86722
|
const client2 = await existing.pendingPromise;
|
|
86417
86723
|
existing.refCount++;
|
|
86418
86724
|
existing.lastUsedAt = Date.now();
|
|
86419
|
-
log?.debug?.(`[SocketPool] Waited for pending socket creation for tag=${
|
|
86725
|
+
log?.debug?.(`[SocketPool] Waited for pending socket creation for tag=${tag2} (refCount=${existing.refCount})`);
|
|
86420
86726
|
return {
|
|
86421
86727
|
client: client2,
|
|
86422
|
-
release: () => this.releasePooledSocket(
|
|
86728
|
+
release: () => this.releasePooledSocket(tag2, logger)
|
|
86423
86729
|
};
|
|
86424
86730
|
}
|
|
86425
86731
|
if (existing.refCount === 0) {
|
|
86426
86732
|
existing.refCount = 1;
|
|
86427
86733
|
existing.lastUsedAt = Date.now();
|
|
86428
|
-
log?.debug?.(`[SocketPool] Reusing idle socket for tag=${
|
|
86734
|
+
log?.debug?.(`[SocketPool] Reusing idle socket for tag=${tag2}`);
|
|
86429
86735
|
try {
|
|
86430
86736
|
if (!existing.client.loggedIn) await existing.client.login();
|
|
86431
86737
|
} catch {}
|
|
86432
86738
|
if (existing.client.loggedIn) return {
|
|
86433
86739
|
client: existing.client,
|
|
86434
|
-
release: () => this.releasePooledSocket(
|
|
86740
|
+
release: () => this.releasePooledSocket(tag2, logger)
|
|
86435
86741
|
};
|
|
86436
|
-
} else if (
|
|
86742
|
+
} else if (tag2.startsWith("replay:")) log?.debug?.(`[SocketPool] Preempting active replay socket for tag=${tag2}`);
|
|
86437
86743
|
else {
|
|
86438
86744
|
existing.refCount++;
|
|
86439
86745
|
existing.lastUsedAt = Date.now();
|
|
86440
|
-
log?.debug?.(`[SocketPool] Reusing active socket for tag=${
|
|
86746
|
+
log?.debug?.(`[SocketPool] Reusing active socket for tag=${tag2} (refCount=${existing.refCount})`);
|
|
86441
86747
|
return {
|
|
86442
86748
|
client: existing.client,
|
|
86443
|
-
release: () => this.releasePooledSocket(
|
|
86749
|
+
release: () => this.releasePooledSocket(tag2, logger)
|
|
86444
86750
|
};
|
|
86445
86751
|
}
|
|
86446
|
-
log?.debug?.(`[SocketPool] Closing existing socket for tag=${
|
|
86447
|
-
this.socketPool.delete(
|
|
86752
|
+
log?.debug?.(`[SocketPool] Closing existing socket for tag=${tag2} (recreating)`);
|
|
86753
|
+
this.socketPool.delete(tag2);
|
|
86448
86754
|
if (existing.generalPermitRelease) {
|
|
86449
86755
|
try {
|
|
86450
86756
|
existing.generalPermitRelease();
|
|
@@ -86457,10 +86763,10 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86457
86763
|
skipLogout: true
|
|
86458
86764
|
});
|
|
86459
86765
|
} catch (e) {
|
|
86460
|
-
log?.warn?.(`[SocketPool] Error closing old socket for tag=${
|
|
86766
|
+
log?.warn?.(`[SocketPool] Error closing old socket for tag=${tag2}: ${e}`);
|
|
86461
86767
|
}
|
|
86462
86768
|
}
|
|
86463
|
-
log?.log?.(`[SocketPool] Creating new socket for tag=${
|
|
86769
|
+
log?.log?.(`[SocketPool] Creating new socket for tag=${tag2}`);
|
|
86464
86770
|
const entry = {
|
|
86465
86771
|
client: void 0,
|
|
86466
86772
|
refCount: 0,
|
|
@@ -86477,7 +86783,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86477
86783
|
} : this.clientOptions);
|
|
86478
86784
|
this.attachD2cDiscListener(newClient);
|
|
86479
86785
|
newClient.on("error", (err) => {
|
|
86480
|
-
log?.debug?.(`[SocketPool] tag=${
|
|
86786
|
+
log?.debug?.(`[SocketPool] tag=${tag2} client error: ${err?.message ?? err}`);
|
|
86481
86787
|
});
|
|
86482
86788
|
await newClient.login();
|
|
86483
86789
|
const existingCooldown = this.socketPoolCooldowns.get(this.host);
|
|
@@ -86489,10 +86795,10 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86489
86795
|
entry.refCount = 1;
|
|
86490
86796
|
entry.lastUsedAt = Date.now();
|
|
86491
86797
|
delete entry.pendingPromise;
|
|
86492
|
-
log?.log?.(`[SocketPool] Socket connected for tag=${
|
|
86493
|
-
if (
|
|
86798
|
+
log?.log?.(`[SocketPool] Socket connected for tag=${tag2}`);
|
|
86799
|
+
if (tag2 !== "general") try {
|
|
86494
86800
|
const generalEntry = this.socketPool.get("general");
|
|
86495
|
-
if (generalEntry?.client) entry.generalPermitRelease = generalEntry.client.acquirePermit(0, `streaming-peer:${
|
|
86801
|
+
if (generalEntry?.client) entry.generalPermitRelease = generalEntry.client.acquirePermit(0, `streaming-peer:${tag2}`);
|
|
86496
86802
|
} catch {}
|
|
86497
86803
|
if (this.sessionGuardEnabled) this.maybeRebootOnTooManySessions();
|
|
86498
86804
|
return newClient;
|
|
@@ -86503,21 +86809,21 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86503
86809
|
if (failureCount >= _ReolinkBaichuanApi.SOCKET_POOL_FAILURE_THRESHOLD) {
|
|
86504
86810
|
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);
|
|
86505
86811
|
cooldownUntil = now2 + backoffMs;
|
|
86506
|
-
log?.warn?.(`[SocketPool] Login failed for host=${this.host} (failure #${failureCount}). Entering cooldown for ${Math.ceil(backoffMs / 1e3)}s. tag=${
|
|
86507
|
-
} else log?.warn?.(`[SocketPool] Login failed for host=${this.host} (failure #${failureCount}/${_ReolinkBaichuanApi.SOCKET_POOL_FAILURE_THRESHOLD} before cooldown). tag=${
|
|
86812
|
+
log?.warn?.(`[SocketPool] Login failed for host=${this.host} (failure #${failureCount}). Entering cooldown for ${Math.ceil(backoffMs / 1e3)}s. tag=${tag2}`);
|
|
86813
|
+
} else log?.warn?.(`[SocketPool] Login failed for host=${this.host} (failure #${failureCount}/${_ReolinkBaichuanApi.SOCKET_POOL_FAILURE_THRESHOLD} before cooldown). tag=${tag2}`);
|
|
86508
86814
|
this.socketPoolCooldowns.set(this.host, {
|
|
86509
86815
|
failureCount,
|
|
86510
86816
|
lastFailureAt: now2,
|
|
86511
86817
|
cooldownUntil
|
|
86512
86818
|
});
|
|
86513
|
-
this.socketPool.delete(
|
|
86819
|
+
this.socketPool.delete(tag2);
|
|
86514
86820
|
throw loginError;
|
|
86515
86821
|
}
|
|
86516
86822
|
})();
|
|
86517
|
-
this.socketPool.set(
|
|
86823
|
+
this.socketPool.set(tag2, entry);
|
|
86518
86824
|
return {
|
|
86519
86825
|
client: await entry.pendingPromise,
|
|
86520
|
-
release: () => this.releasePooledSocket(
|
|
86826
|
+
release: () => this.releasePooledSocket(tag2, logger)
|
|
86521
86827
|
};
|
|
86522
86828
|
}
|
|
86523
86829
|
/**
|
|
@@ -86525,31 +86831,31 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86525
86831
|
* For shared sockets (general, streaming), just decrements refCount.
|
|
86526
86832
|
* For replay sockets, schedules idle close.
|
|
86527
86833
|
*/
|
|
86528
|
-
async releasePooledSocket(
|
|
86834
|
+
async releasePooledSocket(tag2, logger) {
|
|
86529
86835
|
const log = logger ?? this.logger;
|
|
86530
|
-
const entry = this.socketPool.get(
|
|
86836
|
+
const entry = this.socketPool.get(tag2);
|
|
86531
86837
|
if (!entry) return;
|
|
86532
86838
|
entry.refCount = Math.max(0, entry.refCount - 1);
|
|
86533
86839
|
entry.lastUsedAt = Date.now();
|
|
86534
|
-
log?.debug?.(`[SocketPool] Released socket for tag=${
|
|
86840
|
+
log?.debug?.(`[SocketPool] Released socket for tag=${tag2} (refCount=${entry.refCount})`);
|
|
86535
86841
|
if (entry.refCount > 0) return;
|
|
86536
|
-
const isReplayTag =
|
|
86537
|
-
const isStreamingTag =
|
|
86538
|
-
if (
|
|
86842
|
+
const isReplayTag = tag2.startsWith("replay:");
|
|
86843
|
+
const isStreamingTag = tag2.startsWith("streaming:");
|
|
86844
|
+
if (tag2 === "general") return;
|
|
86539
86845
|
if (isStreamingTag) {
|
|
86540
86846
|
if (entry.idleCloseTimer) return;
|
|
86541
86847
|
entry.idleCloseTimer = setTimeout(async () => {
|
|
86542
|
-
const current = this.socketPool.get(
|
|
86848
|
+
const current = this.socketPool.get(tag2);
|
|
86543
86849
|
if (!current) return;
|
|
86544
86850
|
if (current.refCount > 0) return;
|
|
86545
|
-
this.socketPool.delete(
|
|
86851
|
+
this.socketPool.delete(tag2);
|
|
86546
86852
|
if (current.generalPermitRelease) {
|
|
86547
86853
|
try {
|
|
86548
86854
|
current.generalPermitRelease();
|
|
86549
86855
|
} catch {}
|
|
86550
86856
|
current.generalPermitRelease = void 0;
|
|
86551
86857
|
}
|
|
86552
|
-
log?.log?.(`[SocketPool] Closing idle streaming socket for tag=${
|
|
86858
|
+
log?.log?.(`[SocketPool] Closing idle streaming socket for tag=${tag2}`);
|
|
86553
86859
|
try {
|
|
86554
86860
|
await current.client.close({
|
|
86555
86861
|
reason: "streaming idle close",
|
|
@@ -86562,11 +86868,11 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86562
86868
|
if (isReplayTag) {
|
|
86563
86869
|
if (entry.idleCloseTimer) return;
|
|
86564
86870
|
entry.idleCloseTimer = setTimeout(async () => {
|
|
86565
|
-
const current = this.socketPool.get(
|
|
86871
|
+
const current = this.socketPool.get(tag2);
|
|
86566
86872
|
if (!current) return;
|
|
86567
86873
|
if (current.refCount > 0) return;
|
|
86568
|
-
this.socketPool.delete(
|
|
86569
|
-
log?.debug?.(`[SocketPool] Closing idle replay socket for tag=${
|
|
86874
|
+
this.socketPool.delete(tag2);
|
|
86875
|
+
log?.debug?.(`[SocketPool] Closing idle replay socket for tag=${tag2} (keepalive expired)`);
|
|
86570
86876
|
try {
|
|
86571
86877
|
await current.client.close({
|
|
86572
86878
|
reason: "replay idle keepalive expired",
|
|
@@ -86576,24 +86882,24 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86576
86882
|
}, _ReolinkBaichuanApi.SOCKET_POOL_KEEPALIVE_MS);
|
|
86577
86883
|
return;
|
|
86578
86884
|
}
|
|
86579
|
-
this.socketPool.delete(
|
|
86885
|
+
this.socketPool.delete(tag2);
|
|
86580
86886
|
try {
|
|
86581
86887
|
await entry.client.close({
|
|
86582
86888
|
reason: "socket pool release",
|
|
86583
86889
|
skipLogout: true
|
|
86584
86890
|
});
|
|
86585
|
-
log?.log?.(`[SocketPool] Closed socket for tag=${
|
|
86891
|
+
log?.log?.(`[SocketPool] Closed socket for tag=${tag2}`);
|
|
86586
86892
|
} catch (e) {
|
|
86587
|
-
log?.warn?.(`[SocketPool] Error closing socket for tag=${
|
|
86893
|
+
log?.warn?.(`[SocketPool] Error closing socket for tag=${tag2}: ${e}`);
|
|
86588
86894
|
}
|
|
86589
86895
|
}
|
|
86590
86896
|
/**
|
|
86591
86897
|
* Force-close a socket by tag.
|
|
86592
86898
|
* Used to preempt existing connections before acquiring a new one.
|
|
86593
86899
|
*/
|
|
86594
|
-
async forceClosePooledSocket(
|
|
86900
|
+
async forceClosePooledSocket(tag2, logger) {
|
|
86595
86901
|
const log = logger ?? this.logger;
|
|
86596
|
-
const entry = this.socketPool.get(
|
|
86902
|
+
const entry = this.socketPool.get(tag2);
|
|
86597
86903
|
if (!entry) return false;
|
|
86598
86904
|
if (entry.idleCloseTimer) {
|
|
86599
86905
|
clearTimeout(entry.idleCloseTimer);
|
|
@@ -86605,16 +86911,16 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86605
86911
|
} catch {}
|
|
86606
86912
|
entry.generalPermitRelease = void 0;
|
|
86607
86913
|
}
|
|
86608
|
-
log?.debug?.(`[SocketPool] Force-closing socket for tag=${
|
|
86609
|
-
this.socketPool.delete(
|
|
86914
|
+
log?.debug?.(`[SocketPool] Force-closing socket for tag=${tag2}`);
|
|
86915
|
+
this.socketPool.delete(tag2);
|
|
86610
86916
|
try {
|
|
86611
86917
|
await entry.client.close({
|
|
86612
86918
|
reason: "force closed",
|
|
86613
86919
|
skipLogout: true
|
|
86614
86920
|
});
|
|
86615
|
-
log?.log?.(`[SocketPool] Force-closed socket for tag=${
|
|
86921
|
+
log?.log?.(`[SocketPool] Force-closed socket for tag=${tag2}`);
|
|
86616
86922
|
} catch (e) {
|
|
86617
|
-
log?.warn?.(`[SocketPool] Error during force-close for tag=${
|
|
86923
|
+
log?.warn?.(`[SocketPool] Error during force-close for tag=${tag2}: ${e}`);
|
|
86618
86924
|
}
|
|
86619
86925
|
return true;
|
|
86620
86926
|
}
|
|
@@ -86624,7 +86930,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86624
86930
|
async cleanupSocketPool() {
|
|
86625
86931
|
const entries = Array.from(this.socketPool.entries());
|
|
86626
86932
|
this.socketPool.clear();
|
|
86627
|
-
await Promise.allSettled(entries.map(async ([
|
|
86933
|
+
await Promise.allSettled(entries.map(async ([tag2, entry]) => {
|
|
86628
86934
|
try {
|
|
86629
86935
|
if (entry.idleCloseTimer) clearTimeout(entry.idleCloseTimer);
|
|
86630
86936
|
if (entry.generalPermitRelease) {
|
|
@@ -86633,7 +86939,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86633
86939
|
} catch {}
|
|
86634
86940
|
entry.generalPermitRelease = void 0;
|
|
86635
86941
|
}
|
|
86636
|
-
this.logger?.debug?.(`[SocketPool] Cleanup: closing tag=${
|
|
86942
|
+
this.logger?.debug?.(`[SocketPool] Cleanup: closing tag=${tag2}`);
|
|
86637
86943
|
await entry.client.close({
|
|
86638
86944
|
reason: "API cleanup",
|
|
86639
86945
|
skipLogout: true
|
|
@@ -86666,17 +86972,17 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86666
86972
|
* ```
|
|
86667
86973
|
*/
|
|
86668
86974
|
async createDedicatedSession(sessionKey, logger) {
|
|
86669
|
-
const
|
|
86670
|
-
(logger ?? this.logger)?.debug?.(`[SocketPool] createDedicatedSession sessionKey=${sessionKey} \u2192 tag=${
|
|
86671
|
-
return await this.acquirePooledSocket(
|
|
86975
|
+
const tag2 = this.resolveSocketTag(sessionKey);
|
|
86976
|
+
(logger ?? this.logger)?.debug?.(`[SocketPool] createDedicatedSession sessionKey=${sessionKey} \u2192 tag=${tag2}`);
|
|
86977
|
+
return await this.acquirePooledSocket(tag2, logger);
|
|
86672
86978
|
}
|
|
86673
86979
|
/**
|
|
86674
86980
|
* @deprecated Use forceClosePooledSocket via createDedicatedSession instead.
|
|
86675
86981
|
* Force-close a dedicated client if it exists.
|
|
86676
86982
|
*/
|
|
86677
86983
|
async forceCloseDedicatedClient(sessionKey, logger) {
|
|
86678
|
-
const
|
|
86679
|
-
return await this.forceClosePooledSocket(
|
|
86984
|
+
const tag2 = this.resolveSocketTag(sessionKey);
|
|
86985
|
+
return await this.forceClosePooledSocket(tag2, logger);
|
|
86680
86986
|
}
|
|
86681
86987
|
/**
|
|
86682
86988
|
* @deprecated Cleanup handled by cleanupSocketPool now.
|
|
@@ -86691,8 +86997,8 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
86691
86997
|
const debugCfg = this.client.getDebugConfig?.();
|
|
86692
86998
|
if (debugCfg) {
|
|
86693
86999
|
const sid = this.client.getSocketSessionId?.();
|
|
86694
|
-
const
|
|
86695
|
-
eventTraceLog(debugCfg, this.logger,
|
|
87000
|
+
const tag2 = sid ? `ReolinkSimpleEvent sid=${sid}` : "ReolinkSimpleEvent";
|
|
87001
|
+
eventTraceLog(debugCfg, this.logger, tag2, `dispatch type=${evt.type} channel=${evt.channel} timestamp=${evt.timestamp}`);
|
|
86696
87002
|
}
|
|
86697
87003
|
for (const cb of this.simpleEventListeners) try {
|
|
86698
87004
|
Promise.resolve(cb(evt)).catch((e) => {
|
|
@@ -87290,7 +87596,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
87290
87596
|
return;
|
|
87291
87597
|
}
|
|
87292
87598
|
entry.startInFlight = (async () => {
|
|
87293
|
-
const { BaichuanVideoStream: BaichuanVideoStream2 } = await import("./BaichuanVideoStream-
|
|
87599
|
+
const { BaichuanVideoStream: BaichuanVideoStream2 } = await import("./BaichuanVideoStream-2M4UO7MB-CB-tIHMA.mjs");
|
|
87294
87600
|
const sessionKey = `live:object-detections:ch${entry.channel}:${entry.profile}`;
|
|
87295
87601
|
const dedicated = await this.createDedicatedSession(sessionKey);
|
|
87296
87602
|
const stream = new BaichuanVideoStream2({
|
|
@@ -87857,8 +88163,8 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
87857
88163
|
const idleTimeoutMs = options?.idleTimeoutMs ?? 3e4;
|
|
87858
88164
|
const sessionKey = `talk:${options?.deviceId ?? "unknown"}:ch${channel}:${Date.now()}`;
|
|
87859
88165
|
logger?.info?.(`[DedicatedTalk] Creating session: ${sessionKey} (idleTimeout=${idleTimeoutMs}ms)`);
|
|
87860
|
-
const
|
|
87861
|
-
const { client: dedicatedClient, release } = await this.acquirePooledSocket(
|
|
88166
|
+
const tag2 = this.resolveSocketTag(sessionKey);
|
|
88167
|
+
const { client: dedicatedClient, release } = await this.acquirePooledSocket(tag2, logger);
|
|
87862
88168
|
const summary = this.getSocketPoolSummary();
|
|
87863
88169
|
logger?.info?.(`[DedicatedTalk] Session created [sessions: ${summary.count} active${summary.count > 0 ? ` (${summary.tags.join(", ")})` : ""}]`);
|
|
87864
88170
|
try {
|
|
@@ -87971,8 +88277,8 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
87971
88277
|
}
|
|
87972
88278
|
/** SetNetPort via Baichuan: cmd_id 36 (enable/disable rtsp/rtmp/onvif/http/https) */
|
|
87973
88279
|
async setPortEnabled(params) {
|
|
87974
|
-
const
|
|
87975
|
-
const xml = `<?xml version="1.0" encoding="UTF-8" ?><body><${
|
|
88280
|
+
const tag2 = `${params.port[0].toUpperCase()}${params.port.slice(1)}Port`;
|
|
88281
|
+
const xml = `<?xml version="1.0" encoding="UTF-8" ?><body><${tag2} version="1.1"><enable>${params.enable ? 1 : 0}</enable></${tag2}></body>`;
|
|
87976
88282
|
await this.sendXml({
|
|
87977
88283
|
cmdId: 36,
|
|
87978
88284
|
payloadXml: xml
|
|
@@ -88003,13 +88309,13 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
88003
88309
|
*/
|
|
88004
88310
|
async setPortConfig(patch) {
|
|
88005
88311
|
const blocks = [];
|
|
88006
|
-
const append = (
|
|
88312
|
+
const append = (tag2, portField, cfg) => {
|
|
88007
88313
|
if (!cfg) return;
|
|
88008
88314
|
if (cfg.port === void 0 && cfg.enable === void 0) return;
|
|
88009
88315
|
const inner = [];
|
|
88010
88316
|
if (cfg.port !== void 0) inner.push(`<${portField}>${cfg.port}</${portField}>`);
|
|
88011
88317
|
if (cfg.enable !== void 0) inner.push(`<enable>${cfg.enable ? 1 : 0}</enable>`);
|
|
88012
|
-
blocks.push(`<${
|
|
88318
|
+
blocks.push(`<${tag2} version="1.1">${inner.join("")}</${tag2}>`);
|
|
88013
88319
|
};
|
|
88014
88320
|
append("ServerPort", "serverPort", patch.server);
|
|
88015
88321
|
append("HttpPort", "httpPort", patch.http);
|
|
@@ -88523,8 +88829,8 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
88523
88829
|
];
|
|
88524
88830
|
const current = await this.getEncXml(ch);
|
|
88525
88831
|
let updated = null;
|
|
88526
|
-
for (const
|
|
88527
|
-
const sectionRe = new RegExp(`(<${
|
|
88832
|
+
for (const tag2 of candidateTags) {
|
|
88833
|
+
const sectionRe = new RegExp(`(<${tag2}[^>]*>[\\s\\S]*?<videoEncType>)(\\d+)(</videoEncType>)`);
|
|
88528
88834
|
if (!sectionRe.test(current)) continue;
|
|
88529
88835
|
const next = current.replace(sectionRe, `$1${desired}$3`);
|
|
88530
88836
|
if (next !== current) {
|
|
@@ -89054,9 +89360,9 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
89054
89360
|
const streamType = params.streamType;
|
|
89055
89361
|
const logger = params.logger ?? this.logger;
|
|
89056
89362
|
const sessionKey = params.deviceId ? `replay:${params.deviceId}:ch${channel}` : `replay:standalone:ch${channel}:${Date.now()}`;
|
|
89057
|
-
const
|
|
89058
|
-
logger?.debug?.(`[startRecordingReplayStreamStandalone] sessionKey=${sessionKey} -> tag=${
|
|
89059
|
-
const { client: dedicatedClient, release: releaseDedicatedClient } = await this.acquirePooledSocket(
|
|
89363
|
+
const tag2 = this.resolveSocketTag(sessionKey);
|
|
89364
|
+
logger?.debug?.(`[startRecordingReplayStreamStandalone] sessionKey=${sessionKey} -> tag=${tag2}`);
|
|
89365
|
+
const { client: dedicatedClient, release: releaseDedicatedClient } = await this.acquirePooledSocket(tag2, logger);
|
|
89060
89366
|
const uid = await this.ensureUidForRecordings(channel, void 0);
|
|
89061
89367
|
const payloadXml = buildFileInfoListReplayByNameXml({
|
|
89062
89368
|
channel,
|
|
@@ -89159,8 +89465,8 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
89159
89465
|
const streamType = params.streamType;
|
|
89160
89466
|
const logger = params.logger ?? this.logger;
|
|
89161
89467
|
const sessionKey = params.deviceId ? `replay:${params.deviceId}:ch${channel}` : `replay:nvr:ch${channel}:${Date.now()}`;
|
|
89162
|
-
const
|
|
89163
|
-
const { client: dedicatedClient, release: releaseDedicatedClient } = await this.acquirePooledSocket(
|
|
89468
|
+
const tag2 = this.resolveSocketTag(sessionKey);
|
|
89469
|
+
const { client: dedicatedClient, release: releaseDedicatedClient } = await this.acquirePooledSocket(tag2, logger);
|
|
89164
89470
|
let uid;
|
|
89165
89471
|
try {
|
|
89166
89472
|
uid = await this.ensureUidForRecordings(channel, void 0);
|
|
@@ -91387,6 +91693,73 @@ ${stderr}`));
|
|
|
91387
91693
|
return result;
|
|
91388
91694
|
}
|
|
91389
91695
|
/**
|
|
91696
|
+
* Read the PTZ guard point ("monitoring point" in the app).
|
|
91697
|
+
*
|
|
91698
|
+
* cmd_id: 332
|
|
91699
|
+
*
|
|
91700
|
+
* Returns `null` when the camera has no readable guard configuration — never
|
|
91701
|
+
* a fabricated "none", because that answer is what a consumer uses to decide
|
|
91702
|
+
* whether to move the head.
|
|
91703
|
+
*/
|
|
91704
|
+
async getPtzGuard(channel) {
|
|
91705
|
+
const ch = this.normalizeChannel(channel);
|
|
91706
|
+
return parsePtzGuardXml(await this.sendXml({
|
|
91707
|
+
cmdId: 332,
|
|
91708
|
+
channel: ch,
|
|
91709
|
+
channelIdOverride: ch,
|
|
91710
|
+
extensionXml: buildPtzGuardExtensionXml(ch, "read"),
|
|
91711
|
+
messageClass: BC_CLASS_MODERN_24,
|
|
91712
|
+
streamType: 0
|
|
91713
|
+
}));
|
|
91714
|
+
}
|
|
91715
|
+
/**
|
|
91716
|
+
* Configure the PTZ guard point.
|
|
91717
|
+
*
|
|
91718
|
+
* cmd_id: 331 with `<command>setGrd</command>`
|
|
91719
|
+
*
|
|
91720
|
+
* `setPosition` pins the guard point to the head's CURRENT position. Leave it
|
|
91721
|
+
* false when only changing the timeout or the enable flag: re-sending it
|
|
91722
|
+
* would silently move a point the operator had already placed.
|
|
91723
|
+
*/
|
|
91724
|
+
async setPtzGuard(options, channel) {
|
|
91725
|
+
const ch = this.normalizeChannel(channel);
|
|
91726
|
+
await this.sendXml({
|
|
91727
|
+
cmdId: 331,
|
|
91728
|
+
channel: ch,
|
|
91729
|
+
channelIdOverride: ch,
|
|
91730
|
+
extensionXml: buildPtzGuardExtensionXml(ch, "write"),
|
|
91731
|
+
payloadXml: buildPtzGuardSetXml({
|
|
91732
|
+
channelId: ch,
|
|
91733
|
+
enabled: options.enabled,
|
|
91734
|
+
timeoutSeconds: options.timeoutSeconds,
|
|
91735
|
+
setPosition: options.setPosition === true
|
|
91736
|
+
}),
|
|
91737
|
+
messageClass: BC_CLASS_MODERN_24,
|
|
91738
|
+
streamType: 0
|
|
91739
|
+
});
|
|
91740
|
+
}
|
|
91741
|
+
/**
|
|
91742
|
+
* Send the head to the guard point NOW.
|
|
91743
|
+
*
|
|
91744
|
+
* cmd_id: 331 with `<command>toGrd</command>`
|
|
91745
|
+
*
|
|
91746
|
+
* This is the closest thing Baichuan has to a "go home": the guard point is
|
|
91747
|
+
* an operator-placed position, unlike preset slot 0 which merely happens to
|
|
91748
|
+
* be first.
|
|
91749
|
+
*/
|
|
91750
|
+
async goToPtzGuard(channel) {
|
|
91751
|
+
const ch = this.normalizeChannel(channel);
|
|
91752
|
+
await this.sendXml({
|
|
91753
|
+
cmdId: 331,
|
|
91754
|
+
channel: ch,
|
|
91755
|
+
channelIdOverride: ch,
|
|
91756
|
+
extensionXml: buildPtzGuardExtensionXml(ch, "write"),
|
|
91757
|
+
payloadXml: buildPtzGuardGoXml(ch),
|
|
91758
|
+
messageClass: BC_CLASS_MODERN_24,
|
|
91759
|
+
streamType: 0
|
|
91760
|
+
});
|
|
91761
|
+
}
|
|
91762
|
+
/**
|
|
91390
91763
|
* Read zoom/focus min/max/current positions.
|
|
91391
91764
|
* cmd_id: 294 (MSG_ID_GET_ZOOM_FOCUS)
|
|
91392
91765
|
*/
|
|
@@ -91479,10 +91852,10 @@ ${stderr}`));
|
|
|
91479
91852
|
notifyD2cDisc() {
|
|
91480
91853
|
const now = Date.now();
|
|
91481
91854
|
this.lastD2cDiscAtMs = now;
|
|
91482
|
-
const streamingTags = Array.from(this.socketPool.keys()).filter((
|
|
91855
|
+
const streamingTags = Array.from(this.socketPool.keys()).filter((tag2) => tag2.startsWith("streaming:"));
|
|
91483
91856
|
if (streamingTags.length > 0) {
|
|
91484
91857
|
this.logger?.log?.(`[D2C_DISC] Force-closing ${streamingTags.length} streaming socket(s): ${streamingTags.join(", ")}`);
|
|
91485
|
-
for (const
|
|
91858
|
+
for (const tag2 of streamingTags) this.forceClosePooledSocket(tag2, this.logger).catch(() => {});
|
|
91486
91859
|
}
|
|
91487
91860
|
const immediateCooldownUntil = now + _ReolinkBaichuanApi.D2C_DISC_IMMEDIATE_COOLDOWN_MS;
|
|
91488
91861
|
const existing = this.socketPoolCooldowns.get(this.host);
|
|
@@ -91515,15 +91888,15 @@ ${stderr}`));
|
|
|
91515
91888
|
* Returns undefined if the client is not in the pool (e.g. it's the general socket used directly).
|
|
91516
91889
|
*/
|
|
91517
91890
|
findSocketTagForClient(client) {
|
|
91518
|
-
for (const [
|
|
91891
|
+
for (const [tag2, entry] of this.socketPool) if (entry.client === client) return tag2;
|
|
91519
91892
|
}
|
|
91520
91893
|
/**
|
|
91521
91894
|
* Reset the consecutive stream-start timeout counter for a streaming socket.
|
|
91522
91895
|
* Called on successful stream start.
|
|
91523
91896
|
*/
|
|
91524
91897
|
resetStreamTimeoutCounter(client) {
|
|
91525
|
-
const
|
|
91526
|
-
if (
|
|
91898
|
+
const tag2 = this.findSocketTagForClient(client);
|
|
91899
|
+
if (tag2) this.consecutiveStreamTimeouts.delete(tag2);
|
|
91527
91900
|
}
|
|
91528
91901
|
/**
|
|
91529
91902
|
* Track a stream-start timeout on a streaming socket.
|
|
@@ -91531,14 +91904,14 @@ ${stderr}`));
|
|
|
91531
91904
|
* socket so the next attempt creates a fresh connection.
|
|
91532
91905
|
*/
|
|
91533
91906
|
trackStreamTimeout(client) {
|
|
91534
|
-
const
|
|
91535
|
-
if (!
|
|
91536
|
-
const count = (this.consecutiveStreamTimeouts.get(
|
|
91537
|
-
this.consecutiveStreamTimeouts.set(
|
|
91907
|
+
const tag2 = this.findSocketTagForClient(client);
|
|
91908
|
+
if (!tag2 || !tag2.startsWith("streaming:")) return;
|
|
91909
|
+
const count = (this.consecutiveStreamTimeouts.get(tag2) ?? 0) + 1;
|
|
91910
|
+
this.consecutiveStreamTimeouts.set(tag2, count);
|
|
91538
91911
|
if (count >= _ReolinkBaichuanApi.MAX_CONSECUTIVE_STREAM_TIMEOUTS) {
|
|
91539
|
-
this.logger?.warn?.(`[SocketPool] ${count} consecutive stream timeouts on tag=${
|
|
91540
|
-
this.consecutiveStreamTimeouts.delete(
|
|
91541
|
-
this.forceClosePooledSocket(
|
|
91912
|
+
this.logger?.warn?.(`[SocketPool] ${count} consecutive stream timeouts on tag=${tag2}, force-closing socket`);
|
|
91913
|
+
this.consecutiveStreamTimeouts.delete(tag2);
|
|
91914
|
+
this.forceClosePooledSocket(tag2, this.logger).catch(() => {});
|
|
91542
91915
|
}
|
|
91543
91916
|
}
|
|
91544
91917
|
/**
|
|
@@ -92405,8 +92778,20 @@ ${xml}`);
|
|
|
92405
92778
|
const support = supportResult.status === "fulfilled" ? supportResult.value : void 0;
|
|
92406
92779
|
const abilities = abilitiesResult.status === "fulfilled" ? abilitiesResult.value : void 0;
|
|
92407
92780
|
const supportItem = getSupportItemForChannel(support, ch);
|
|
92781
|
+
let model;
|
|
92782
|
+
try {
|
|
92783
|
+
const info = await this.getInfo(void 0, { timeoutMs: 5e3 });
|
|
92784
|
+
model = typeof info?.type === "string" ? info.type : void 0;
|
|
92785
|
+
} catch (e) {
|
|
92786
|
+
this.logger.debug("[ReolinkBaichuanApi] getDeviceCapabilities: getInfo(type) failed", {
|
|
92787
|
+
host: this.host,
|
|
92788
|
+
channel: ch,
|
|
92789
|
+
err: e instanceof Error ? e.message : String(e)
|
|
92790
|
+
});
|
|
92791
|
+
}
|
|
92408
92792
|
const capabilities = computeDeviceCapabilities({
|
|
92409
92793
|
channel: ch,
|
|
92794
|
+
...model != null && { model },
|
|
92410
92795
|
...support != null && { support },
|
|
92411
92796
|
...abilities != null && { abilities }
|
|
92412
92797
|
});
|
|
@@ -93175,7 +93560,7 @@ ${xml}`);
|
|
|
93175
93560
|
* @returns Test results for all stream types and profiles
|
|
93176
93561
|
*/
|
|
93177
93562
|
async testChannelStreams(channel, logger) {
|
|
93178
|
-
const { testChannelStreams } = await import("./DiagnosticsTools-
|
|
93563
|
+
const { testChannelStreams } = await import("./DiagnosticsTools-GOFDNGAY-9NV95vRN.mjs");
|
|
93179
93564
|
return await testChannelStreams({
|
|
93180
93565
|
api: this,
|
|
93181
93566
|
channel: this.normalizeChannel(channel),
|
|
@@ -93191,7 +93576,7 @@ ${xml}`);
|
|
|
93191
93576
|
* @returns Complete diagnostics for all channels and streams
|
|
93192
93577
|
*/
|
|
93193
93578
|
async collectMultifocalDiagnostics(logger) {
|
|
93194
|
-
const { collectMultifocalDiagnostics } = await import("./DiagnosticsTools-
|
|
93579
|
+
const { collectMultifocalDiagnostics } = await import("./DiagnosticsTools-GOFDNGAY-9NV95vRN.mjs");
|
|
93195
93580
|
return await collectMultifocalDiagnostics({
|
|
93196
93581
|
api: this,
|
|
93197
93582
|
logger
|
|
@@ -148318,6 +148703,17 @@ var ReolinkStreamProfileOptionsSchema = object({
|
|
|
148318
148703
|
}).optional()
|
|
148319
148704
|
});
|
|
148320
148705
|
var reolinkCameraSchema = object({
|
|
148706
|
+
/**
|
|
148707
|
+
* Which preset `ptz.goHome` targets on THIS camera.
|
|
148708
|
+
*
|
|
148709
|
+
* Empty = nothing configured, and `goHome` then REFUSES. It used to go to a
|
|
148710
|
+
* hardcoded slot on a vendor "convention" this fleet contradicts: reolink
|
|
148711
|
+
* went to preset 0, which on device 592 holds "stanza"; amcrest went to
|
|
148712
|
+
* preset 1, which on device 3836 holds "Preset1". Both swallowed the failure
|
|
148713
|
+
* in a bare catch, so a head that never moved was indistinguishable from a
|
|
148714
|
+
* head that went home.
|
|
148715
|
+
*/
|
|
148716
|
+
homePresetId: string().default("").describe("Preset id ptz.goHome targets (empty = goHome refuses)"),
|
|
148321
148717
|
host: string().describe("Camera IP or hostname"),
|
|
148322
148718
|
port: number().default(9e3).describe("Baichuan port (default 9000)"),
|
|
148323
148719
|
username: string().default("admin").describe("Username"),
|
|
@@ -149608,6 +150004,20 @@ function coerceNumber(value) {
|
|
|
149608
150004
|
return null;
|
|
149609
150005
|
}
|
|
149610
150006
|
/**
|
|
150007
|
+
* Baichuan's native PTZ speed scale is 1..63; `runPtz` maps the cap's
|
|
150008
|
+
* normalized 0..1 onto it. Named because the autotrack loop has to know the
|
|
150009
|
+
* GRANULARITY of the knob it turns -- a normalized 0.37 means something very
|
|
150010
|
+
* different on 63 steps than on amcrest's 8.
|
|
150011
|
+
*/
|
|
150012
|
+
var BAICHUAN_SPEED_STEPS = 63;
|
|
150013
|
+
/**
|
|
150014
|
+
* How long one `ptz.move` pulse runs, ms -- the `autoStopMs` handed to
|
|
150015
|
+
* Baichuan. It is the DENOMINATOR of "how far did the head travel per pulse",
|
|
150016
|
+
* so it is exported through `getOptions` rather than left a literal no caller
|
|
150017
|
+
* could read.
|
|
150018
|
+
*/
|
|
150019
|
+
var PTZ_MOVE_IMPULSE_MS = 200;
|
|
150020
|
+
/**
|
|
149611
150021
|
* Per-device transient diagnostics blob populated from the lib's
|
|
149612
150022
|
* `getOnlineUserSessionsForUi` + `getSocketPoolSummary` +
|
|
149613
150023
|
* `getSocketPoolCooldownStatus` calls. NOT persisted — recomputed on
|
|
@@ -149949,6 +150359,48 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
149949
150359
|
* during `adoptDevice`). Standalone cameras default to 0 so the
|
|
149950
150360
|
* pre-Hub behavior is preserved verbatim.
|
|
149951
150361
|
*/
|
|
150362
|
+
/**
|
|
150363
|
+
* Which preset `goHome` targets, and who decided it.
|
|
150364
|
+
*
|
|
150365
|
+
* Baichuan names no home of its own, so the operator's pick is the only
|
|
150366
|
+
* answer there can be. With none, `none` -- and `goHome` refuses rather than
|
|
150367
|
+
* moving the head to whatever happens to sit in slot 0.
|
|
150368
|
+
*/
|
|
150369
|
+
async resolveHomePreset() {
|
|
150370
|
+
const chosen = this.config.get("homePresetId") ?? "";
|
|
150371
|
+
if (chosen !== "") return {
|
|
150372
|
+
presetId: chosen,
|
|
150373
|
+
source: "operator"
|
|
150374
|
+
};
|
|
150375
|
+
if ((await this.readGuardStatus())?.valid === true) return {
|
|
150376
|
+
presetId: null,
|
|
150377
|
+
source: "native"
|
|
150378
|
+
};
|
|
150379
|
+
return {
|
|
150380
|
+
presetId: null,
|
|
150381
|
+
source: "none"
|
|
150382
|
+
};
|
|
150383
|
+
}
|
|
150384
|
+
/**
|
|
150385
|
+
* The camera's guard-point configuration, or `null` when it cannot be read.
|
|
150386
|
+
*
|
|
150387
|
+
* Never a fabricated "no guard configured": that answer decides whether
|
|
150388
|
+
* `goHome` moves the head, and an unreadable camera is not evidence that no
|
|
150389
|
+
* guard point exists (D393).
|
|
150390
|
+
*/
|
|
150391
|
+
async readGuardStatus() {
|
|
150392
|
+
try {
|
|
150393
|
+
const api = await this.ensureApi();
|
|
150394
|
+
const channel = this.getChannel();
|
|
150395
|
+
return await api.getPtzGuard(channel);
|
|
150396
|
+
} catch (err) {
|
|
150397
|
+
this.ctx.logger.warn("reolink guard point unreadable — goHome will fall back", {
|
|
150398
|
+
tags: { deviceId: this.id },
|
|
150399
|
+
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
150400
|
+
});
|
|
150401
|
+
return null;
|
|
150402
|
+
}
|
|
150403
|
+
}
|
|
149952
150404
|
getChannel() {
|
|
149953
150405
|
const ch = this.config.get("channel");
|
|
149954
150406
|
if (typeof ch === "number" && ch >= 0) return ch;
|
|
@@ -153326,7 +153778,9 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153326
153778
|
const channel = this.getChannel();
|
|
153327
153779
|
return (await api.getPtzPresets(channel)).map((p) => ({
|
|
153328
153780
|
id: String(p.id),
|
|
153329
|
-
name: p.name
|
|
153781
|
+
name: p.name,
|
|
153782
|
+
fn: null,
|
|
153783
|
+
writable: true
|
|
153330
153784
|
}));
|
|
153331
153785
|
} catch {
|
|
153332
153786
|
return [];
|
|
@@ -153362,7 +153816,9 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153362
153816
|
hasTilt: false,
|
|
153363
153817
|
hasZoom: false,
|
|
153364
153818
|
supportsPresets: false,
|
|
153365
|
-
hasAutofocus: false
|
|
153819
|
+
hasAutofocus: false,
|
|
153820
|
+
speedSteps: BAICHUAN_SPEED_STEPS,
|
|
153821
|
+
moveImpulseMs: PTZ_MOVE_IMPULSE_MS
|
|
153366
153822
|
};
|
|
153367
153823
|
const hasAutofocus = this.config.get("deviceCache")?.autoFocusSnapshot?.supported === true;
|
|
153368
153824
|
return this.resolveCapOptions({
|
|
@@ -153375,7 +153831,9 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153375
153831
|
hasTilt: capabilities.hasTilt,
|
|
153376
153832
|
hasZoom: capabilities.hasZoom,
|
|
153377
153833
|
supportsPresets: capabilities.hasPresets,
|
|
153378
|
-
hasAutofocus
|
|
153834
|
+
hasAutofocus,
|
|
153835
|
+
speedSteps: BAICHUAN_SPEED_STEPS,
|
|
153836
|
+
moveImpulseMs: PTZ_MOVE_IMPULSE_MS
|
|
153379
153837
|
};
|
|
153380
153838
|
},
|
|
153381
153839
|
fallback: () => {
|
|
@@ -153385,42 +153843,89 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153385
153843
|
hasTilt: hasPtz,
|
|
153386
153844
|
hasZoom: hasPtz,
|
|
153387
153845
|
supportsPresets: hasPtz,
|
|
153388
|
-
hasAutofocus
|
|
153846
|
+
hasAutofocus,
|
|
153847
|
+
speedSteps: BAICHUAN_SPEED_STEPS,
|
|
153848
|
+
moveImpulseMs: PTZ_MOVE_IMPULSE_MS
|
|
153389
153849
|
};
|
|
153390
153850
|
}
|
|
153391
153851
|
});
|
|
153392
153852
|
},
|
|
153393
153853
|
goHome: async ({ deviceId }) => {
|
|
153394
153854
|
if (deviceId !== this.id) return;
|
|
153395
|
-
|
|
153396
|
-
|
|
153397
|
-
|
|
153398
|
-
|
|
153399
|
-
}
|
|
153855
|
+
const home = await this.resolveHomePreset();
|
|
153856
|
+
if (home.source === "native") {
|
|
153857
|
+
await (await this.ensureApi()).goToPtzGuard(this.getChannel());
|
|
153858
|
+
return;
|
|
153859
|
+
}
|
|
153860
|
+
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");
|
|
153861
|
+
const id = Number(home.presetId);
|
|
153862
|
+
if (!Number.isFinite(id)) throw new Error(`Configured home preset is not a Baichuan id: ${home.presetId}`);
|
|
153863
|
+
const api = await this.ensureApi();
|
|
153864
|
+
const channel = this.getChannel();
|
|
153865
|
+
await api.moveToPtzPreset(channel, id);
|
|
153866
|
+
},
|
|
153867
|
+
getHomePreset: async ({ deviceId }) => {
|
|
153868
|
+
if (deviceId !== this.id) return {
|
|
153869
|
+
presetId: null,
|
|
153870
|
+
source: "none"
|
|
153871
|
+
};
|
|
153872
|
+
return this.resolveHomePreset();
|
|
153873
|
+
},
|
|
153874
|
+
captureHomeHere: async ({ deviceId }) => {
|
|
153875
|
+
if (deviceId !== this.id) return;
|
|
153876
|
+
const current = await this.readGuardStatus();
|
|
153877
|
+
const api = await this.ensureApi();
|
|
153878
|
+
const channel = this.getChannel();
|
|
153879
|
+
await api.setPtzGuard({
|
|
153880
|
+
enabled: current?.enabled ?? false,
|
|
153881
|
+
timeoutSeconds: current?.timeoutSeconds ?? 60,
|
|
153882
|
+
setPosition: true
|
|
153883
|
+
}, channel);
|
|
153884
|
+
this.ctx.logger.info("reolink: guard point pinned to the current position", { tags: { deviceId: this.id } });
|
|
153885
|
+
},
|
|
153886
|
+
getHomeReturnSeconds: async ({ deviceId }) => {
|
|
153887
|
+
if (deviceId !== this.id) return null;
|
|
153888
|
+
const guard = await this.readGuardStatus();
|
|
153889
|
+
return guard === null ? null : guard.timeoutSeconds;
|
|
153890
|
+
},
|
|
153891
|
+
setHomeReturnSeconds: async ({ deviceId, seconds }) => {
|
|
153892
|
+
if (deviceId !== this.id) return;
|
|
153893
|
+
const current = await this.readGuardStatus();
|
|
153894
|
+
const api = await this.ensureApi();
|
|
153895
|
+
const channel = this.getChannel();
|
|
153896
|
+
await api.setPtzGuard({
|
|
153897
|
+
enabled: current?.enabled ?? true,
|
|
153898
|
+
timeoutSeconds: seconds,
|
|
153899
|
+
setPosition: false
|
|
153900
|
+
}, channel);
|
|
153901
|
+
},
|
|
153902
|
+
setHomePreset: async ({ deviceId, presetId }) => {
|
|
153903
|
+
if (deviceId !== this.id) return;
|
|
153904
|
+
await this.config.setAll({ homePresetId: presetId ?? "" });
|
|
153400
153905
|
},
|
|
153401
153906
|
getPosition: async ({ deviceId }) => {
|
|
153402
153907
|
if (deviceId !== this.id) return {
|
|
153403
|
-
pan:
|
|
153404
|
-
tilt:
|
|
153405
|
-
zoom:
|
|
153908
|
+
pan: null,
|
|
153909
|
+
tilt: null,
|
|
153910
|
+
zoom: null
|
|
153406
153911
|
};
|
|
153407
153912
|
return {
|
|
153408
|
-
pan:
|
|
153409
|
-
tilt:
|
|
153410
|
-
zoom:
|
|
153913
|
+
pan: null,
|
|
153914
|
+
tilt: null,
|
|
153915
|
+
zoom: null
|
|
153411
153916
|
};
|
|
153412
153917
|
},
|
|
153413
153918
|
getStatus: async ({ deviceId }) => {
|
|
153414
153919
|
if (deviceId !== this.id) return {
|
|
153415
|
-
pan:
|
|
153416
|
-
tilt:
|
|
153417
|
-
zoom:
|
|
153920
|
+
pan: null,
|
|
153921
|
+
tilt: null,
|
|
153922
|
+
zoom: null,
|
|
153418
153923
|
autofocus: false
|
|
153419
153924
|
};
|
|
153420
153925
|
return {
|
|
153421
|
-
pan:
|
|
153422
|
-
tilt:
|
|
153423
|
-
zoom:
|
|
153926
|
+
pan: null,
|
|
153927
|
+
tilt: null,
|
|
153928
|
+
zoom: null,
|
|
153424
153929
|
autofocus: (this.config.get("deviceCache")?.autoFocusSnapshot)?.enabled === true
|
|
153425
153930
|
};
|
|
153426
153931
|
},
|
|
@@ -153471,13 +153976,13 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153471
153976
|
if (tilt !== void 0 && Math.abs(tilt) > .01) cmds.push(tilt > 0 ? "Up" : "Down");
|
|
153472
153977
|
if (zoom !== void 0 && Math.abs(zoom) > .01) await this.runZoom(api, channel, zoom);
|
|
153473
153978
|
if (cmds.length === 0) return;
|
|
153474
|
-
const baichuanSpeed = speed === void 0 ?
|
|
153979
|
+
const baichuanSpeed = speed === void 0 ? Math.round(BAICHUAN_SPEED_STEPS / 2) : Math.max(1, Math.min(BAICHUAN_SPEED_STEPS, Math.round(speed * BAICHUAN_SPEED_STEPS)));
|
|
153475
153980
|
for (const command of cmds) try {
|
|
153476
153981
|
await api.ptz(channel, {
|
|
153477
153982
|
action: "start",
|
|
153478
153983
|
command,
|
|
153479
153984
|
speed: baichuanSpeed,
|
|
153480
|
-
...continuous ? {} : { autoStopMs:
|
|
153985
|
+
...continuous ? {} : { autoStopMs: PTZ_MOVE_IMPULSE_MS }
|
|
153481
153986
|
});
|
|
153482
153987
|
} catch (err) {
|
|
153483
153988
|
this.ctx.logger.warn("Baichuan ptz command failed", {
|