@camstack/addon-export-hap 1.2.121 → 1.2.123
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/hap-export.addon.js +255 -29
- package/dist/hap-export.addon.mjs +255 -29
- package/package.json +1 -1
package/dist/hap-export.addon.js
CHANGED
|
@@ -5445,7 +5445,7 @@ var ZodIssueCode = {
|
|
|
5445
5445
|
var ZodFirstPartyTypeKind;
|
|
5446
5446
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5447
5447
|
//#endregion
|
|
5448
|
-
//#region ../types/dist/sleep-
|
|
5448
|
+
//#region ../types/dist/sleep-B1y0Fo1U.mjs
|
|
5449
5449
|
/**
|
|
5450
5450
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5451
5451
|
* window to float samples (D455).
|
|
@@ -7446,7 +7446,7 @@ method(object({ deviceId: number() }), array(StreamSourceEntrySchema)), method(o
|
|
|
7446
7446
|
input: unknown()
|
|
7447
7447
|
}), unknown(), { kind: "mutation" }), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), unknown().nullable()), method(object({ deviceId: number() }), RawStateResultSchema.nullable(), { auth: "protected" });
|
|
7448
7448
|
//#endregion
|
|
7449
|
-
//#region ../types/dist/canonical-hash-
|
|
7449
|
+
//#region ../types/dist/canonical-hash-DBkl8q6V.mjs
|
|
7450
7450
|
var AUDIO_ENCODER_BY_CODEC = {
|
|
7451
7451
|
opus: "libopus",
|
|
7452
7452
|
aac: "aac",
|
|
@@ -7511,8 +7511,41 @@ function buildInputArgs(input, decodeHwAccel) {
|
|
|
7511
7511
|
args.push("-i", input.url);
|
|
7512
7512
|
return args;
|
|
7513
7513
|
}
|
|
7514
|
+
/**
|
|
7515
|
+
* Derive the output `-map` selectors. The three cases, in the order they are
|
|
7516
|
+
* decided:
|
|
7517
|
+
*
|
|
7518
|
+
* 1. **A filter graph** — the picture is the graph's declared video pad, and
|
|
7519
|
+
* the audio is its audio pad when it has one, otherwise input 0's mic. A
|
|
7520
|
+
* graph's output is not addressable any other way.
|
|
7521
|
+
* 2. **N inputs, no graph** — map input 0 EXPLICITLY. ffmpeg's own selection
|
|
7522
|
+
* picks the "best" stream across ALL inputs, so the egress would silently
|
|
7523
|
+
* become whichever camera happens to have the largest frame.
|
|
7524
|
+
* 3. **One input, no graph** — no `-map`. Anything else would change the argv
|
|
7525
|
+
* of every call site that exists today for no gain.
|
|
7526
|
+
*/
|
|
7527
|
+
function resolveStreamMaps(selection) {
|
|
7528
|
+
const graph = selection.filterGraph;
|
|
7529
|
+
if (graph) return {
|
|
7530
|
+
video: `[${graph.videoOutLabel}]`,
|
|
7531
|
+
audio: graph.audioOutLabel ? `[${graph.audioOutLabel}]` : "0:a:0?"
|
|
7532
|
+
};
|
|
7533
|
+
if (selection.extraInputs?.length) return {
|
|
7534
|
+
video: "0:v:0",
|
|
7535
|
+
audio: "0:a:0?"
|
|
7536
|
+
};
|
|
7537
|
+
return {
|
|
7538
|
+
video: null,
|
|
7539
|
+
audio: null
|
|
7540
|
+
};
|
|
7541
|
+
}
|
|
7514
7542
|
/** The `-vf` filter args, or `[]` when a consumer `-vf` already claims the slot. */
|
|
7515
|
-
function buildVideoFilterArgs(scale, outputArgs) {
|
|
7543
|
+
function buildVideoFilterArgs(scale, outputArgs, filterGraph) {
|
|
7544
|
+
if (filterGraph) {
|
|
7545
|
+
if (scale) throw new Error("FfmpegInvocation: video.scale cannot be combined with a filter graph — scale inside the graph instead (the -vf would feed an output the graph already feeds)");
|
|
7546
|
+
if (outputArgs.some((a) => a === "-vf")) throw new Error("FfmpegInvocation: outputArgs carries a -vf while a filter graph feeds the same output — fold the filter into the graph");
|
|
7547
|
+
return [];
|
|
7548
|
+
}
|
|
7516
7549
|
if (!scale) return [];
|
|
7517
7550
|
if (outputArgs.some((a) => a === "-vf")) return [];
|
|
7518
7551
|
if (scale.mode === "exact") return ["-vf", `scale=${scale.width}:${scale.height}`];
|
|
@@ -7536,14 +7569,17 @@ function buildRateControlArgs(video) {
|
|
|
7536
7569
|
];
|
|
7537
7570
|
}
|
|
7538
7571
|
/** The whole video block (`-vf` … `-c:v` … knobs), after `-i`. */
|
|
7539
|
-
function buildVideoArgs(video, outputArgs) {
|
|
7540
|
-
if (video.kind === "copy")
|
|
7541
|
-
"
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7572
|
+
function buildVideoArgs(video, outputArgs, filterGraph) {
|
|
7573
|
+
if (video.kind === "copy") {
|
|
7574
|
+
if (filterGraph) throw new Error("FfmpegInvocation: a filter graph produces a NEW picture, so video.kind must be `encode`");
|
|
7575
|
+
return [
|
|
7576
|
+
"-c:v",
|
|
7577
|
+
"copy",
|
|
7578
|
+
...video.bitstreamFilter ? ["-bsf:v", video.bitstreamFilter] : []
|
|
7579
|
+
];
|
|
7580
|
+
}
|
|
7545
7581
|
const args = [
|
|
7546
|
-
...buildVideoFilterArgs(video.scale, outputArgs),
|
|
7582
|
+
...buildVideoFilterArgs(video.scale, outputArgs, filterGraph),
|
|
7547
7583
|
"-c:v",
|
|
7548
7584
|
video.encoder
|
|
7549
7585
|
];
|
|
@@ -7698,20 +7734,39 @@ function buildAudioSidecarArgs(sidecar) {
|
|
|
7698
7734
|
* [<input.extraArgs>] │
|
|
7699
7735
|
* [-fflags <flag>…] │
|
|
7700
7736
|
* [-rtsp_transport tcp] │
|
|
7701
|
-
* -i <url> ─┘
|
|
7702
|
-
*
|
|
7737
|
+
* -i <url> ─┘ …repeated per extraInput
|
|
7738
|
+
* [-filter_complex <graph>] ─ global, after the LAST -i
|
|
7739
|
+
* [-map <video>] <video block> <threads> ─┐ OUTPUT options.
|
|
7740
|
+
* [-map <audio>] <audio block> │
|
|
7703
7741
|
* <consumer outputArgs verbatim> │
|
|
7704
7742
|
* <sink> ─┘ terminal
|
|
7743
|
+
*
|
|
7744
|
+
* The `-map`s are DERIVED (see {@link resolveStreamMaps}), never constants:
|
|
7745
|
+
* one input with no graph emits none at all, which is why the argv of every
|
|
7746
|
+
* call site that predates the N-input extension is unchanged to the byte.
|
|
7747
|
+
*
|
|
7748
|
+
* {@link FfmpegInvocation.audioSidecar} is the one map that stays literal
|
|
7749
|
+
* (`0:a:0?`): the sidecar is defined as the SOURCE's microphone on its own RTP
|
|
7750
|
+
* output, not as whatever the main output happens to carry.
|
|
7705
7751
|
*/
|
|
7706
7752
|
function buildFfmpegArgs(inv) {
|
|
7707
|
-
const
|
|
7753
|
+
const graph = inv.filterGraph ?? null;
|
|
7754
|
+
const head = [
|
|
7755
|
+
...logBannerArgs(inv.logLevel),
|
|
7756
|
+
...buildInputArgs(inv.input, inv.decodeHwAccel),
|
|
7757
|
+
...(inv.extraInputs ?? []).flatMap((extra) => buildInputArgs(extra, inv.decodeHwAccel)),
|
|
7758
|
+
...graph ? ["-filter_complex", graph.graph] : []
|
|
7759
|
+
];
|
|
7708
7760
|
const threadArgs = inv.threadCount > 0 ? ["-threads", String(inv.threadCount)] : [];
|
|
7761
|
+
const maps = resolveStreamMaps(inv);
|
|
7762
|
+
const videoMapArgs = maps.video ? ["-map", maps.video] : [];
|
|
7763
|
+
const audioMapArgs = maps.audio ? ["-map", maps.audio] : [];
|
|
7709
7764
|
if (inv.sink.kind === "rtp-outputs") {
|
|
7710
7765
|
const videoLeg = inv.sink.video ? [
|
|
7711
7766
|
"-an",
|
|
7712
7767
|
"-map",
|
|
7713
|
-
"0:v:0",
|
|
7714
|
-
...buildVideoArgs(inv.video, inv.outputArgs),
|
|
7768
|
+
maps.video ?? "0:v:0",
|
|
7769
|
+
...buildVideoArgs(inv.video, inv.outputArgs, graph),
|
|
7715
7770
|
...threadArgs,
|
|
7716
7771
|
...inv.outputArgs,
|
|
7717
7772
|
...buildRtpOutputArgs(inv.sink.video)
|
|
@@ -7719,7 +7774,7 @@ function buildFfmpegArgs(inv) {
|
|
|
7719
7774
|
const audioLeg = inv.sink.audio ? [
|
|
7720
7775
|
"-vn",
|
|
7721
7776
|
"-map",
|
|
7722
|
-
"0:a:0?",
|
|
7777
|
+
maps.audio ?? "0:a:0?",
|
|
7723
7778
|
...buildAudioArgs(inv.audio),
|
|
7724
7779
|
...buildRtpOutputArgs(inv.sink.audio)
|
|
7725
7780
|
] : [];
|
|
@@ -7729,15 +7784,17 @@ function buildFfmpegArgs(inv) {
|
|
|
7729
7784
|
...audioLeg
|
|
7730
7785
|
];
|
|
7731
7786
|
}
|
|
7787
|
+
const mutedAudio = isElementaryVideoSink(inv.sink) || inv.audio.kind === "none";
|
|
7732
7788
|
const audioArgs = isElementaryVideoSink(inv.sink) ? ["-an"] : buildAudioArgs(inv.audio);
|
|
7733
|
-
const sinkArgs = buildStdoutOrRtspSinkArgs(inv.sink);
|
|
7734
7789
|
return [
|
|
7735
7790
|
...head,
|
|
7736
|
-
...
|
|
7791
|
+
...videoMapArgs,
|
|
7792
|
+
...buildVideoArgs(inv.video, inv.outputArgs, graph),
|
|
7737
7793
|
...threadArgs,
|
|
7794
|
+
...mutedAudio ? [] : audioMapArgs,
|
|
7738
7795
|
...audioArgs,
|
|
7739
7796
|
...inv.outputArgs,
|
|
7740
|
-
...
|
|
7797
|
+
...buildStdoutOrRtspSinkArgs(inv.sink),
|
|
7741
7798
|
...inv.audioSidecar ? buildAudioSidecarArgs(inv.audioSidecar) : []
|
|
7742
7799
|
];
|
|
7743
7800
|
}
|
|
@@ -29596,14 +29653,81 @@ DeviceType.Camera, method(object({ deviceId: number() }), PrivacyMaskOptionsSche
|
|
|
29596
29653
|
kind: "mutation",
|
|
29597
29654
|
auth: "admin"
|
|
29598
29655
|
});
|
|
29656
|
+
/**
|
|
29657
|
+
* What a preset id DOES on this camera.
|
|
29658
|
+
*
|
|
29659
|
+
* On several PTZ firmwares the preset namespace is shared: low ids are stored
|
|
29660
|
+
* positions, a reserved band triggers camera FUNCTIONS. The dispensa camera
|
|
29661
|
+
* (Hikvision) returns its own reserved band in the preset list, named by the
|
|
29662
|
+
* firmware:
|
|
29663
|
+
*
|
|
29664
|
+
* 34 Back to origin · 39 Day mode · 40 Night mode · 46 Day/Night Auto Mode
|
|
29665
|
+
* 92 Set manual limits · 93 Save manual limits · 94 Remote reboot
|
|
29666
|
+
* 95 Call OSD menu
|
|
29667
|
+
*
|
|
29668
|
+
* We used to hand all of these to the UI as ordinary presets. Saving over one
|
|
29669
|
+
* is what produced `PUT /ISAPI/PTZCtrl/channels/1/presets/34 -> HTTP 500`: the
|
|
29670
|
+
* camera refusing to let "Back to origin" be overwritten, surfaced to the
|
|
29671
|
+
* operator as a transport failure.
|
|
29672
|
+
*
|
|
29673
|
+
* A function preset stays fully usable as a DESTINATION -- `goToPreset('34')`
|
|
29674
|
+
* is the single most useful thing that camera can do. Only `savePreset` and
|
|
29675
|
+
* `deletePreset` are gated.
|
|
29676
|
+
*/
|
|
29677
|
+
var PtzPresetFunctionSchema = _enum([
|
|
29678
|
+
"home",
|
|
29679
|
+
"day-mode",
|
|
29680
|
+
"night-mode",
|
|
29681
|
+
"day-night-auto",
|
|
29682
|
+
"set-limits",
|
|
29683
|
+
"save-limits",
|
|
29684
|
+
"reboot",
|
|
29685
|
+
"osd-menu"
|
|
29686
|
+
]);
|
|
29599
29687
|
var PtzPresetSchema = object({
|
|
29600
29688
|
id: string(),
|
|
29601
|
-
name: string()
|
|
29689
|
+
name: string(),
|
|
29690
|
+
/**
|
|
29691
|
+
* The firmware function this id performs, when the provider KNOWS the id is
|
|
29692
|
+
* reserved. `null` = an ordinary stored position, OR a reserved id this
|
|
29693
|
+
* provider does not recognise -- the two are told apart by `writable`.
|
|
29694
|
+
*/
|
|
29695
|
+
fn: PtzPresetFunctionSchema.nullable(),
|
|
29696
|
+
/**
|
|
29697
|
+
* Whether `savePreset` / `deletePreset` may target this id. A provider sets
|
|
29698
|
+
* it false only for a band it KNOWS is reserved; where the firmware is not
|
|
29699
|
+
* documented it stays true and a refusal comes from the camera, named.
|
|
29700
|
+
*/
|
|
29701
|
+
writable: boolean()
|
|
29602
29702
|
});
|
|
29703
|
+
/**
|
|
29704
|
+
* Where the head is pointing -- per axis, and `null` when the driver cannot
|
|
29705
|
+
* read it.
|
|
29706
|
+
*
|
|
29707
|
+
* It used to be three REQUIRED numbers, which left a provider that cannot read
|
|
29708
|
+
* position no way to say so. All four said `0`:
|
|
29709
|
+
*
|
|
29710
|
+
* reolink `return { pan: 0, tilt: 0, zoom: 0 }` "until a position-query
|
|
29711
|
+
* path lands upstream"
|
|
29712
|
+
* hikvision stub, "firmware-dependent and frequently absent"
|
|
29713
|
+
* amcrest stub, "no generic absolute-position read for this model family"
|
|
29714
|
+
* onvif throws
|
|
29715
|
+
*
|
|
29716
|
+
* So every consumer asking where a camera points was told "perfectly centred"
|
|
29717
|
+
* by four cameras that had never looked. That is D393 -- a measurement that
|
|
29718
|
+
* FAILED is `null`, never `0`, and the type says so all the way to the
|
|
29719
|
+
* decision -- the same shape as the unreadable `statfs` folded into "0 bytes of
|
|
29720
|
+
* headroom", which evacuated a healthy disk.
|
|
29721
|
+
*
|
|
29722
|
+
* D393 also says to check for a narrower structural TWIN of the result type.
|
|
29723
|
+
* There is one: `PtzStatusSchema` extends this, so the lie had already
|
|
29724
|
+
* propagated into `getStatus`, which is the surface `getPosition`'s own comment
|
|
29725
|
+
* tells callers to migrate to.
|
|
29726
|
+
*/
|
|
29603
29727
|
var PtzPositionSchema = object({
|
|
29604
|
-
pan: number(),
|
|
29605
|
-
tilt: number(),
|
|
29606
|
-
zoom: number()
|
|
29728
|
+
pan: number().nullable(),
|
|
29729
|
+
tilt: number().nullable(),
|
|
29730
|
+
zoom: number().nullable()
|
|
29607
29731
|
});
|
|
29608
29732
|
var PtzMoveCommandSchema = object({
|
|
29609
29733
|
pan: number().optional(),
|
|
@@ -29622,7 +29746,72 @@ var PtzOptionsSchema = object({
|
|
|
29622
29746
|
maxPresets: number().optional(),
|
|
29623
29747
|
/** Whether the camera exposes a controllable autofocus toggle
|
|
29624
29748
|
* (boolean `hasX` per the getOptions availability convention). */
|
|
29625
|
-
hasAutofocus: boolean()
|
|
29749
|
+
hasAutofocus: boolean(),
|
|
29750
|
+
/**
|
|
29751
|
+
* How many distinct speeds this camera's NATIVE scale offers.
|
|
29752
|
+
*
|
|
29753
|
+
* The cap's `speed` is normalized 0..1 and each provider maps it down --
|
|
29754
|
+
* Baichuan 1..63, Dahua 1..8, ISAPI's percentage -100..100. The normalized
|
|
29755
|
+
* value hides how coarse that really is: asking an amcrest for `0.37` is
|
|
29756
|
+
* meaningless, because it has eight steps and three of them round to the
|
|
29757
|
+
* same one. A loop that tunes its own gain has to know the granularity of
|
|
29758
|
+
* the knob it is turning.
|
|
29759
|
+
*
|
|
29760
|
+
* `null` = the driver does not know, or the scale is continuous (ONVIF takes
|
|
29761
|
+
* a float). Never a made-up number.
|
|
29762
|
+
*/
|
|
29763
|
+
speedSteps: number().int().positive().nullable(),
|
|
29764
|
+
/**
|
|
29765
|
+
* How long ONE `move` pulse runs on this driver, ms.
|
|
29766
|
+
*
|
|
29767
|
+
* `move` is a self-terminating burst everywhere, but every provider hardcoded
|
|
29768
|
+
* its own duration in private -- 200 reolink, 350 amcrest, 500 hikvision,
|
|
29769
|
+
* 1000 onvif -- so no caller could read it. It is the DENOMINATOR of "how far
|
|
29770
|
+
* did the head travel per pulse": without it a measured displacement has no
|
|
29771
|
+
* gain to be divided into.
|
|
29772
|
+
*
|
|
29773
|
+
* `null` = the driver cannot say.
|
|
29774
|
+
*/
|
|
29775
|
+
moveImpulseMs: number().int().positive().nullable()
|
|
29776
|
+
});
|
|
29777
|
+
/**
|
|
29778
|
+
* Which preset `goHome` goes to, and WHO decided.
|
|
29779
|
+
*
|
|
29780
|
+
* `goHome` used to be three vendor "conventions" written in three comments --
|
|
29781
|
+
* Reolink preset 0, Hikvision preset '1', Dahua preset 1 -- and on this fleet
|
|
29782
|
+
* not one of the three cameras honours its own:
|
|
29783
|
+
*
|
|
29784
|
+
* 592 Videocamera camera Daniel (reolink) preset 0 holds "stanza"
|
|
29785
|
+
* 1438 Videocamera dispensa (hikvision) preset 1 holds "Credenza"
|
|
29786
|
+
* 3836 Videocamera studio (amcrest) preset 1 holds "Preset1"
|
|
29787
|
+
*
|
|
29788
|
+
* So `goHome` meant "go wherever the operator happened to save in slot 0 or 1",
|
|
29789
|
+
* and two of the three swallowed the failure in a bare `catch`. For autotrack
|
|
29790
|
+
* this is the HOTTEST path -- it runs every time a subject is released -- so it
|
|
29791
|
+
* needs an answer that is true per camera and audible when it is missing.
|
|
29792
|
+
*
|
|
29793
|
+
* `source` is what makes it honest:
|
|
29794
|
+
* - `operator` -- picked in the UI, stored by the provider.
|
|
29795
|
+
* - `firmware` -- the camera itself named a preset `fn: 'home'` (Hikvision's
|
|
29796
|
+
* "Back to origin"), adopted as the default with nothing to configure.
|
|
29797
|
+
* - `native` -- the driver homes WITHOUT a preset at all. ONVIF does:
|
|
29798
|
+
* `goHome` is `ptzAbsoluteMove({x:0, y:0, zoom:0})`. `presetId` is null and
|
|
29799
|
+
* that is not a failure -- there is nothing to pick, and the UI must offer
|
|
29800
|
+
* no picker.
|
|
29801
|
+
* - `none` -- nothing is configured and the camera names nothing.
|
|
29802
|
+
* `goHome` REFUSES rather than moving the head somewhere arbitrary.
|
|
29803
|
+
*
|
|
29804
|
+
* `source === 'none'` is the refusal condition, NOT `presetId === null`: the
|
|
29805
|
+
* native case has no preset and homes perfectly well.
|
|
29806
|
+
*/
|
|
29807
|
+
var PtzHomePresetSchema = object({
|
|
29808
|
+
presetId: string().nullable(),
|
|
29809
|
+
source: _enum([
|
|
29810
|
+
"operator",
|
|
29811
|
+
"firmware",
|
|
29812
|
+
"native",
|
|
29813
|
+
"none"
|
|
29814
|
+
])
|
|
29626
29815
|
});
|
|
29627
29816
|
DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _void(), { kind: "mutation" }), method(PtzMoveCommandSchema.extend({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), array(PtzPresetSchema)), method(object({
|
|
29628
29817
|
deviceId: number(),
|
|
@@ -29640,7 +29829,13 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
|
|
|
29640
29829
|
}), _void(), {
|
|
29641
29830
|
kind: "mutation",
|
|
29642
29831
|
auth: "admin"
|
|
29643
|
-
}), method(object({ deviceId: number() }), PtzOptionsSchema), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }),
|
|
29832
|
+
}), method(object({ deviceId: number() }), PtzOptionsSchema), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), PtzHomePresetSchema), method(object({
|
|
29833
|
+
deviceId: number(),
|
|
29834
|
+
presetId: string().nullable()
|
|
29835
|
+
}), _void(), {
|
|
29836
|
+
kind: "mutation",
|
|
29837
|
+
auth: "admin"
|
|
29838
|
+
}), method(object({ deviceId: number() }), PtzPositionSchema), method(object({
|
|
29644
29839
|
deviceId: number(),
|
|
29645
29840
|
enabled: boolean()
|
|
29646
29841
|
}), _void(), { kind: "mutation" });
|
|
@@ -36680,6 +36875,12 @@ Object.freeze({
|
|
|
36680
36875
|
addonId: null,
|
|
36681
36876
|
access: "delete"
|
|
36682
36877
|
},
|
|
36878
|
+
"ptz.getHomePreset": {
|
|
36879
|
+
capName: "ptz",
|
|
36880
|
+
capScope: "device",
|
|
36881
|
+
addonId: null,
|
|
36882
|
+
access: "view"
|
|
36883
|
+
},
|
|
36683
36884
|
"ptz.getOptions": {
|
|
36684
36885
|
capName: "ptz",
|
|
36685
36886
|
capScope: "device",
|
|
@@ -36728,6 +36929,12 @@ Object.freeze({
|
|
|
36728
36929
|
addonId: null,
|
|
36729
36930
|
access: "create"
|
|
36730
36931
|
},
|
|
36932
|
+
"ptz.setHomePreset": {
|
|
36933
|
+
capName: "ptz",
|
|
36934
|
+
capScope: "device",
|
|
36935
|
+
addonId: null,
|
|
36936
|
+
access: "create"
|
|
36937
|
+
},
|
|
36731
36938
|
"ptz.stop": {
|
|
36732
36939
|
capName: "ptz",
|
|
36733
36940
|
capScope: "device",
|
|
@@ -39901,6 +40108,11 @@ Object.freeze({
|
|
|
39901
40108
|
form: "single",
|
|
39902
40109
|
optional: false
|
|
39903
40110
|
}],
|
|
40111
|
+
"ptz.getHomePreset": [{
|
|
40112
|
+
name: "deviceId",
|
|
40113
|
+
form: "single",
|
|
40114
|
+
optional: false
|
|
40115
|
+
}],
|
|
39904
40116
|
"ptz.getOptions": [{
|
|
39905
40117
|
name: "deviceId",
|
|
39906
40118
|
form: "single",
|
|
@@ -39941,6 +40153,11 @@ Object.freeze({
|
|
|
39941
40153
|
form: "single",
|
|
39942
40154
|
optional: false
|
|
39943
40155
|
}],
|
|
40156
|
+
"ptz.setHomePreset": [{
|
|
40157
|
+
name: "deviceId",
|
|
40158
|
+
form: "single",
|
|
40159
|
+
optional: false
|
|
40160
|
+
}],
|
|
39944
40161
|
"ptz.stop": [{
|
|
39945
40162
|
name: "deviceId",
|
|
39946
40163
|
form: "single",
|
|
@@ -101755,10 +101972,19 @@ async function readPresets(bctx) {
|
|
|
101755
101972
|
try {
|
|
101756
101973
|
const raw = await proxy.ptz?.getPresets({});
|
|
101757
101974
|
if (!Array.isArray(raw)) return [];
|
|
101758
|
-
|
|
101759
|
-
|
|
101760
|
-
|
|
101761
|
-
|
|
101975
|
+
const entries = [];
|
|
101976
|
+
for (const p of raw) {
|
|
101977
|
+
if (typeof p !== "object" || p === null) continue;
|
|
101978
|
+
const candidate = p;
|
|
101979
|
+
if (typeof candidate.id !== "string" || typeof candidate.name !== "string") continue;
|
|
101980
|
+
const fn = candidate.fn;
|
|
101981
|
+
if (fn !== null && fn !== void 0 && fn !== "home") continue;
|
|
101982
|
+
entries.push({
|
|
101983
|
+
id: candidate.id,
|
|
101984
|
+
name: candidate.name
|
|
101985
|
+
});
|
|
101986
|
+
}
|
|
101987
|
+
return entries;
|
|
101762
101988
|
} catch (err) {
|
|
101763
101989
|
ctx.logger.withTags({ deviceId: numericDeviceId }).debug("export-hap: ptz.getPresets failed (non-fatal)", { meta: { error: errMsg$8(err) } });
|
|
101764
101990
|
return [];
|
|
@@ -5433,7 +5433,7 @@ var ZodIssueCode = {
|
|
|
5433
5433
|
var ZodFirstPartyTypeKind;
|
|
5434
5434
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5435
5435
|
//#endregion
|
|
5436
|
-
//#region ../types/dist/sleep-
|
|
5436
|
+
//#region ../types/dist/sleep-B1y0Fo1U.mjs
|
|
5437
5437
|
/**
|
|
5438
5438
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5439
5439
|
* window to float samples (D455).
|
|
@@ -7434,7 +7434,7 @@ method(object({ deviceId: number() }), array(StreamSourceEntrySchema)), method(o
|
|
|
7434
7434
|
input: unknown()
|
|
7435
7435
|
}), unknown(), { kind: "mutation" }), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), unknown().nullable()), method(object({ deviceId: number() }), RawStateResultSchema.nullable(), { auth: "protected" });
|
|
7436
7436
|
//#endregion
|
|
7437
|
-
//#region ../types/dist/canonical-hash-
|
|
7437
|
+
//#region ../types/dist/canonical-hash-DBkl8q6V.mjs
|
|
7438
7438
|
var AUDIO_ENCODER_BY_CODEC = {
|
|
7439
7439
|
opus: "libopus",
|
|
7440
7440
|
aac: "aac",
|
|
@@ -7499,8 +7499,41 @@ function buildInputArgs(input, decodeHwAccel) {
|
|
|
7499
7499
|
args.push("-i", input.url);
|
|
7500
7500
|
return args;
|
|
7501
7501
|
}
|
|
7502
|
+
/**
|
|
7503
|
+
* Derive the output `-map` selectors. The three cases, in the order they are
|
|
7504
|
+
* decided:
|
|
7505
|
+
*
|
|
7506
|
+
* 1. **A filter graph** — the picture is the graph's declared video pad, and
|
|
7507
|
+
* the audio is its audio pad when it has one, otherwise input 0's mic. A
|
|
7508
|
+
* graph's output is not addressable any other way.
|
|
7509
|
+
* 2. **N inputs, no graph** — map input 0 EXPLICITLY. ffmpeg's own selection
|
|
7510
|
+
* picks the "best" stream across ALL inputs, so the egress would silently
|
|
7511
|
+
* become whichever camera happens to have the largest frame.
|
|
7512
|
+
* 3. **One input, no graph** — no `-map`. Anything else would change the argv
|
|
7513
|
+
* of every call site that exists today for no gain.
|
|
7514
|
+
*/
|
|
7515
|
+
function resolveStreamMaps(selection) {
|
|
7516
|
+
const graph = selection.filterGraph;
|
|
7517
|
+
if (graph) return {
|
|
7518
|
+
video: `[${graph.videoOutLabel}]`,
|
|
7519
|
+
audio: graph.audioOutLabel ? `[${graph.audioOutLabel}]` : "0:a:0?"
|
|
7520
|
+
};
|
|
7521
|
+
if (selection.extraInputs?.length) return {
|
|
7522
|
+
video: "0:v:0",
|
|
7523
|
+
audio: "0:a:0?"
|
|
7524
|
+
};
|
|
7525
|
+
return {
|
|
7526
|
+
video: null,
|
|
7527
|
+
audio: null
|
|
7528
|
+
};
|
|
7529
|
+
}
|
|
7502
7530
|
/** The `-vf` filter args, or `[]` when a consumer `-vf` already claims the slot. */
|
|
7503
|
-
function buildVideoFilterArgs(scale, outputArgs) {
|
|
7531
|
+
function buildVideoFilterArgs(scale, outputArgs, filterGraph) {
|
|
7532
|
+
if (filterGraph) {
|
|
7533
|
+
if (scale) throw new Error("FfmpegInvocation: video.scale cannot be combined with a filter graph — scale inside the graph instead (the -vf would feed an output the graph already feeds)");
|
|
7534
|
+
if (outputArgs.some((a) => a === "-vf")) throw new Error("FfmpegInvocation: outputArgs carries a -vf while a filter graph feeds the same output — fold the filter into the graph");
|
|
7535
|
+
return [];
|
|
7536
|
+
}
|
|
7504
7537
|
if (!scale) return [];
|
|
7505
7538
|
if (outputArgs.some((a) => a === "-vf")) return [];
|
|
7506
7539
|
if (scale.mode === "exact") return ["-vf", `scale=${scale.width}:${scale.height}`];
|
|
@@ -7524,14 +7557,17 @@ function buildRateControlArgs(video) {
|
|
|
7524
7557
|
];
|
|
7525
7558
|
}
|
|
7526
7559
|
/** The whole video block (`-vf` … `-c:v` … knobs), after `-i`. */
|
|
7527
|
-
function buildVideoArgs(video, outputArgs) {
|
|
7528
|
-
if (video.kind === "copy")
|
|
7529
|
-
"
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7560
|
+
function buildVideoArgs(video, outputArgs, filterGraph) {
|
|
7561
|
+
if (video.kind === "copy") {
|
|
7562
|
+
if (filterGraph) throw new Error("FfmpegInvocation: a filter graph produces a NEW picture, so video.kind must be `encode`");
|
|
7563
|
+
return [
|
|
7564
|
+
"-c:v",
|
|
7565
|
+
"copy",
|
|
7566
|
+
...video.bitstreamFilter ? ["-bsf:v", video.bitstreamFilter] : []
|
|
7567
|
+
];
|
|
7568
|
+
}
|
|
7533
7569
|
const args = [
|
|
7534
|
-
...buildVideoFilterArgs(video.scale, outputArgs),
|
|
7570
|
+
...buildVideoFilterArgs(video.scale, outputArgs, filterGraph),
|
|
7535
7571
|
"-c:v",
|
|
7536
7572
|
video.encoder
|
|
7537
7573
|
];
|
|
@@ -7686,20 +7722,39 @@ function buildAudioSidecarArgs(sidecar) {
|
|
|
7686
7722
|
* [<input.extraArgs>] │
|
|
7687
7723
|
* [-fflags <flag>…] │
|
|
7688
7724
|
* [-rtsp_transport tcp] │
|
|
7689
|
-
* -i <url> ─┘
|
|
7690
|
-
*
|
|
7725
|
+
* -i <url> ─┘ …repeated per extraInput
|
|
7726
|
+
* [-filter_complex <graph>] ─ global, after the LAST -i
|
|
7727
|
+
* [-map <video>] <video block> <threads> ─┐ OUTPUT options.
|
|
7728
|
+
* [-map <audio>] <audio block> │
|
|
7691
7729
|
* <consumer outputArgs verbatim> │
|
|
7692
7730
|
* <sink> ─┘ terminal
|
|
7731
|
+
*
|
|
7732
|
+
* The `-map`s are DERIVED (see {@link resolveStreamMaps}), never constants:
|
|
7733
|
+
* one input with no graph emits none at all, which is why the argv of every
|
|
7734
|
+
* call site that predates the N-input extension is unchanged to the byte.
|
|
7735
|
+
*
|
|
7736
|
+
* {@link FfmpegInvocation.audioSidecar} is the one map that stays literal
|
|
7737
|
+
* (`0:a:0?`): the sidecar is defined as the SOURCE's microphone on its own RTP
|
|
7738
|
+
* output, not as whatever the main output happens to carry.
|
|
7693
7739
|
*/
|
|
7694
7740
|
function buildFfmpegArgs(inv) {
|
|
7695
|
-
const
|
|
7741
|
+
const graph = inv.filterGraph ?? null;
|
|
7742
|
+
const head = [
|
|
7743
|
+
...logBannerArgs(inv.logLevel),
|
|
7744
|
+
...buildInputArgs(inv.input, inv.decodeHwAccel),
|
|
7745
|
+
...(inv.extraInputs ?? []).flatMap((extra) => buildInputArgs(extra, inv.decodeHwAccel)),
|
|
7746
|
+
...graph ? ["-filter_complex", graph.graph] : []
|
|
7747
|
+
];
|
|
7696
7748
|
const threadArgs = inv.threadCount > 0 ? ["-threads", String(inv.threadCount)] : [];
|
|
7749
|
+
const maps = resolveStreamMaps(inv);
|
|
7750
|
+
const videoMapArgs = maps.video ? ["-map", maps.video] : [];
|
|
7751
|
+
const audioMapArgs = maps.audio ? ["-map", maps.audio] : [];
|
|
7697
7752
|
if (inv.sink.kind === "rtp-outputs") {
|
|
7698
7753
|
const videoLeg = inv.sink.video ? [
|
|
7699
7754
|
"-an",
|
|
7700
7755
|
"-map",
|
|
7701
|
-
"0:v:0",
|
|
7702
|
-
...buildVideoArgs(inv.video, inv.outputArgs),
|
|
7756
|
+
maps.video ?? "0:v:0",
|
|
7757
|
+
...buildVideoArgs(inv.video, inv.outputArgs, graph),
|
|
7703
7758
|
...threadArgs,
|
|
7704
7759
|
...inv.outputArgs,
|
|
7705
7760
|
...buildRtpOutputArgs(inv.sink.video)
|
|
@@ -7707,7 +7762,7 @@ function buildFfmpegArgs(inv) {
|
|
|
7707
7762
|
const audioLeg = inv.sink.audio ? [
|
|
7708
7763
|
"-vn",
|
|
7709
7764
|
"-map",
|
|
7710
|
-
"0:a:0?",
|
|
7765
|
+
maps.audio ?? "0:a:0?",
|
|
7711
7766
|
...buildAudioArgs(inv.audio),
|
|
7712
7767
|
...buildRtpOutputArgs(inv.sink.audio)
|
|
7713
7768
|
] : [];
|
|
@@ -7717,15 +7772,17 @@ function buildFfmpegArgs(inv) {
|
|
|
7717
7772
|
...audioLeg
|
|
7718
7773
|
];
|
|
7719
7774
|
}
|
|
7775
|
+
const mutedAudio = isElementaryVideoSink(inv.sink) || inv.audio.kind === "none";
|
|
7720
7776
|
const audioArgs = isElementaryVideoSink(inv.sink) ? ["-an"] : buildAudioArgs(inv.audio);
|
|
7721
|
-
const sinkArgs = buildStdoutOrRtspSinkArgs(inv.sink);
|
|
7722
7777
|
return [
|
|
7723
7778
|
...head,
|
|
7724
|
-
...
|
|
7779
|
+
...videoMapArgs,
|
|
7780
|
+
...buildVideoArgs(inv.video, inv.outputArgs, graph),
|
|
7725
7781
|
...threadArgs,
|
|
7782
|
+
...mutedAudio ? [] : audioMapArgs,
|
|
7726
7783
|
...audioArgs,
|
|
7727
7784
|
...inv.outputArgs,
|
|
7728
|
-
...
|
|
7785
|
+
...buildStdoutOrRtspSinkArgs(inv.sink),
|
|
7729
7786
|
...inv.audioSidecar ? buildAudioSidecarArgs(inv.audioSidecar) : []
|
|
7730
7787
|
];
|
|
7731
7788
|
}
|
|
@@ -29584,14 +29641,81 @@ DeviceType.Camera, method(object({ deviceId: number() }), PrivacyMaskOptionsSche
|
|
|
29584
29641
|
kind: "mutation",
|
|
29585
29642
|
auth: "admin"
|
|
29586
29643
|
});
|
|
29644
|
+
/**
|
|
29645
|
+
* What a preset id DOES on this camera.
|
|
29646
|
+
*
|
|
29647
|
+
* On several PTZ firmwares the preset namespace is shared: low ids are stored
|
|
29648
|
+
* positions, a reserved band triggers camera FUNCTIONS. The dispensa camera
|
|
29649
|
+
* (Hikvision) returns its own reserved band in the preset list, named by the
|
|
29650
|
+
* firmware:
|
|
29651
|
+
*
|
|
29652
|
+
* 34 Back to origin · 39 Day mode · 40 Night mode · 46 Day/Night Auto Mode
|
|
29653
|
+
* 92 Set manual limits · 93 Save manual limits · 94 Remote reboot
|
|
29654
|
+
* 95 Call OSD menu
|
|
29655
|
+
*
|
|
29656
|
+
* We used to hand all of these to the UI as ordinary presets. Saving over one
|
|
29657
|
+
* is what produced `PUT /ISAPI/PTZCtrl/channels/1/presets/34 -> HTTP 500`: the
|
|
29658
|
+
* camera refusing to let "Back to origin" be overwritten, surfaced to the
|
|
29659
|
+
* operator as a transport failure.
|
|
29660
|
+
*
|
|
29661
|
+
* A function preset stays fully usable as a DESTINATION -- `goToPreset('34')`
|
|
29662
|
+
* is the single most useful thing that camera can do. Only `savePreset` and
|
|
29663
|
+
* `deletePreset` are gated.
|
|
29664
|
+
*/
|
|
29665
|
+
var PtzPresetFunctionSchema = _enum([
|
|
29666
|
+
"home",
|
|
29667
|
+
"day-mode",
|
|
29668
|
+
"night-mode",
|
|
29669
|
+
"day-night-auto",
|
|
29670
|
+
"set-limits",
|
|
29671
|
+
"save-limits",
|
|
29672
|
+
"reboot",
|
|
29673
|
+
"osd-menu"
|
|
29674
|
+
]);
|
|
29587
29675
|
var PtzPresetSchema = object({
|
|
29588
29676
|
id: string(),
|
|
29589
|
-
name: string()
|
|
29677
|
+
name: string(),
|
|
29678
|
+
/**
|
|
29679
|
+
* The firmware function this id performs, when the provider KNOWS the id is
|
|
29680
|
+
* reserved. `null` = an ordinary stored position, OR a reserved id this
|
|
29681
|
+
* provider does not recognise -- the two are told apart by `writable`.
|
|
29682
|
+
*/
|
|
29683
|
+
fn: PtzPresetFunctionSchema.nullable(),
|
|
29684
|
+
/**
|
|
29685
|
+
* Whether `savePreset` / `deletePreset` may target this id. A provider sets
|
|
29686
|
+
* it false only for a band it KNOWS is reserved; where the firmware is not
|
|
29687
|
+
* documented it stays true and a refusal comes from the camera, named.
|
|
29688
|
+
*/
|
|
29689
|
+
writable: boolean()
|
|
29590
29690
|
});
|
|
29691
|
+
/**
|
|
29692
|
+
* Where the head is pointing -- per axis, and `null` when the driver cannot
|
|
29693
|
+
* read it.
|
|
29694
|
+
*
|
|
29695
|
+
* It used to be three REQUIRED numbers, which left a provider that cannot read
|
|
29696
|
+
* position no way to say so. All four said `0`:
|
|
29697
|
+
*
|
|
29698
|
+
* reolink `return { pan: 0, tilt: 0, zoom: 0 }` "until a position-query
|
|
29699
|
+
* path lands upstream"
|
|
29700
|
+
* hikvision stub, "firmware-dependent and frequently absent"
|
|
29701
|
+
* amcrest stub, "no generic absolute-position read for this model family"
|
|
29702
|
+
* onvif throws
|
|
29703
|
+
*
|
|
29704
|
+
* So every consumer asking where a camera points was told "perfectly centred"
|
|
29705
|
+
* by four cameras that had never looked. That is D393 -- a measurement that
|
|
29706
|
+
* FAILED is `null`, never `0`, and the type says so all the way to the
|
|
29707
|
+
* decision -- the same shape as the unreadable `statfs` folded into "0 bytes of
|
|
29708
|
+
* headroom", which evacuated a healthy disk.
|
|
29709
|
+
*
|
|
29710
|
+
* D393 also says to check for a narrower structural TWIN of the result type.
|
|
29711
|
+
* There is one: `PtzStatusSchema` extends this, so the lie had already
|
|
29712
|
+
* propagated into `getStatus`, which is the surface `getPosition`'s own comment
|
|
29713
|
+
* tells callers to migrate to.
|
|
29714
|
+
*/
|
|
29591
29715
|
var PtzPositionSchema = object({
|
|
29592
|
-
pan: number(),
|
|
29593
|
-
tilt: number(),
|
|
29594
|
-
zoom: number()
|
|
29716
|
+
pan: number().nullable(),
|
|
29717
|
+
tilt: number().nullable(),
|
|
29718
|
+
zoom: number().nullable()
|
|
29595
29719
|
});
|
|
29596
29720
|
var PtzMoveCommandSchema = object({
|
|
29597
29721
|
pan: number().optional(),
|
|
@@ -29610,7 +29734,72 @@ var PtzOptionsSchema = object({
|
|
|
29610
29734
|
maxPresets: number().optional(),
|
|
29611
29735
|
/** Whether the camera exposes a controllable autofocus toggle
|
|
29612
29736
|
* (boolean `hasX` per the getOptions availability convention). */
|
|
29613
|
-
hasAutofocus: boolean()
|
|
29737
|
+
hasAutofocus: boolean(),
|
|
29738
|
+
/**
|
|
29739
|
+
* How many distinct speeds this camera's NATIVE scale offers.
|
|
29740
|
+
*
|
|
29741
|
+
* The cap's `speed` is normalized 0..1 and each provider maps it down --
|
|
29742
|
+
* Baichuan 1..63, Dahua 1..8, ISAPI's percentage -100..100. The normalized
|
|
29743
|
+
* value hides how coarse that really is: asking an amcrest for `0.37` is
|
|
29744
|
+
* meaningless, because it has eight steps and three of them round to the
|
|
29745
|
+
* same one. A loop that tunes its own gain has to know the granularity of
|
|
29746
|
+
* the knob it is turning.
|
|
29747
|
+
*
|
|
29748
|
+
* `null` = the driver does not know, or the scale is continuous (ONVIF takes
|
|
29749
|
+
* a float). Never a made-up number.
|
|
29750
|
+
*/
|
|
29751
|
+
speedSteps: number().int().positive().nullable(),
|
|
29752
|
+
/**
|
|
29753
|
+
* How long ONE `move` pulse runs on this driver, ms.
|
|
29754
|
+
*
|
|
29755
|
+
* `move` is a self-terminating burst everywhere, but every provider hardcoded
|
|
29756
|
+
* its own duration in private -- 200 reolink, 350 amcrest, 500 hikvision,
|
|
29757
|
+
* 1000 onvif -- so no caller could read it. It is the DENOMINATOR of "how far
|
|
29758
|
+
* did the head travel per pulse": without it a measured displacement has no
|
|
29759
|
+
* gain to be divided into.
|
|
29760
|
+
*
|
|
29761
|
+
* `null` = the driver cannot say.
|
|
29762
|
+
*/
|
|
29763
|
+
moveImpulseMs: number().int().positive().nullable()
|
|
29764
|
+
});
|
|
29765
|
+
/**
|
|
29766
|
+
* Which preset `goHome` goes to, and WHO decided.
|
|
29767
|
+
*
|
|
29768
|
+
* `goHome` used to be three vendor "conventions" written in three comments --
|
|
29769
|
+
* Reolink preset 0, Hikvision preset '1', Dahua preset 1 -- and on this fleet
|
|
29770
|
+
* not one of the three cameras honours its own:
|
|
29771
|
+
*
|
|
29772
|
+
* 592 Videocamera camera Daniel (reolink) preset 0 holds "stanza"
|
|
29773
|
+
* 1438 Videocamera dispensa (hikvision) preset 1 holds "Credenza"
|
|
29774
|
+
* 3836 Videocamera studio (amcrest) preset 1 holds "Preset1"
|
|
29775
|
+
*
|
|
29776
|
+
* So `goHome` meant "go wherever the operator happened to save in slot 0 or 1",
|
|
29777
|
+
* and two of the three swallowed the failure in a bare `catch`. For autotrack
|
|
29778
|
+
* this is the HOTTEST path -- it runs every time a subject is released -- so it
|
|
29779
|
+
* needs an answer that is true per camera and audible when it is missing.
|
|
29780
|
+
*
|
|
29781
|
+
* `source` is what makes it honest:
|
|
29782
|
+
* - `operator` -- picked in the UI, stored by the provider.
|
|
29783
|
+
* - `firmware` -- the camera itself named a preset `fn: 'home'` (Hikvision's
|
|
29784
|
+
* "Back to origin"), adopted as the default with nothing to configure.
|
|
29785
|
+
* - `native` -- the driver homes WITHOUT a preset at all. ONVIF does:
|
|
29786
|
+
* `goHome` is `ptzAbsoluteMove({x:0, y:0, zoom:0})`. `presetId` is null and
|
|
29787
|
+
* that is not a failure -- there is nothing to pick, and the UI must offer
|
|
29788
|
+
* no picker.
|
|
29789
|
+
* - `none` -- nothing is configured and the camera names nothing.
|
|
29790
|
+
* `goHome` REFUSES rather than moving the head somewhere arbitrary.
|
|
29791
|
+
*
|
|
29792
|
+
* `source === 'none'` is the refusal condition, NOT `presetId === null`: the
|
|
29793
|
+
* native case has no preset and homes perfectly well.
|
|
29794
|
+
*/
|
|
29795
|
+
var PtzHomePresetSchema = object({
|
|
29796
|
+
presetId: string().nullable(),
|
|
29797
|
+
source: _enum([
|
|
29798
|
+
"operator",
|
|
29799
|
+
"firmware",
|
|
29800
|
+
"native",
|
|
29801
|
+
"none"
|
|
29802
|
+
])
|
|
29614
29803
|
});
|
|
29615
29804
|
DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _void(), { kind: "mutation" }), method(PtzMoveCommandSchema.extend({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), array(PtzPresetSchema)), method(object({
|
|
29616
29805
|
deviceId: number(),
|
|
@@ -29628,7 +29817,13 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
|
|
|
29628
29817
|
}), _void(), {
|
|
29629
29818
|
kind: "mutation",
|
|
29630
29819
|
auth: "admin"
|
|
29631
|
-
}), method(object({ deviceId: number() }), PtzOptionsSchema), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }),
|
|
29820
|
+
}), method(object({ deviceId: number() }), PtzOptionsSchema), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), PtzHomePresetSchema), method(object({
|
|
29821
|
+
deviceId: number(),
|
|
29822
|
+
presetId: string().nullable()
|
|
29823
|
+
}), _void(), {
|
|
29824
|
+
kind: "mutation",
|
|
29825
|
+
auth: "admin"
|
|
29826
|
+
}), method(object({ deviceId: number() }), PtzPositionSchema), method(object({
|
|
29632
29827
|
deviceId: number(),
|
|
29633
29828
|
enabled: boolean()
|
|
29634
29829
|
}), _void(), { kind: "mutation" });
|
|
@@ -36668,6 +36863,12 @@ Object.freeze({
|
|
|
36668
36863
|
addonId: null,
|
|
36669
36864
|
access: "delete"
|
|
36670
36865
|
},
|
|
36866
|
+
"ptz.getHomePreset": {
|
|
36867
|
+
capName: "ptz",
|
|
36868
|
+
capScope: "device",
|
|
36869
|
+
addonId: null,
|
|
36870
|
+
access: "view"
|
|
36871
|
+
},
|
|
36671
36872
|
"ptz.getOptions": {
|
|
36672
36873
|
capName: "ptz",
|
|
36673
36874
|
capScope: "device",
|
|
@@ -36716,6 +36917,12 @@ Object.freeze({
|
|
|
36716
36917
|
addonId: null,
|
|
36717
36918
|
access: "create"
|
|
36718
36919
|
},
|
|
36920
|
+
"ptz.setHomePreset": {
|
|
36921
|
+
capName: "ptz",
|
|
36922
|
+
capScope: "device",
|
|
36923
|
+
addonId: null,
|
|
36924
|
+
access: "create"
|
|
36925
|
+
},
|
|
36719
36926
|
"ptz.stop": {
|
|
36720
36927
|
capName: "ptz",
|
|
36721
36928
|
capScope: "device",
|
|
@@ -39889,6 +40096,11 @@ Object.freeze({
|
|
|
39889
40096
|
form: "single",
|
|
39890
40097
|
optional: false
|
|
39891
40098
|
}],
|
|
40099
|
+
"ptz.getHomePreset": [{
|
|
40100
|
+
name: "deviceId",
|
|
40101
|
+
form: "single",
|
|
40102
|
+
optional: false
|
|
40103
|
+
}],
|
|
39892
40104
|
"ptz.getOptions": [{
|
|
39893
40105
|
name: "deviceId",
|
|
39894
40106
|
form: "single",
|
|
@@ -39929,6 +40141,11 @@ Object.freeze({
|
|
|
39929
40141
|
form: "single",
|
|
39930
40142
|
optional: false
|
|
39931
40143
|
}],
|
|
40144
|
+
"ptz.setHomePreset": [{
|
|
40145
|
+
name: "deviceId",
|
|
40146
|
+
form: "single",
|
|
40147
|
+
optional: false
|
|
40148
|
+
}],
|
|
39932
40149
|
"ptz.stop": [{
|
|
39933
40150
|
name: "deviceId",
|
|
39934
40151
|
form: "single",
|
|
@@ -101743,10 +101960,19 @@ async function readPresets(bctx) {
|
|
|
101743
101960
|
try {
|
|
101744
101961
|
const raw = await proxy.ptz?.getPresets({});
|
|
101745
101962
|
if (!Array.isArray(raw)) return [];
|
|
101746
|
-
|
|
101747
|
-
|
|
101748
|
-
|
|
101749
|
-
|
|
101963
|
+
const entries = [];
|
|
101964
|
+
for (const p of raw) {
|
|
101965
|
+
if (typeof p !== "object" || p === null) continue;
|
|
101966
|
+
const candidate = p;
|
|
101967
|
+
if (typeof candidate.id !== "string" || typeof candidate.name !== "string") continue;
|
|
101968
|
+
const fn = candidate.fn;
|
|
101969
|
+
if (fn !== null && fn !== void 0 && fn !== "home") continue;
|
|
101970
|
+
entries.push({
|
|
101971
|
+
id: candidate.id,
|
|
101972
|
+
name: candidate.name
|
|
101973
|
+
});
|
|
101974
|
+
}
|
|
101975
|
+
return entries;
|
|
101750
101976
|
} catch (err) {
|
|
101751
101977
|
ctx.logger.withTags({ deviceId: numericDeviceId }).debug("export-hap: ptz.getPresets failed (non-fatal)", { meta: { error: errMsg$8(err) } });
|
|
101752
101978
|
return [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-export-hap",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.123",
|
|
4
4
|
"description": "HomeKit (HAP) exporter for CamStack devices. Publishes each exposed device as its own HomeKit accessory: cameras and doorbells with SRTP streaming, HomeKit Secure Video, motion, two-way audio, PTZ and battery; switches, lights, locks and sensors through a capability→service table.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|