@camstack/addon-provider-reolink 1.2.138 → 1.2.139
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/addon.js +265 -29
- package/dist/addon.mjs +265 -29
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -5386,7 +5386,7 @@ var ZodIssueCode = {
|
|
|
5386
5386
|
var ZodFirstPartyTypeKind;
|
|
5387
5387
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5388
5388
|
//#endregion
|
|
5389
|
-
//#region ../types/dist/sleep-
|
|
5389
|
+
//#region ../types/dist/sleep-B1y0Fo1U.mjs
|
|
5390
5390
|
/**
|
|
5391
5391
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5392
5392
|
* window to float samples (D455).
|
|
@@ -31657,14 +31657,81 @@ function summarisePrivacyAudio(profiles) {
|
|
|
31657
31657
|
if (profiles.length === 0) return null;
|
|
31658
31658
|
return profiles.some((p) => p.audioEnabled);
|
|
31659
31659
|
}
|
|
31660
|
+
/**
|
|
31661
|
+
* What a preset id DOES on this camera.
|
|
31662
|
+
*
|
|
31663
|
+
* On several PTZ firmwares the preset namespace is shared: low ids are stored
|
|
31664
|
+
* positions, a reserved band triggers camera FUNCTIONS. The dispensa camera
|
|
31665
|
+
* (Hikvision) returns its own reserved band in the preset list, named by the
|
|
31666
|
+
* firmware:
|
|
31667
|
+
*
|
|
31668
|
+
* 34 Back to origin · 39 Day mode · 40 Night mode · 46 Day/Night Auto Mode
|
|
31669
|
+
* 92 Set manual limits · 93 Save manual limits · 94 Remote reboot
|
|
31670
|
+
* 95 Call OSD menu
|
|
31671
|
+
*
|
|
31672
|
+
* We used to hand all of these to the UI as ordinary presets. Saving over one
|
|
31673
|
+
* is what produced `PUT /ISAPI/PTZCtrl/channels/1/presets/34 -> HTTP 500`: the
|
|
31674
|
+
* camera refusing to let "Back to origin" be overwritten, surfaced to the
|
|
31675
|
+
* operator as a transport failure.
|
|
31676
|
+
*
|
|
31677
|
+
* A function preset stays fully usable as a DESTINATION -- `goToPreset('34')`
|
|
31678
|
+
* is the single most useful thing that camera can do. Only `savePreset` and
|
|
31679
|
+
* `deletePreset` are gated.
|
|
31680
|
+
*/
|
|
31681
|
+
var PtzPresetFunctionSchema = _enum([
|
|
31682
|
+
"home",
|
|
31683
|
+
"day-mode",
|
|
31684
|
+
"night-mode",
|
|
31685
|
+
"day-night-auto",
|
|
31686
|
+
"set-limits",
|
|
31687
|
+
"save-limits",
|
|
31688
|
+
"reboot",
|
|
31689
|
+
"osd-menu"
|
|
31690
|
+
]);
|
|
31660
31691
|
var PtzPresetSchema = object({
|
|
31661
31692
|
id: string(),
|
|
31662
|
-
name: string()
|
|
31693
|
+
name: string(),
|
|
31694
|
+
/**
|
|
31695
|
+
* The firmware function this id performs, when the provider KNOWS the id is
|
|
31696
|
+
* reserved. `null` = an ordinary stored position, OR a reserved id this
|
|
31697
|
+
* provider does not recognise -- the two are told apart by `writable`.
|
|
31698
|
+
*/
|
|
31699
|
+
fn: PtzPresetFunctionSchema.nullable(),
|
|
31700
|
+
/**
|
|
31701
|
+
* Whether `savePreset` / `deletePreset` may target this id. A provider sets
|
|
31702
|
+
* it false only for a band it KNOWS is reserved; where the firmware is not
|
|
31703
|
+
* documented it stays true and a refusal comes from the camera, named.
|
|
31704
|
+
*/
|
|
31705
|
+
writable: boolean()
|
|
31663
31706
|
});
|
|
31707
|
+
/**
|
|
31708
|
+
* Where the head is pointing -- per axis, and `null` when the driver cannot
|
|
31709
|
+
* read it.
|
|
31710
|
+
*
|
|
31711
|
+
* It used to be three REQUIRED numbers, which left a provider that cannot read
|
|
31712
|
+
* position no way to say so. All four said `0`:
|
|
31713
|
+
*
|
|
31714
|
+
* reolink `return { pan: 0, tilt: 0, zoom: 0 }` "until a position-query
|
|
31715
|
+
* path lands upstream"
|
|
31716
|
+
* hikvision stub, "firmware-dependent and frequently absent"
|
|
31717
|
+
* amcrest stub, "no generic absolute-position read for this model family"
|
|
31718
|
+
* onvif throws
|
|
31719
|
+
*
|
|
31720
|
+
* So every consumer asking where a camera points was told "perfectly centred"
|
|
31721
|
+
* by four cameras that had never looked. That is D393 -- a measurement that
|
|
31722
|
+
* FAILED is `null`, never `0`, and the type says so all the way to the
|
|
31723
|
+
* decision -- the same shape as the unreadable `statfs` folded into "0 bytes of
|
|
31724
|
+
* headroom", which evacuated a healthy disk.
|
|
31725
|
+
*
|
|
31726
|
+
* D393 also says to check for a narrower structural TWIN of the result type.
|
|
31727
|
+
* There is one: `PtzStatusSchema` extends this, so the lie had already
|
|
31728
|
+
* propagated into `getStatus`, which is the surface `getPosition`'s own comment
|
|
31729
|
+
* tells callers to migrate to.
|
|
31730
|
+
*/
|
|
31664
31731
|
var PtzPositionSchema = object({
|
|
31665
|
-
pan: number(),
|
|
31666
|
-
tilt: number(),
|
|
31667
|
-
zoom: number()
|
|
31732
|
+
pan: number().nullable(),
|
|
31733
|
+
tilt: number().nullable(),
|
|
31734
|
+
zoom: number().nullable()
|
|
31668
31735
|
});
|
|
31669
31736
|
var PtzMoveCommandSchema = object({
|
|
31670
31737
|
pan: number().optional(),
|
|
@@ -31686,7 +31753,72 @@ var PtzOptionsSchema = object({
|
|
|
31686
31753
|
maxPresets: number().optional(),
|
|
31687
31754
|
/** Whether the camera exposes a controllable autofocus toggle
|
|
31688
31755
|
* (boolean `hasX` per the getOptions availability convention). */
|
|
31689
|
-
hasAutofocus: boolean()
|
|
31756
|
+
hasAutofocus: boolean(),
|
|
31757
|
+
/**
|
|
31758
|
+
* How many distinct speeds this camera's NATIVE scale offers.
|
|
31759
|
+
*
|
|
31760
|
+
* The cap's `speed` is normalized 0..1 and each provider maps it down --
|
|
31761
|
+
* Baichuan 1..63, Dahua 1..8, ISAPI's percentage -100..100. The normalized
|
|
31762
|
+
* value hides how coarse that really is: asking an amcrest for `0.37` is
|
|
31763
|
+
* meaningless, because it has eight steps and three of them round to the
|
|
31764
|
+
* same one. A loop that tunes its own gain has to know the granularity of
|
|
31765
|
+
* the knob it is turning.
|
|
31766
|
+
*
|
|
31767
|
+
* `null` = the driver does not know, or the scale is continuous (ONVIF takes
|
|
31768
|
+
* a float). Never a made-up number.
|
|
31769
|
+
*/
|
|
31770
|
+
speedSteps: number().int().positive().nullable(),
|
|
31771
|
+
/**
|
|
31772
|
+
* How long ONE `move` pulse runs on this driver, ms.
|
|
31773
|
+
*
|
|
31774
|
+
* `move` is a self-terminating burst everywhere, but every provider hardcoded
|
|
31775
|
+
* its own duration in private -- 200 reolink, 350 amcrest, 500 hikvision,
|
|
31776
|
+
* 1000 onvif -- so no caller could read it. It is the DENOMINATOR of "how far
|
|
31777
|
+
* did the head travel per pulse": without it a measured displacement has no
|
|
31778
|
+
* gain to be divided into.
|
|
31779
|
+
*
|
|
31780
|
+
* `null` = the driver cannot say.
|
|
31781
|
+
*/
|
|
31782
|
+
moveImpulseMs: number().int().positive().nullable()
|
|
31783
|
+
});
|
|
31784
|
+
/**
|
|
31785
|
+
* Which preset `goHome` goes to, and WHO decided.
|
|
31786
|
+
*
|
|
31787
|
+
* `goHome` used to be three vendor "conventions" written in three comments --
|
|
31788
|
+
* Reolink preset 0, Hikvision preset '1', Dahua preset 1 -- and on this fleet
|
|
31789
|
+
* not one of the three cameras honours its own:
|
|
31790
|
+
*
|
|
31791
|
+
* 592 Videocamera camera Daniel (reolink) preset 0 holds "stanza"
|
|
31792
|
+
* 1438 Videocamera dispensa (hikvision) preset 1 holds "Credenza"
|
|
31793
|
+
* 3836 Videocamera studio (amcrest) preset 1 holds "Preset1"
|
|
31794
|
+
*
|
|
31795
|
+
* So `goHome` meant "go wherever the operator happened to save in slot 0 or 1",
|
|
31796
|
+
* and two of the three swallowed the failure in a bare `catch`. For autotrack
|
|
31797
|
+
* this is the HOTTEST path -- it runs every time a subject is released -- so it
|
|
31798
|
+
* needs an answer that is true per camera and audible when it is missing.
|
|
31799
|
+
*
|
|
31800
|
+
* `source` is what makes it honest:
|
|
31801
|
+
* - `operator` -- picked in the UI, stored by the provider.
|
|
31802
|
+
* - `firmware` -- the camera itself named a preset `fn: 'home'` (Hikvision's
|
|
31803
|
+
* "Back to origin"), adopted as the default with nothing to configure.
|
|
31804
|
+
* - `native` -- the driver homes WITHOUT a preset at all. ONVIF does:
|
|
31805
|
+
* `goHome` is `ptzAbsoluteMove({x:0, y:0, zoom:0})`. `presetId` is null and
|
|
31806
|
+
* that is not a failure -- there is nothing to pick, and the UI must offer
|
|
31807
|
+
* no picker.
|
|
31808
|
+
* - `none` -- nothing is configured and the camera names nothing.
|
|
31809
|
+
* `goHome` REFUSES rather than moving the head somewhere arbitrary.
|
|
31810
|
+
*
|
|
31811
|
+
* `source === 'none'` is the refusal condition, NOT `presetId === null`: the
|
|
31812
|
+
* native case has no preset and homes perfectly well.
|
|
31813
|
+
*/
|
|
31814
|
+
var PtzHomePresetSchema = object({
|
|
31815
|
+
presetId: string().nullable(),
|
|
31816
|
+
source: _enum([
|
|
31817
|
+
"operator",
|
|
31818
|
+
"firmware",
|
|
31819
|
+
"native",
|
|
31820
|
+
"none"
|
|
31821
|
+
])
|
|
31690
31822
|
});
|
|
31691
31823
|
var ptzCapability = {
|
|
31692
31824
|
name: "ptz",
|
|
@@ -31727,7 +31859,25 @@ var ptzCapability = {
|
|
|
31727
31859
|
auth: "admin"
|
|
31728
31860
|
}),
|
|
31729
31861
|
getOptions: method(object({ deviceId: number() }), PtzOptionsSchema),
|
|
31862
|
+
/**
|
|
31863
|
+
* Move to the camera's configured home preset. THROWS when
|
|
31864
|
+
* `getHomePreset()` resolves to `none` -- a head that did not move must
|
|
31865
|
+
* never look like a head that went home.
|
|
31866
|
+
*/
|
|
31730
31867
|
goHome: method(object({ deviceId: number() }), _void(), { kind: "mutation" }),
|
|
31868
|
+
/** Which preset `goHome` targets, and who decided it. */
|
|
31869
|
+
getHomePreset: method(object({ deviceId: number() }), PtzHomePresetSchema),
|
|
31870
|
+
/**
|
|
31871
|
+
* Pin the home preset for this camera. `null` clears the operator's choice
|
|
31872
|
+
* and falls back to whatever the firmware names, or to `none`.
|
|
31873
|
+
*/
|
|
31874
|
+
setHomePreset: method(object({
|
|
31875
|
+
deviceId: number(),
|
|
31876
|
+
presetId: string().nullable()
|
|
31877
|
+
}), _void(), {
|
|
31878
|
+
kind: "mutation",
|
|
31879
|
+
auth: "admin"
|
|
31880
|
+
}),
|
|
31731
31881
|
/**
|
|
31732
31882
|
* Pull the current PTZ position. Redundant with the auto-injected
|
|
31733
31883
|
* `getStatus` method (see `status` below); kept for callers that
|
|
@@ -41398,6 +41548,12 @@ Object.freeze({
|
|
|
41398
41548
|
addonId: null,
|
|
41399
41549
|
access: "delete"
|
|
41400
41550
|
},
|
|
41551
|
+
"ptz.getHomePreset": {
|
|
41552
|
+
capName: "ptz",
|
|
41553
|
+
capScope: "device",
|
|
41554
|
+
addonId: null,
|
|
41555
|
+
access: "view"
|
|
41556
|
+
},
|
|
41401
41557
|
"ptz.getOptions": {
|
|
41402
41558
|
capName: "ptz",
|
|
41403
41559
|
capScope: "device",
|
|
@@ -41446,6 +41602,12 @@ Object.freeze({
|
|
|
41446
41602
|
addonId: null,
|
|
41447
41603
|
access: "create"
|
|
41448
41604
|
},
|
|
41605
|
+
"ptz.setHomePreset": {
|
|
41606
|
+
capName: "ptz",
|
|
41607
|
+
capScope: "device",
|
|
41608
|
+
addonId: null,
|
|
41609
|
+
access: "create"
|
|
41610
|
+
},
|
|
41449
41611
|
"ptz.stop": {
|
|
41450
41612
|
capName: "ptz",
|
|
41451
41613
|
capScope: "device",
|
|
@@ -44619,6 +44781,11 @@ Object.freeze({
|
|
|
44619
44781
|
form: "single",
|
|
44620
44782
|
optional: false
|
|
44621
44783
|
}],
|
|
44784
|
+
"ptz.getHomePreset": [{
|
|
44785
|
+
name: "deviceId",
|
|
44786
|
+
form: "single",
|
|
44787
|
+
optional: false
|
|
44788
|
+
}],
|
|
44622
44789
|
"ptz.getOptions": [{
|
|
44623
44790
|
name: "deviceId",
|
|
44624
44791
|
form: "single",
|
|
@@ -44659,6 +44826,11 @@ Object.freeze({
|
|
|
44659
44826
|
form: "single",
|
|
44660
44827
|
optional: false
|
|
44661
44828
|
}],
|
|
44829
|
+
"ptz.setHomePreset": [{
|
|
44830
|
+
name: "deviceId",
|
|
44831
|
+
form: "single",
|
|
44832
|
+
optional: false
|
|
44833
|
+
}],
|
|
44662
44834
|
"ptz.stop": [{
|
|
44663
44835
|
name: "deviceId",
|
|
44664
44836
|
form: "single",
|
|
@@ -148323,6 +148495,17 @@ var ReolinkStreamProfileOptionsSchema = object({
|
|
|
148323
148495
|
}).optional()
|
|
148324
148496
|
});
|
|
148325
148497
|
var reolinkCameraSchema = object({
|
|
148498
|
+
/**
|
|
148499
|
+
* Which preset `ptz.goHome` targets on THIS camera.
|
|
148500
|
+
*
|
|
148501
|
+
* Empty = nothing configured, and `goHome` then REFUSES. It used to go to a
|
|
148502
|
+
* hardcoded slot on a vendor "convention" this fleet contradicts: reolink
|
|
148503
|
+
* went to preset 0, which on device 592 holds "stanza"; amcrest went to
|
|
148504
|
+
* preset 1, which on device 3836 holds "Preset1". Both swallowed the failure
|
|
148505
|
+
* in a bare catch, so a head that never moved was indistinguishable from a
|
|
148506
|
+
* head that went home.
|
|
148507
|
+
*/
|
|
148508
|
+
homePresetId: string().default("").describe("Preset id ptz.goHome targets (empty = goHome refuses)"),
|
|
148326
148509
|
host: string().describe("Camera IP or hostname"),
|
|
148327
148510
|
port: number().default(9e3).describe("Baichuan port (default 9000)"),
|
|
148328
148511
|
username: string().default("admin").describe("Username"),
|
|
@@ -149613,6 +149796,20 @@ function coerceNumber(value) {
|
|
|
149613
149796
|
return null;
|
|
149614
149797
|
}
|
|
149615
149798
|
/**
|
|
149799
|
+
* Baichuan's native PTZ speed scale is 1..63; `runPtz` maps the cap's
|
|
149800
|
+
* normalized 0..1 onto it. Named because the autotrack loop has to know the
|
|
149801
|
+
* GRANULARITY of the knob it turns -- a normalized 0.37 means something very
|
|
149802
|
+
* different on 63 steps than on amcrest's 8.
|
|
149803
|
+
*/
|
|
149804
|
+
var BAICHUAN_SPEED_STEPS = 63;
|
|
149805
|
+
/**
|
|
149806
|
+
* How long one `ptz.move` pulse runs, ms -- the `autoStopMs` handed to
|
|
149807
|
+
* Baichuan. It is the DENOMINATOR of "how far did the head travel per pulse",
|
|
149808
|
+
* so it is exported through `getOptions` rather than left a literal no caller
|
|
149809
|
+
* could read.
|
|
149810
|
+
*/
|
|
149811
|
+
var PTZ_MOVE_IMPULSE_MS = 200;
|
|
149812
|
+
/**
|
|
149616
149813
|
* Per-device transient diagnostics blob populated from the lib's
|
|
149617
149814
|
* `getOnlineUserSessionsForUi` + `getSocketPoolSummary` +
|
|
149618
149815
|
* `getSocketPoolCooldownStatus` calls. NOT persisted — recomputed on
|
|
@@ -149954,6 +150151,24 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
149954
150151
|
* during `adoptDevice`). Standalone cameras default to 0 so the
|
|
149955
150152
|
* pre-Hub behavior is preserved verbatim.
|
|
149956
150153
|
*/
|
|
150154
|
+
/**
|
|
150155
|
+
* Which preset `goHome` targets, and who decided it.
|
|
150156
|
+
*
|
|
150157
|
+
* Baichuan names no home of its own, so the operator's pick is the only
|
|
150158
|
+
* answer there can be. With none, `none` -- and `goHome` refuses rather than
|
|
150159
|
+
* moving the head to whatever happens to sit in slot 0.
|
|
150160
|
+
*/
|
|
150161
|
+
async resolveHomePreset() {
|
|
150162
|
+
const chosen = this.config.get("homePresetId") ?? "";
|
|
150163
|
+
if (chosen === "") return {
|
|
150164
|
+
presetId: null,
|
|
150165
|
+
source: "none"
|
|
150166
|
+
};
|
|
150167
|
+
return {
|
|
150168
|
+
presetId: chosen,
|
|
150169
|
+
source: "operator"
|
|
150170
|
+
};
|
|
150171
|
+
}
|
|
149957
150172
|
getChannel() {
|
|
149958
150173
|
const ch = this.config.get("channel");
|
|
149959
150174
|
if (typeof ch === "number" && ch >= 0) return ch;
|
|
@@ -153331,7 +153546,9 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153331
153546
|
const channel = this.getChannel();
|
|
153332
153547
|
return (await api.getPtzPresets(channel)).map((p) => ({
|
|
153333
153548
|
id: String(p.id),
|
|
153334
|
-
name: p.name
|
|
153549
|
+
name: p.name,
|
|
153550
|
+
fn: null,
|
|
153551
|
+
writable: true
|
|
153335
153552
|
}));
|
|
153336
153553
|
} catch {
|
|
153337
153554
|
return [];
|
|
@@ -153367,7 +153584,9 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153367
153584
|
hasTilt: false,
|
|
153368
153585
|
hasZoom: false,
|
|
153369
153586
|
supportsPresets: false,
|
|
153370
|
-
hasAutofocus: false
|
|
153587
|
+
hasAutofocus: false,
|
|
153588
|
+
speedSteps: BAICHUAN_SPEED_STEPS,
|
|
153589
|
+
moveImpulseMs: PTZ_MOVE_IMPULSE_MS
|
|
153371
153590
|
};
|
|
153372
153591
|
const hasAutofocus = this.config.get("deviceCache")?.autoFocusSnapshot?.supported === true;
|
|
153373
153592
|
return this.resolveCapOptions({
|
|
@@ -153380,7 +153599,9 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153380
153599
|
hasTilt: capabilities.hasTilt,
|
|
153381
153600
|
hasZoom: capabilities.hasZoom,
|
|
153382
153601
|
supportsPresets: capabilities.hasPresets,
|
|
153383
|
-
hasAutofocus
|
|
153602
|
+
hasAutofocus,
|
|
153603
|
+
speedSteps: BAICHUAN_SPEED_STEPS,
|
|
153604
|
+
moveImpulseMs: PTZ_MOVE_IMPULSE_MS
|
|
153384
153605
|
};
|
|
153385
153606
|
},
|
|
153386
153607
|
fallback: () => {
|
|
@@ -153390,42 +153611,57 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153390
153611
|
hasTilt: hasPtz,
|
|
153391
153612
|
hasZoom: hasPtz,
|
|
153392
153613
|
supportsPresets: hasPtz,
|
|
153393
|
-
hasAutofocus
|
|
153614
|
+
hasAutofocus,
|
|
153615
|
+
speedSteps: BAICHUAN_SPEED_STEPS,
|
|
153616
|
+
moveImpulseMs: PTZ_MOVE_IMPULSE_MS
|
|
153394
153617
|
};
|
|
153395
153618
|
}
|
|
153396
153619
|
});
|
|
153397
153620
|
},
|
|
153398
153621
|
goHome: async ({ deviceId }) => {
|
|
153399
153622
|
if (deviceId !== this.id) return;
|
|
153400
|
-
|
|
153401
|
-
|
|
153402
|
-
|
|
153403
|
-
|
|
153404
|
-
|
|
153623
|
+
const home = await this.resolveHomePreset();
|
|
153624
|
+
if (home.source === "none" || home.presetId === null) throw new Error("No home preset configured for this camera — set one in the PTZ settings");
|
|
153625
|
+
const id = Number(home.presetId);
|
|
153626
|
+
if (!Number.isFinite(id)) throw new Error(`Configured home preset is not a Baichuan id: ${home.presetId}`);
|
|
153627
|
+
const api = await this.ensureApi();
|
|
153628
|
+
const channel = this.getChannel();
|
|
153629
|
+
await api.moveToPtzPreset(channel, id);
|
|
153630
|
+
},
|
|
153631
|
+
getHomePreset: async ({ deviceId }) => {
|
|
153632
|
+
if (deviceId !== this.id) return {
|
|
153633
|
+
presetId: null,
|
|
153634
|
+
source: "none"
|
|
153635
|
+
};
|
|
153636
|
+
return this.resolveHomePreset();
|
|
153637
|
+
},
|
|
153638
|
+
setHomePreset: async ({ deviceId, presetId }) => {
|
|
153639
|
+
if (deviceId !== this.id) return;
|
|
153640
|
+
await this.config.setAll({ homePresetId: presetId ?? "" });
|
|
153405
153641
|
},
|
|
153406
153642
|
getPosition: async ({ deviceId }) => {
|
|
153407
153643
|
if (deviceId !== this.id) return {
|
|
153408
|
-
pan:
|
|
153409
|
-
tilt:
|
|
153410
|
-
zoom:
|
|
153644
|
+
pan: null,
|
|
153645
|
+
tilt: null,
|
|
153646
|
+
zoom: null
|
|
153411
153647
|
};
|
|
153412
153648
|
return {
|
|
153413
|
-
pan:
|
|
153414
|
-
tilt:
|
|
153415
|
-
zoom:
|
|
153649
|
+
pan: null,
|
|
153650
|
+
tilt: null,
|
|
153651
|
+
zoom: null
|
|
153416
153652
|
};
|
|
153417
153653
|
},
|
|
153418
153654
|
getStatus: async ({ deviceId }) => {
|
|
153419
153655
|
if (deviceId !== this.id) return {
|
|
153420
|
-
pan:
|
|
153421
|
-
tilt:
|
|
153422
|
-
zoom:
|
|
153656
|
+
pan: null,
|
|
153657
|
+
tilt: null,
|
|
153658
|
+
zoom: null,
|
|
153423
153659
|
autofocus: false
|
|
153424
153660
|
};
|
|
153425
153661
|
return {
|
|
153426
|
-
pan:
|
|
153427
|
-
tilt:
|
|
153428
|
-
zoom:
|
|
153662
|
+
pan: null,
|
|
153663
|
+
tilt: null,
|
|
153664
|
+
zoom: null,
|
|
153429
153665
|
autofocus: (this.config.get("deviceCache")?.autoFocusSnapshot)?.enabled === true
|
|
153430
153666
|
};
|
|
153431
153667
|
},
|
|
@@ -153476,13 +153712,13 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153476
153712
|
if (tilt !== void 0 && Math.abs(tilt) > .01) cmds.push(tilt > 0 ? "Up" : "Down");
|
|
153477
153713
|
if (zoom !== void 0 && Math.abs(zoom) > .01) await this.runZoom(api, channel, zoom);
|
|
153478
153714
|
if (cmds.length === 0) return;
|
|
153479
|
-
const baichuanSpeed = speed === void 0 ?
|
|
153715
|
+
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)));
|
|
153480
153716
|
for (const command of cmds) try {
|
|
153481
153717
|
await api.ptz(channel, {
|
|
153482
153718
|
action: "start",
|
|
153483
153719
|
command,
|
|
153484
153720
|
speed: baichuanSpeed,
|
|
153485
|
-
...continuous ? {} : { autoStopMs:
|
|
153721
|
+
...continuous ? {} : { autoStopMs: PTZ_MOVE_IMPULSE_MS }
|
|
153486
153722
|
});
|
|
153487
153723
|
} catch (err) {
|
|
153488
153724
|
this.ctx.logger.warn("Baichuan ptz command failed", {
|
package/dist/addon.mjs
CHANGED
|
@@ -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-B1y0Fo1U.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,25 @@ 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
|
+
}),
|
|
31726
31876
|
/**
|
|
31727
31877
|
* Pull the current PTZ position. Redundant with the auto-injected
|
|
31728
31878
|
* `getStatus` method (see `status` below); kept for callers that
|
|
@@ -41393,6 +41543,12 @@ Object.freeze({
|
|
|
41393
41543
|
addonId: null,
|
|
41394
41544
|
access: "delete"
|
|
41395
41545
|
},
|
|
41546
|
+
"ptz.getHomePreset": {
|
|
41547
|
+
capName: "ptz",
|
|
41548
|
+
capScope: "device",
|
|
41549
|
+
addonId: null,
|
|
41550
|
+
access: "view"
|
|
41551
|
+
},
|
|
41396
41552
|
"ptz.getOptions": {
|
|
41397
41553
|
capName: "ptz",
|
|
41398
41554
|
capScope: "device",
|
|
@@ -41441,6 +41597,12 @@ Object.freeze({
|
|
|
41441
41597
|
addonId: null,
|
|
41442
41598
|
access: "create"
|
|
41443
41599
|
},
|
|
41600
|
+
"ptz.setHomePreset": {
|
|
41601
|
+
capName: "ptz",
|
|
41602
|
+
capScope: "device",
|
|
41603
|
+
addonId: null,
|
|
41604
|
+
access: "create"
|
|
41605
|
+
},
|
|
41444
41606
|
"ptz.stop": {
|
|
41445
41607
|
capName: "ptz",
|
|
41446
41608
|
capScope: "device",
|
|
@@ -44614,6 +44776,11 @@ Object.freeze({
|
|
|
44614
44776
|
form: "single",
|
|
44615
44777
|
optional: false
|
|
44616
44778
|
}],
|
|
44779
|
+
"ptz.getHomePreset": [{
|
|
44780
|
+
name: "deviceId",
|
|
44781
|
+
form: "single",
|
|
44782
|
+
optional: false
|
|
44783
|
+
}],
|
|
44617
44784
|
"ptz.getOptions": [{
|
|
44618
44785
|
name: "deviceId",
|
|
44619
44786
|
form: "single",
|
|
@@ -44654,6 +44821,11 @@ Object.freeze({
|
|
|
44654
44821
|
form: "single",
|
|
44655
44822
|
optional: false
|
|
44656
44823
|
}],
|
|
44824
|
+
"ptz.setHomePreset": [{
|
|
44825
|
+
name: "deviceId",
|
|
44826
|
+
form: "single",
|
|
44827
|
+
optional: false
|
|
44828
|
+
}],
|
|
44657
44829
|
"ptz.stop": [{
|
|
44658
44830
|
name: "deviceId",
|
|
44659
44831
|
form: "single",
|
|
@@ -148318,6 +148490,17 @@ var ReolinkStreamProfileOptionsSchema = object({
|
|
|
148318
148490
|
}).optional()
|
|
148319
148491
|
});
|
|
148320
148492
|
var reolinkCameraSchema = object({
|
|
148493
|
+
/**
|
|
148494
|
+
* Which preset `ptz.goHome` targets on THIS camera.
|
|
148495
|
+
*
|
|
148496
|
+
* Empty = nothing configured, and `goHome` then REFUSES. It used to go to a
|
|
148497
|
+
* hardcoded slot on a vendor "convention" this fleet contradicts: reolink
|
|
148498
|
+
* went to preset 0, which on device 592 holds "stanza"; amcrest went to
|
|
148499
|
+
* preset 1, which on device 3836 holds "Preset1". Both swallowed the failure
|
|
148500
|
+
* in a bare catch, so a head that never moved was indistinguishable from a
|
|
148501
|
+
* head that went home.
|
|
148502
|
+
*/
|
|
148503
|
+
homePresetId: string().default("").describe("Preset id ptz.goHome targets (empty = goHome refuses)"),
|
|
148321
148504
|
host: string().describe("Camera IP or hostname"),
|
|
148322
148505
|
port: number().default(9e3).describe("Baichuan port (default 9000)"),
|
|
148323
148506
|
username: string().default("admin").describe("Username"),
|
|
@@ -149608,6 +149791,20 @@ function coerceNumber(value) {
|
|
|
149608
149791
|
return null;
|
|
149609
149792
|
}
|
|
149610
149793
|
/**
|
|
149794
|
+
* Baichuan's native PTZ speed scale is 1..63; `runPtz` maps the cap's
|
|
149795
|
+
* normalized 0..1 onto it. Named because the autotrack loop has to know the
|
|
149796
|
+
* GRANULARITY of the knob it turns -- a normalized 0.37 means something very
|
|
149797
|
+
* different on 63 steps than on amcrest's 8.
|
|
149798
|
+
*/
|
|
149799
|
+
var BAICHUAN_SPEED_STEPS = 63;
|
|
149800
|
+
/**
|
|
149801
|
+
* How long one `ptz.move` pulse runs, ms -- the `autoStopMs` handed to
|
|
149802
|
+
* Baichuan. It is the DENOMINATOR of "how far did the head travel per pulse",
|
|
149803
|
+
* so it is exported through `getOptions` rather than left a literal no caller
|
|
149804
|
+
* could read.
|
|
149805
|
+
*/
|
|
149806
|
+
var PTZ_MOVE_IMPULSE_MS = 200;
|
|
149807
|
+
/**
|
|
149611
149808
|
* Per-device transient diagnostics blob populated from the lib's
|
|
149612
149809
|
* `getOnlineUserSessionsForUi` + `getSocketPoolSummary` +
|
|
149613
149810
|
* `getSocketPoolCooldownStatus` calls. NOT persisted — recomputed on
|
|
@@ -149949,6 +150146,24 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
149949
150146
|
* during `adoptDevice`). Standalone cameras default to 0 so the
|
|
149950
150147
|
* pre-Hub behavior is preserved verbatim.
|
|
149951
150148
|
*/
|
|
150149
|
+
/**
|
|
150150
|
+
* Which preset `goHome` targets, and who decided it.
|
|
150151
|
+
*
|
|
150152
|
+
* Baichuan names no home of its own, so the operator's pick is the only
|
|
150153
|
+
* answer there can be. With none, `none` -- and `goHome` refuses rather than
|
|
150154
|
+
* moving the head to whatever happens to sit in slot 0.
|
|
150155
|
+
*/
|
|
150156
|
+
async resolveHomePreset() {
|
|
150157
|
+
const chosen = this.config.get("homePresetId") ?? "";
|
|
150158
|
+
if (chosen === "") return {
|
|
150159
|
+
presetId: null,
|
|
150160
|
+
source: "none"
|
|
150161
|
+
};
|
|
150162
|
+
return {
|
|
150163
|
+
presetId: chosen,
|
|
150164
|
+
source: "operator"
|
|
150165
|
+
};
|
|
150166
|
+
}
|
|
149952
150167
|
getChannel() {
|
|
149953
150168
|
const ch = this.config.get("channel");
|
|
149954
150169
|
if (typeof ch === "number" && ch >= 0) return ch;
|
|
@@ -153326,7 +153541,9 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153326
153541
|
const channel = this.getChannel();
|
|
153327
153542
|
return (await api.getPtzPresets(channel)).map((p) => ({
|
|
153328
153543
|
id: String(p.id),
|
|
153329
|
-
name: p.name
|
|
153544
|
+
name: p.name,
|
|
153545
|
+
fn: null,
|
|
153546
|
+
writable: true
|
|
153330
153547
|
}));
|
|
153331
153548
|
} catch {
|
|
153332
153549
|
return [];
|
|
@@ -153362,7 +153579,9 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153362
153579
|
hasTilt: false,
|
|
153363
153580
|
hasZoom: false,
|
|
153364
153581
|
supportsPresets: false,
|
|
153365
|
-
hasAutofocus: false
|
|
153582
|
+
hasAutofocus: false,
|
|
153583
|
+
speedSteps: BAICHUAN_SPEED_STEPS,
|
|
153584
|
+
moveImpulseMs: PTZ_MOVE_IMPULSE_MS
|
|
153366
153585
|
};
|
|
153367
153586
|
const hasAutofocus = this.config.get("deviceCache")?.autoFocusSnapshot?.supported === true;
|
|
153368
153587
|
return this.resolveCapOptions({
|
|
@@ -153375,7 +153594,9 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153375
153594
|
hasTilt: capabilities.hasTilt,
|
|
153376
153595
|
hasZoom: capabilities.hasZoom,
|
|
153377
153596
|
supportsPresets: capabilities.hasPresets,
|
|
153378
|
-
hasAutofocus
|
|
153597
|
+
hasAutofocus,
|
|
153598
|
+
speedSteps: BAICHUAN_SPEED_STEPS,
|
|
153599
|
+
moveImpulseMs: PTZ_MOVE_IMPULSE_MS
|
|
153379
153600
|
};
|
|
153380
153601
|
},
|
|
153381
153602
|
fallback: () => {
|
|
@@ -153385,42 +153606,57 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153385
153606
|
hasTilt: hasPtz,
|
|
153386
153607
|
hasZoom: hasPtz,
|
|
153387
153608
|
supportsPresets: hasPtz,
|
|
153388
|
-
hasAutofocus
|
|
153609
|
+
hasAutofocus,
|
|
153610
|
+
speedSteps: BAICHUAN_SPEED_STEPS,
|
|
153611
|
+
moveImpulseMs: PTZ_MOVE_IMPULSE_MS
|
|
153389
153612
|
};
|
|
153390
153613
|
}
|
|
153391
153614
|
});
|
|
153392
153615
|
},
|
|
153393
153616
|
goHome: async ({ deviceId }) => {
|
|
153394
153617
|
if (deviceId !== this.id) return;
|
|
153395
|
-
|
|
153396
|
-
|
|
153397
|
-
|
|
153398
|
-
|
|
153399
|
-
|
|
153618
|
+
const home = await this.resolveHomePreset();
|
|
153619
|
+
if (home.source === "none" || home.presetId === null) throw new Error("No home preset configured for this camera — set one in the PTZ settings");
|
|
153620
|
+
const id = Number(home.presetId);
|
|
153621
|
+
if (!Number.isFinite(id)) throw new Error(`Configured home preset is not a Baichuan id: ${home.presetId}`);
|
|
153622
|
+
const api = await this.ensureApi();
|
|
153623
|
+
const channel = this.getChannel();
|
|
153624
|
+
await api.moveToPtzPreset(channel, id);
|
|
153625
|
+
},
|
|
153626
|
+
getHomePreset: async ({ deviceId }) => {
|
|
153627
|
+
if (deviceId !== this.id) return {
|
|
153628
|
+
presetId: null,
|
|
153629
|
+
source: "none"
|
|
153630
|
+
};
|
|
153631
|
+
return this.resolveHomePreset();
|
|
153632
|
+
},
|
|
153633
|
+
setHomePreset: async ({ deviceId, presetId }) => {
|
|
153634
|
+
if (deviceId !== this.id) return;
|
|
153635
|
+
await this.config.setAll({ homePresetId: presetId ?? "" });
|
|
153400
153636
|
},
|
|
153401
153637
|
getPosition: async ({ deviceId }) => {
|
|
153402
153638
|
if (deviceId !== this.id) return {
|
|
153403
|
-
pan:
|
|
153404
|
-
tilt:
|
|
153405
|
-
zoom:
|
|
153639
|
+
pan: null,
|
|
153640
|
+
tilt: null,
|
|
153641
|
+
zoom: null
|
|
153406
153642
|
};
|
|
153407
153643
|
return {
|
|
153408
|
-
pan:
|
|
153409
|
-
tilt:
|
|
153410
|
-
zoom:
|
|
153644
|
+
pan: null,
|
|
153645
|
+
tilt: null,
|
|
153646
|
+
zoom: null
|
|
153411
153647
|
};
|
|
153412
153648
|
},
|
|
153413
153649
|
getStatus: async ({ deviceId }) => {
|
|
153414
153650
|
if (deviceId !== this.id) return {
|
|
153415
|
-
pan:
|
|
153416
|
-
tilt:
|
|
153417
|
-
zoom:
|
|
153651
|
+
pan: null,
|
|
153652
|
+
tilt: null,
|
|
153653
|
+
zoom: null,
|
|
153418
153654
|
autofocus: false
|
|
153419
153655
|
};
|
|
153420
153656
|
return {
|
|
153421
|
-
pan:
|
|
153422
|
-
tilt:
|
|
153423
|
-
zoom:
|
|
153657
|
+
pan: null,
|
|
153658
|
+
tilt: null,
|
|
153659
|
+
zoom: null,
|
|
153424
153660
|
autofocus: (this.config.get("deviceCache")?.autoFocusSnapshot)?.enabled === true
|
|
153425
153661
|
};
|
|
153426
153662
|
},
|
|
@@ -153471,13 +153707,13 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
153471
153707
|
if (tilt !== void 0 && Math.abs(tilt) > .01) cmds.push(tilt > 0 ? "Up" : "Down");
|
|
153472
153708
|
if (zoom !== void 0 && Math.abs(zoom) > .01) await this.runZoom(api, channel, zoom);
|
|
153473
153709
|
if (cmds.length === 0) return;
|
|
153474
|
-
const baichuanSpeed = speed === void 0 ?
|
|
153710
|
+
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
153711
|
for (const command of cmds) try {
|
|
153476
153712
|
await api.ptz(channel, {
|
|
153477
153713
|
action: "start",
|
|
153478
153714
|
command,
|
|
153479
153715
|
speed: baichuanSpeed,
|
|
153480
|
-
...continuous ? {} : { autoStopMs:
|
|
153716
|
+
...continuous ? {} : { autoStopMs: PTZ_MOVE_IMPULSE_MS }
|
|
153481
153717
|
});
|
|
153482
153718
|
} catch (err) {
|
|
153483
153719
|
this.ctx.logger.warn("Baichuan ptz command failed", {
|