@camstack/types 1.2.188 → 1.2.189
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 +2 -2
- package/dist/addon.mjs +2 -2
- package/dist/camera/stream-pick.d.ts +71 -0
- package/dist/capabilities/camera-streams.cap.d.ts +10 -0
- package/dist/capabilities/index.d.ts +4 -2
- package/dist/capabilities/notification-rules.cap.d.ts +48 -0
- package/dist/capabilities/osd-manager.cap.d.ts +18 -0
- package/dist/capabilities/stream-broker.cap.d.ts +37 -0
- package/dist/capabilities/stream-signals.cap.d.ts +80 -0
- package/dist/capabilities/webrtc-session.cap.d.ts +1 -0
- package/dist/enums/event-category.d.ts +14 -0
- package/dist/enums.js +1 -1
- package/dist/enums.mjs +1 -1
- package/dist/{event-category-ZyX6jcse.mjs → event-category-BVDXG4tB.mjs} +14 -0
- package/dist/{event-category-yW4s1MGD.js → event-category-BVfsrBYA.js} +14 -0
- package/dist/generated/addon-api.d.ts +22 -0
- package/dist/generated/capability-router-map.d.ts +5 -2
- package/dist/generated/device-local-state.d.ts +3 -0
- package/dist/generated/device-proxy.d.ts +4 -1
- package/dist/generated/device-scoped-caps.d.ts +1 -1
- package/dist/generated/method-access-map.d.ts +1 -1
- package/dist/generated/method-device-selectors.d.ts +2 -2
- package/dist/generated/runtime-state-durability.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +365 -30
- package/dist/index.mjs +360 -31
- package/dist/{sleep-DXsYD0RX.js → sleep-D7dV-FZB.js} +6 -2
- package/dist/{sleep-DBKu2-U5.mjs → sleep-DEuj7E3j.mjs} +6 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_sleep = require("./sleep-
|
|
3
|
-
const require_event_category = require("./event-category-
|
|
2
|
+
const require_sleep = require("./sleep-D7dV-FZB.js");
|
|
3
|
+
const require_event_category = require("./event-category-BVfsrBYA.js");
|
|
4
4
|
const require_canonical_hash = require("./canonical-hash-9lydiEzx.js");
|
|
5
5
|
const require_enums = require("./enums.js");
|
|
6
6
|
const require_err_msg = require("./err-msg-COpsHMw2.js");
|
|
@@ -394,6 +394,84 @@ function encodeProfileFromStreamShape(stream) {
|
|
|
394
394
|
};
|
|
395
395
|
}
|
|
396
396
|
//#endregion
|
|
397
|
+
//#region src/camera/stream-pick.ts
|
|
398
|
+
/**
|
|
399
|
+
* THE target rule: which of two sizes serves a consumer better.
|
|
400
|
+
*
|
|
401
|
+
* "Prefer at or above the target so the consumer downscales instead of
|
|
402
|
+
* upscaling; among those the SMALLEST, because the extra pixels are thrown
|
|
403
|
+
* away and the decode is not; among the rest the LARGEST, because it is the
|
|
404
|
+
* closest we can get." One sentence, and until now it was written twice — once
|
|
405
|
+
* here in lines, once in `stream-selection.ts` in pixels, for the same two
|
|
406
|
+
* exporters. The two agreed, which is luck, not a property.
|
|
407
|
+
*
|
|
408
|
+
* The `size` is whatever measure the caller ranks by: the cap ranks by LINES
|
|
409
|
+
* (a consumer's requirement is stated in p), the RTSP picker by PIXELS (it
|
|
410
|
+
* compares entries of possibly different aspect). The rule is the same either
|
|
411
|
+
* way and the measure is the caller's business.
|
|
412
|
+
*
|
|
413
|
+
* Returns a sort delta: negative when `a` should come first.
|
|
414
|
+
*/
|
|
415
|
+
function compareToTarget(a, b, target) {
|
|
416
|
+
const aReaches = a >= target;
|
|
417
|
+
if (aReaches !== b >= target) return aReaches ? -1 : 1;
|
|
418
|
+
return aReaches ? a - b : b - a;
|
|
419
|
+
}
|
|
420
|
+
/** `h265` and `hevc` are one codec spelled two ways, and a camera publishes
|
|
421
|
+
* whichever its vendor felt like. */
|
|
422
|
+
function canonicalCodec(value) {
|
|
423
|
+
const v = value.toLowerCase().replace(/[.\s-]/g, "");
|
|
424
|
+
if (v === "hevc" || v === "hvc1" || v === "hev1") return "h265";
|
|
425
|
+
if (v === "avc" || v === "avc1") return "h264";
|
|
426
|
+
return v;
|
|
427
|
+
}
|
|
428
|
+
function codecIsAccepted(codec, accept) {
|
|
429
|
+
if (accept.length === 0) return true;
|
|
430
|
+
if (codec === void 0) return false;
|
|
431
|
+
const c = canonicalCodec(codec);
|
|
432
|
+
return accept.some((a) => canonicalCodec(a) === c);
|
|
433
|
+
}
|
|
434
|
+
function providerRank(camStreamId, preferred) {
|
|
435
|
+
for (let i = 0; i < preferred.length; i += 1) if (camStreamId.startsWith(preferred[i])) return i;
|
|
436
|
+
return preferred.length;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Order the candidates and return the winner, or `null` when none qualifies.
|
|
440
|
+
*
|
|
441
|
+
* The order, and every term is there for a measured reason:
|
|
442
|
+
*
|
|
443
|
+
* 1. **A stream we can take AS IS beats one we must re-encode.** A transcode
|
|
444
|
+
* is a decode plus an encode for as long as the session lasts, and on this
|
|
445
|
+
* hub a concurrent HEVC decode is what exhausted the VAAPI device.
|
|
446
|
+
* 2. **Provider preference**, unchanged — a native dial beats a relayed one.
|
|
447
|
+
* 3. **The target**, when the consumer named one: the smallest stream that
|
|
448
|
+
* reaches it, else the tallest that does not. Absent, the caller's
|
|
449
|
+
* `resolutionPreference` decides, exactly as before.
|
|
450
|
+
*/
|
|
451
|
+
function rankStreams(candidates, ranking) {
|
|
452
|
+
const usable = candidates.filter((c) => ranking.allowTranscode || codecIsAccepted(c.codec, ranking.acceptCodecs));
|
|
453
|
+
if (usable.length === 0) return null;
|
|
454
|
+
const target = ranking.targetHeight;
|
|
455
|
+
const scored = usable.map((candidate) => ({
|
|
456
|
+
candidate,
|
|
457
|
+
transcodes: !codecIsAccepted(candidate.codec, ranking.acceptCodecs),
|
|
458
|
+
provider: providerRank(candidate.camStreamId, ranking.preferredProviders),
|
|
459
|
+
height: candidate.height ?? 0
|
|
460
|
+
}));
|
|
461
|
+
scored.sort((a, b) => {
|
|
462
|
+
if (a.transcodes !== b.transcodes) return a.transcodes ? 1 : -1;
|
|
463
|
+
if (a.provider !== b.provider) return a.provider - b.provider;
|
|
464
|
+
if (target !== void 0) return compareToTarget(a.height, b.height, target);
|
|
465
|
+
return ranking.resolutionPreference === "highest" ? b.height - a.height : a.height - b.height;
|
|
466
|
+
});
|
|
467
|
+
const winner = scored[0];
|
|
468
|
+
if (winner === void 0) return null;
|
|
469
|
+
return {
|
|
470
|
+
candidate: winner.candidate,
|
|
471
|
+
transcodes: winner.transcodes
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
//#endregion
|
|
397
475
|
//#region src/ffmpeg/encode-defaults.ts
|
|
398
476
|
/**
|
|
399
477
|
* The shape every live egress starts from: H.264 Baseline 3.1 at 720p25.
|
|
@@ -4519,6 +4597,47 @@ function reducePoints(points, bucketMs, origin) {
|
|
|
4519
4597
|
}
|
|
4520
4598
|
//#endregion
|
|
4521
4599
|
//#region src/stream-selection.ts
|
|
4600
|
+
/**
|
|
4601
|
+
* Shared stream-selection picker for export addons.
|
|
4602
|
+
*
|
|
4603
|
+
* Both `addon-export-alexa` and `addon-export-hap` resolve the
|
|
4604
|
+
* operator's `streamPreference` setting against the broker's RTSP
|
|
4605
|
+
* entries before dialling a camera. Each addon used to roll its own:
|
|
4606
|
+
* Alexa had `pickPreferredRtspEntry` under `handlers/`, HAP had an
|
|
4607
|
+
* inline `"prefer /mid, then /high, then first"` chain inside its
|
|
4608
|
+
* camera-streams mapper. The chains drifted (Alexa honoured
|
|
4609
|
+
* `streamPreference`, HAP didn't; only Alexa got the smart
|
|
4610
|
+
* resolution-targeted auto-pick) and the cross-addon import rule
|
|
4611
|
+
* blocks deduping in either direction.
|
|
4612
|
+
*
|
|
4613
|
+
* Lifting the picker into `@camstack/types` lets every exporter call
|
|
4614
|
+
* the same code path with the consumer's target resolution baked into
|
|
4615
|
+
* the call site — Alexa passes 1280×720, HomeKit passes 1920×1080,
|
|
4616
|
+
* the picker handles the rest.
|
|
4617
|
+
*
|
|
4618
|
+
* Selection algorithm:
|
|
4619
|
+
* - pref === '<profile>' → profile-keyed entries (from
|
|
4620
|
+
* `getProfileRtspEntries`) match on `e.profile`;
|
|
4621
|
+
* legacy raw entries match on the brokerId
|
|
4622
|
+
* suffix (`brokerId` ENDS WITH `/${pref}`, shape
|
|
4623
|
+
* `${deviceId}/${camStreamId}`). Addons store the
|
|
4624
|
+
* bare profile/camStream id in their
|
|
4625
|
+
* `ExportDeviceSettings.streamPreference`. Falls
|
|
4626
|
+
* back to 'auto' when no enabled entry matches
|
|
4627
|
+
* either way.
|
|
4628
|
+
* - pref === 'auto' → when `targetResolution` is given, pick the
|
|
4629
|
+
* entry whose source resolution is closest to
|
|
4630
|
+
* it (prefer ≥ target so the consumer
|
|
4631
|
+
* downscales instead of upscaling). Without a
|
|
4632
|
+
* target, fall back to first-enabled. If no
|
|
4633
|
+
* entry advertises a resolution at all
|
|
4634
|
+
* (broker hasn't probed yet), also fall back
|
|
4635
|
+
* to first-enabled so cold start still serves.
|
|
4636
|
+
*
|
|
4637
|
+
* Returns `null` when nothing is publishable — callers fold that into
|
|
4638
|
+
* the consumer's "no stream" error (Alexa: STREAM_TYPE_UNSUPPORTED;
|
|
4639
|
+
* HAP: a thrown error inside `startFfmpegForSession`).
|
|
4640
|
+
*/
|
|
4522
4641
|
function pickPreferredRtspEntry(entries, pref, deviceId, options = {}) {
|
|
4523
4642
|
if (entries.length === 0) return null;
|
|
4524
4643
|
const prefix = `${deviceId}/`;
|
|
@@ -4548,30 +4667,23 @@ function pickPreferredRtspEntry(entries, pref, deviceId, options = {}) {
|
|
|
4548
4667
|
return toPicked(eligible[0]);
|
|
4549
4668
|
}
|
|
4550
4669
|
/**
|
|
4551
|
-
* Pick the entry whose `resolution` is closest to `target
|
|
4552
|
-
*
|
|
4553
|
-
*
|
|
4554
|
-
*
|
|
4555
|
-
*
|
|
4670
|
+
* Pick the entry whose `resolution` is closest to `target`, by the ONE target
|
|
4671
|
+
* rule ({@link compareToTarget}): prefer at or above the target so the consumer
|
|
4672
|
+
* downscales instead of upscaling; among those the smallest; among the rest the
|
|
4673
|
+
* largest.
|
|
4674
|
+
*
|
|
4675
|
+
* This function used to implement that rule itself, in pixels, while
|
|
4676
|
+
* `rankStreams` implemented it again in lines — the same sentence written twice
|
|
4677
|
+
* for the same two exporters, agreeing by luck rather than by construction.
|
|
4678
|
+
* Only the MEASURE is this picker's own: it compares whole pixel counts because
|
|
4679
|
+
* its entries can differ in aspect, where the cap's requirement is stated in p.
|
|
4556
4680
|
*/
|
|
4557
4681
|
function pickClosestResolution(entries, target) {
|
|
4558
4682
|
const targetPixels = target.width * target.height;
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
const pixels = e.resolution.width * e.resolution.height;
|
|
4564
|
-
if (pixels >= targetPixels) {
|
|
4565
|
-
if (!bestAbove || pixels < bestAbove.pixels) bestAbove = {
|
|
4566
|
-
entry: e,
|
|
4567
|
-
pixels
|
|
4568
|
-
};
|
|
4569
|
-
} else if (!bestBelow || pixels > bestBelow.pixels) bestBelow = {
|
|
4570
|
-
entry: e,
|
|
4571
|
-
pixels
|
|
4572
|
-
};
|
|
4573
|
-
}
|
|
4574
|
-
return bestAbove?.entry ?? bestBelow?.entry;
|
|
4683
|
+
return [...entries.flatMap((entry) => entry.resolution === void 0 ? [] : [{
|
|
4684
|
+
entry,
|
|
4685
|
+
pixels: entry.resolution.width * entry.resolution.height
|
|
4686
|
+
}])].sort((a, b) => compareToTarget(a.pixels, b.pixels, targetPixels))[0]?.entry;
|
|
4575
4687
|
}
|
|
4576
4688
|
//#endregion
|
|
4577
4689
|
//#region src/types/model-variant-groups.ts
|
|
@@ -8199,6 +8311,74 @@ var cameraPipelineConfigCapability = {
|
|
|
8199
8311
|
methods: {}
|
|
8200
8312
|
};
|
|
8201
8313
|
//#endregion
|
|
8314
|
+
//#region src/capabilities/stream-signals.cap.ts
|
|
8315
|
+
/**
|
|
8316
|
+
* The signals a device can emit to WAKE its own stream.
|
|
8317
|
+
*
|
|
8318
|
+
* A camera whose stream is built on demand sleeps until something asks for it,
|
|
8319
|
+
* and "something" cannot be a consumer that is merely attached — a Frigate-style
|
|
8320
|
+
* puller holds a session open for ever, and treating that as demand would keep
|
|
8321
|
+
* a battery camera awake for ever, which is the whole thing the battery is for
|
|
8322
|
+
* (D173). So the wake has to come from the CAMERA: an event it noticed by
|
|
8323
|
+
* itself, with no stream running.
|
|
8324
|
+
*
|
|
8325
|
+
* ## The vocabulary is the PROVIDER'S, not ours
|
|
8326
|
+
*
|
|
8327
|
+
* Like `consumables`, this cap declares no vocabulary of its own. A provider
|
|
8328
|
+
* names each signal with a `code` it chooses and a `label` an operator reads.
|
|
8329
|
+
* Reolink offers motion and camera-native detection; another provider may offer
|
|
8330
|
+
* a tamper, a doorbell press, a PIR, or something no camera in this fleet has
|
|
8331
|
+
* yet. A fixed enum here would mean every new signal is a framework release.
|
|
8332
|
+
*
|
|
8333
|
+
* It is deliberately NOT derived from the caps a device already binds. Whether
|
|
8334
|
+
* a camera CAN push firmware motion is expressed by `motionSources` containing
|
|
8335
|
+
* `'onboard'`, and whether it does AI on-camera by the `native-object-detection`
|
|
8336
|
+
* binding — but both answer "what drives the detection pipeline", which is a
|
|
8337
|
+
* different question from "what may wake a sleeping stream". A camera can do
|
|
8338
|
+
* the first and not be trusted with the second, and the operator picks per
|
|
8339
|
+
* camera. Two questions, two authorities.
|
|
8340
|
+
*
|
|
8341
|
+
* ## Availability is not permission
|
|
8342
|
+
*
|
|
8343
|
+
* `listSignals` says what the device CAN emit. Whether a given signal actually
|
|
8344
|
+
* wakes the stream is the operator's per-camera choice, held by the broker
|
|
8345
|
+
* alongside the cooldown — see the stream-broker cap's wake settings. A
|
|
8346
|
+
* provider declaring a signal is not a provider enabling it.
|
|
8347
|
+
*/
|
|
8348
|
+
/** One signal a device can emit. */
|
|
8349
|
+
var StreamSignalSchema = zod.z.object({
|
|
8350
|
+
/** Stable id chosen by the provider, e.g. `'motion'`, `'person'`, `'tamper'`. */
|
|
8351
|
+
code: zod.z.string().min(1),
|
|
8352
|
+
/** What an operator reads in the picker. The provider's own wording. */
|
|
8353
|
+
label: zod.z.string().min(1),
|
|
8354
|
+
/**
|
|
8355
|
+
* Whether the provider recommends this signal ON when a camera is first set
|
|
8356
|
+
* up. A provider knows which of its signals are cheap and reliable; an
|
|
8357
|
+
* operator should not have to discover that by trial. Reolink recommends
|
|
8358
|
+
* both of its own.
|
|
8359
|
+
*/
|
|
8360
|
+
recommended: zod.z.boolean()
|
|
8361
|
+
});
|
|
8362
|
+
var StreamSignalsStatusSchema = zod.z.object({
|
|
8363
|
+
signals: zod.z.array(StreamSignalSchema),
|
|
8364
|
+
lastFetchedAt: zod.z.number()
|
|
8365
|
+
});
|
|
8366
|
+
var streamSignalsCapability = {
|
|
8367
|
+
name: "stream-signals",
|
|
8368
|
+
scope: "device",
|
|
8369
|
+
deviceNative: true,
|
|
8370
|
+
mode: "singleton",
|
|
8371
|
+
deviceTypes: Object.values(require_sleep.DeviceType),
|
|
8372
|
+
runtimeState: StreamSignalsStatusSchema,
|
|
8373
|
+
methods: {
|
|
8374
|
+
/**
|
|
8375
|
+
* What this device can emit. Empty is a valid and common answer — most
|
|
8376
|
+
* cameras have nothing to offer here, and an empty list is what makes the
|
|
8377
|
+
* broker's picker show nothing rather than a false choice.
|
|
8378
|
+
*/
|
|
8379
|
+
listSignals: require_sleep.method(zod.z.void(), zod.z.array(StreamSignalSchema).readonly()) }
|
|
8380
|
+
};
|
|
8381
|
+
//#endregion
|
|
8202
8382
|
//#region src/capabilities/stream-broker.cap.ts
|
|
8203
8383
|
/** Stream delivery format. (Relocated from the retired `streaming-engine` cap.) */
|
|
8204
8384
|
var StreamFormatSchema = zod.z.enum([
|
|
@@ -8609,6 +8789,46 @@ var streamBrokerCapability = {
|
|
|
8609
8789
|
mode: "singleton",
|
|
8610
8790
|
exposesDeviceSettings: true,
|
|
8611
8791
|
methods: {
|
|
8792
|
+
/**
|
|
8793
|
+
* A device reporting that one of ITS OWN signals just fired.
|
|
8794
|
+
*
|
|
8795
|
+
* The provider calls this; nothing polls. The broker then decides whether
|
|
8796
|
+
* to wake the source, and the decision has two terms that must BOTH hold:
|
|
8797
|
+
*
|
|
8798
|
+
* - the signal is ENABLED for this camera (the operator's per-camera
|
|
8799
|
+
* choice — a camera may declare a signal the operator does not want
|
|
8800
|
+
* waking anything), and
|
|
8801
|
+
* - a consumer is actually attached and waiting on the prestream. Waking
|
|
8802
|
+
* a camera nobody is watching is the one thing a battery camera must
|
|
8803
|
+
* never do, and a signal arriving with no consumer is exactly that.
|
|
8804
|
+
*
|
|
8805
|
+
* Reported, never inferred: a signal that fires while the stream is
|
|
8806
|
+
* already up refreshes the hold instead of starting a second one, which is
|
|
8807
|
+
* what makes continuous motion keep a camera awake without stacking
|
|
8808
|
+
* timers.
|
|
8809
|
+
*
|
|
8810
|
+
* `code` is the provider's own, from `streamSignals.listSignals`. An
|
|
8811
|
+
* unknown code is REFUSED rather than treated as a wildcard wake — a
|
|
8812
|
+
* provider and an operator disagreeing about a name must not become a
|
|
8813
|
+
* camera that wakes for everything.
|
|
8814
|
+
*/
|
|
8815
|
+
reportStreamSignal: require_sleep.method(zod.z.object({
|
|
8816
|
+
deviceId: zod.z.number().int().nonnegative(),
|
|
8817
|
+
/** The provider's signal code. */
|
|
8818
|
+
code: zod.z.string().min(1),
|
|
8819
|
+
/** Ms epoch. Absent ⇒ now. */
|
|
8820
|
+
at: zod.z.number().optional()
|
|
8821
|
+
}), zod.z.object({
|
|
8822
|
+
/** Whether the broker acted on it, and if not, why. */
|
|
8823
|
+
accepted: zod.z.boolean(),
|
|
8824
|
+
reason: zod.z.enum([
|
|
8825
|
+
"woke",
|
|
8826
|
+
"hold-extended",
|
|
8827
|
+
"not-enabled",
|
|
8828
|
+
"no-consumer",
|
|
8829
|
+
"unknown-code"
|
|
8830
|
+
])
|
|
8831
|
+
}), { kind: "mutation" }),
|
|
8612
8832
|
publishCameraStream: require_sleep.method(zod.z.object({
|
|
8613
8833
|
deviceId: zod.z.number().int().nonnegative(),
|
|
8614
8834
|
camStreamId: zod.z.string().min(1),
|
|
@@ -9086,6 +9306,16 @@ var PickStreamRequirementsSchema = zod.z.object({
|
|
|
9086
9306
|
acceptCodecs: zod.z.array(StreamCodecSchema).readonly().optional(),
|
|
9087
9307
|
/** Minimum vertical resolution. Streams shorter than this are dropped. */
|
|
9088
9308
|
minHeight: zod.z.number().int().positive().optional(),
|
|
9309
|
+
/**
|
|
9310
|
+
* Maximum vertical resolution. Streams TALLER than this are dropped.
|
|
9311
|
+
*
|
|
9312
|
+
* A consumer can have a ceiling as real as its floor: Alexa documents
|
|
9313
|
+
* 480p to 1080p, and a 4K stream is as unusable to an Echo as a 360p one.
|
|
9314
|
+
* Without this the ceiling had to be re-implemented by every caller — and
|
|
9315
|
+
* one caller implementing it privately is how a second scoring authority
|
|
9316
|
+
* gets born.
|
|
9317
|
+
*/
|
|
9318
|
+
maxHeight: zod.z.number().int().positive().optional(),
|
|
9089
9319
|
/** Minimum horizontal resolution. */
|
|
9090
9320
|
minWidth: zod.z.number().int().positive().optional(),
|
|
9091
9321
|
/**
|
|
@@ -9102,7 +9332,22 @@ var PickStreamRequirementsSchema = zod.z.object({
|
|
|
9102
9332
|
* transcoded" guard: if the device is already serving the consumer's
|
|
9103
9333
|
* codec end-to-end, there's nothing to optimise.
|
|
9104
9334
|
*/
|
|
9105
|
-
requireSiblingCodec: zod.z.array(StreamCodecSchema).readonly().optional()
|
|
9335
|
+
requireSiblingCodec: zod.z.array(StreamCodecSchema).readonly().optional(),
|
|
9336
|
+
/**
|
|
9337
|
+
* Whether a stream the consumer CANNOT decode may still be picked, on the
|
|
9338
|
+
* understanding that it will be transcoded.
|
|
9339
|
+
*
|
|
9340
|
+
* Default `false` — today's behaviour, and the right one for a bypass
|
|
9341
|
+
* question ("is there a stream I can forward untouched?"). Set `true` to
|
|
9342
|
+
* ask the larger question: "what is the best stream for me, transcoding if
|
|
9343
|
+
* I must?" A stream that satisfies `acceptCodecs` always outranks one that
|
|
9344
|
+
* does not, so a passthrough is never lost to a transcode; the answer says
|
|
9345
|
+
* which it is in {@link PickedCamStreamSchema.transcodes}.
|
|
9346
|
+
*
|
|
9347
|
+
* This is what lets one picker serve both the bypass and the full source
|
|
9348
|
+
* choice, instead of a consumer scoring privately when the bypass misses.
|
|
9349
|
+
*/
|
|
9350
|
+
allowTranscode: zod.z.boolean().optional()
|
|
9106
9351
|
}).readonly();
|
|
9107
9352
|
var PickStreamPreferencesSchema = zod.z.object({
|
|
9108
9353
|
/**
|
|
@@ -9116,12 +9361,32 @@ var PickStreamPreferencesSchema = zod.z.object({
|
|
|
9116
9361
|
* picks the tallest stream; `'lowest'` picks the shortest (used by
|
|
9117
9362
|
* memory-constrained consumers / Apple Home guest sessions).
|
|
9118
9363
|
*/
|
|
9119
|
-
resolutionPreference: zod.z.enum(["highest", "lowest"]).optional()
|
|
9364
|
+
resolutionPreference: zod.z.enum(["highest", "lowest"]).optional(),
|
|
9365
|
+
/**
|
|
9366
|
+
* The height the consumer actually wants to DELIVER.
|
|
9367
|
+
*
|
|
9368
|
+
* Not a constraint — an ordering. With it set, the SMALLEST stream at or
|
|
9369
|
+
* above the target wins, and only if nothing reaches it does the tallest
|
|
9370
|
+
* take over. Pulling 1296 lines to draw 720 on a 1280x800 Echo panel costs
|
|
9371
|
+
* a decode and buys nothing, and `resolutionPreference: 'highest'` cannot
|
|
9372
|
+
* express that: it says "as big as possible", which is a different wish.
|
|
9373
|
+
*
|
|
9374
|
+
* Ignored when absent, so every existing caller keeps its ordering.
|
|
9375
|
+
*/
|
|
9376
|
+
targetHeight: zod.z.number().int().positive().optional()
|
|
9120
9377
|
}).readonly();
|
|
9121
9378
|
var PickedCamStreamSchema = zod.z.object({
|
|
9122
9379
|
camStreamId: zod.z.string(),
|
|
9123
9380
|
codec: zod.z.string().optional(),
|
|
9124
9381
|
resolution: require_sleep.CamStreamResolutionSchema.optional(),
|
|
9382
|
+
/**
|
|
9383
|
+
* Whether serving this stream requires a decode + re-encode.
|
|
9384
|
+
*
|
|
9385
|
+
* `false` is a stream the consumer can take as it stands. Only ever `true`
|
|
9386
|
+
* when the caller asked for it with `allowTranscode`, so a caller that did
|
|
9387
|
+
* not ask cannot be handed a cost it never agreed to pay.
|
|
9388
|
+
*/
|
|
9389
|
+
transcodes: zod.z.boolean(),
|
|
9125
9390
|
/** One-line explanation of why this stream won — for logs / debug UI. */
|
|
9126
9391
|
reason: zod.z.string()
|
|
9127
9392
|
});
|
|
@@ -15917,6 +16182,8 @@ var NcSystemEventKindSchema = zod.z.enum([
|
|
|
15917
16182
|
"device-enabled",
|
|
15918
16183
|
"device-battery-low",
|
|
15919
16184
|
"device-battery-normal",
|
|
16185
|
+
"device-consumable-low",
|
|
16186
|
+
"device-consumable-normal",
|
|
15920
16187
|
"stream-online",
|
|
15921
16188
|
"stream-offline",
|
|
15922
16189
|
"node-online",
|
|
@@ -15935,6 +16202,7 @@ var NcSystemEventKindSchema = zod.z.enum([
|
|
|
15935
16202
|
"addon-updated",
|
|
15936
16203
|
"server-updated",
|
|
15937
16204
|
"export-completed",
|
|
16205
|
+
"backup-completed",
|
|
15938
16206
|
"camera-online",
|
|
15939
16207
|
"camera-offline",
|
|
15940
16208
|
"camera-disabled",
|
|
@@ -17117,10 +17385,22 @@ var NC_CONDITION_CATALOG = [
|
|
|
17117
17385
|
value: "server-updated",
|
|
17118
17386
|
label: "Server updated"
|
|
17119
17387
|
},
|
|
17388
|
+
{
|
|
17389
|
+
value: "device-consumable-low",
|
|
17390
|
+
label: "Consumable low"
|
|
17391
|
+
},
|
|
17392
|
+
{
|
|
17393
|
+
value: "device-consumable-normal",
|
|
17394
|
+
label: "Consumable replaced"
|
|
17395
|
+
},
|
|
17120
17396
|
{
|
|
17121
17397
|
value: "export-completed",
|
|
17122
17398
|
label: "Export completed"
|
|
17123
17399
|
},
|
|
17400
|
+
{
|
|
17401
|
+
value: "backup-completed",
|
|
17402
|
+
label: "Backup completed"
|
|
17403
|
+
},
|
|
17124
17404
|
{
|
|
17125
17405
|
value: "alarm-arming",
|
|
17126
17406
|
label: "Alarm arming (exit delay)"
|
|
@@ -26257,10 +26537,28 @@ var webrtcSessionCapability = {
|
|
|
26257
26537
|
getSessionState: require_sleep.method(zod.z.object({
|
|
26258
26538
|
deviceId: zod.z.number().int().nonnegative(),
|
|
26259
26539
|
sessionId: zod.z.string()
|
|
26260
|
-
}), zod.z.object({
|
|
26261
|
-
|
|
26262
|
-
|
|
26263
|
-
|
|
26540
|
+
}), zod.z.object({
|
|
26541
|
+
pendingRenegotiation: zod.z.object({
|
|
26542
|
+
target: WebrtcStreamTargetSchema,
|
|
26543
|
+
epoch: zod.z.number()
|
|
26544
|
+
}).nullable(),
|
|
26545
|
+
/**
|
|
26546
|
+
* Whether the session still EXISTS.
|
|
26547
|
+
*
|
|
26548
|
+
* A consumer that holds a resource for the life of a session needs to
|
|
26549
|
+
* be able to ask, because a session does not always end the way it
|
|
26550
|
+
* began. Measured on this hub 2026-09-13: an Echo that got stuck never
|
|
26551
|
+
* sent `SessionDisconnected`, so the Alexa exporter's
|
|
26552
|
+
* `releaseEgressTranscode` never ran, and a 2304x1296 HEVC to 720p
|
|
26553
|
+
* H.264 transcode kept running for NOBODY for more than twenty
|
|
26554
|
+
* minutes. The WebRTC session had logged `WebRTC session closed`
|
|
26555
|
+
* minutes earlier — the broker knew; the holder had no way to ask.
|
|
26556
|
+
*
|
|
26557
|
+
* `false` for a session id the provider has never heard of, which is
|
|
26558
|
+
* the same answer as "it ended": either way nothing is holding it up.
|
|
26559
|
+
*/
|
|
26560
|
+
alive: zod.z.boolean()
|
|
26561
|
+
}))
|
|
26264
26562
|
},
|
|
26265
26563
|
/** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
|
|
26266
26564
|
mount: { kind: "server-provided" }
|
|
@@ -38653,6 +38951,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
38653
38951
|
smoke: smokeCapability,
|
|
38654
38952
|
streamCatalog: streamCatalogCapability,
|
|
38655
38953
|
streamParams: streamParamsCapability,
|
|
38954
|
+
streamSignals: streamSignalsCapability,
|
|
38656
38955
|
switch: switchCapability,
|
|
38657
38956
|
tamper: tamperCapability,
|
|
38658
38957
|
temperatureSensor: temperatureSensorCapability,
|
|
@@ -41797,6 +42096,7 @@ var CAPABILITY_NAMES = {
|
|
|
41797
42096
|
streamBroker: "stream-broker",
|
|
41798
42097
|
streamCatalog: "stream-catalog",
|
|
41799
42098
|
streamParams: "stream-params",
|
|
42099
|
+
streamSignals: "stream-signals",
|
|
41800
42100
|
switch: "switch",
|
|
41801
42101
|
system: "system",
|
|
41802
42102
|
tamper: "tamper",
|
|
@@ -42358,6 +42658,10 @@ var CAPABILITY_ROUTER_KEYS = [
|
|
|
42358
42658
|
key: "streamParams",
|
|
42359
42659
|
name: "stream-params"
|
|
42360
42660
|
},
|
|
42661
|
+
{
|
|
42662
|
+
key: "streamSignals",
|
|
42663
|
+
name: "stream-signals"
|
|
42664
|
+
},
|
|
42361
42665
|
{
|
|
42362
42666
|
key: "switch",
|
|
42363
42667
|
name: "switch"
|
|
@@ -42591,6 +42895,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
|
|
|
42591
42895
|
streamBrokerCapability,
|
|
42592
42896
|
streamCatalogCapability,
|
|
42593
42897
|
streamParamsCapability,
|
|
42898
|
+
streamSignalsCapability,
|
|
42594
42899
|
switchCapability,
|
|
42595
42900
|
systemCapability,
|
|
42596
42901
|
tamperCapability,
|
|
@@ -48035,6 +48340,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
48035
48340
|
addonId: null,
|
|
48036
48341
|
access: "create"
|
|
48037
48342
|
},
|
|
48343
|
+
"streamBroker.reportStreamSignal": {
|
|
48344
|
+
capName: "stream-broker",
|
|
48345
|
+
capScope: "system",
|
|
48346
|
+
addonId: null,
|
|
48347
|
+
access: "create"
|
|
48348
|
+
},
|
|
48038
48349
|
"streamBroker.restartProfile": {
|
|
48039
48350
|
capName: "stream-broker",
|
|
48040
48351
|
capScope: "system",
|
|
@@ -48119,6 +48430,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
48119
48430
|
addonId: null,
|
|
48120
48431
|
access: "create"
|
|
48121
48432
|
},
|
|
48433
|
+
"streamSignals.listSignals": {
|
|
48434
|
+
capName: "stream-signals",
|
|
48435
|
+
capScope: "device",
|
|
48436
|
+
addonId: null,
|
|
48437
|
+
access: "view"
|
|
48438
|
+
},
|
|
48122
48439
|
"switch.setState": {
|
|
48123
48440
|
capName: "switch",
|
|
48124
48441
|
capScope: "device",
|
|
@@ -48923,6 +49240,7 @@ var KNOWN_CAP_NAMES = [
|
|
|
48923
49240
|
"stream-broker",
|
|
48924
49241
|
"stream-catalog",
|
|
48925
49242
|
"stream-params",
|
|
49243
|
+
"stream-signals",
|
|
48926
49244
|
"switch",
|
|
48927
49245
|
"system",
|
|
48928
49246
|
"terminal-session",
|
|
@@ -48990,6 +49308,7 @@ var DEVICE_CAP_NAMES = [
|
|
|
48990
49308
|
"snapshot",
|
|
48991
49309
|
"stream-catalog",
|
|
48992
49310
|
"stream-params",
|
|
49311
|
+
"stream-signals",
|
|
48993
49312
|
"switch",
|
|
48994
49313
|
"update",
|
|
48995
49314
|
"vacuum-control",
|
|
@@ -50760,6 +51079,11 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
|
|
|
50760
51079
|
form: "single",
|
|
50761
51080
|
optional: false
|
|
50762
51081
|
}],
|
|
51082
|
+
"streamBroker.reportStreamSignal": [{
|
|
51083
|
+
name: "deviceId",
|
|
51084
|
+
form: "single",
|
|
51085
|
+
optional: false
|
|
51086
|
+
}],
|
|
50763
51087
|
"streamBroker.restartProfile": [{
|
|
50764
51088
|
name: "deviceId",
|
|
50765
51089
|
form: "single",
|
|
@@ -51119,6 +51443,7 @@ var SYSTEM_SCOPE_DEVICE_METHODS = [
|
|
|
51119
51443
|
"streamBroker.produceEventMedia",
|
|
51120
51444
|
"streamBroker.publishCameraStream",
|
|
51121
51445
|
"streamBroker.renderPreBufferClip",
|
|
51446
|
+
"streamBroker.reportStreamSignal",
|
|
51122
51447
|
"streamBroker.restartProfile",
|
|
51123
51448
|
"streamBroker.retractCameraStream",
|
|
51124
51449
|
"streamBroker.setDeviceAudioMute",
|
|
@@ -51158,7 +51483,7 @@ function getCapsByProviderKind(kind) {
|
|
|
51158
51483
|
//#region src/generated/runtime-state-durability.ts
|
|
51159
51484
|
/**
|
|
51160
51485
|
* Per-cap runtime-state policy, projected from every `*.cap.ts` that declares
|
|
51161
|
-
* `runtimeState`.
|
|
51486
|
+
* `runtimeState`. 68 caps: 35 `restored`, 33 `session`.
|
|
51162
51487
|
*
|
|
51163
51488
|
* A cap absent from this map has no runtime-state slice at all. A cap PRESENT
|
|
51164
51489
|
* with `durability: 'session'` has one and has decided not to keep it.
|
|
@@ -51384,6 +51709,10 @@ var RUNTIME_STATE_POLICY = {
|
|
|
51384
51709
|
durability: "restored",
|
|
51385
51710
|
volatileFields: ["lastFetchedAt"]
|
|
51386
51711
|
},
|
|
51712
|
+
"stream-signals": {
|
|
51713
|
+
durability: "session",
|
|
51714
|
+
volatileFields: []
|
|
51715
|
+
},
|
|
51387
51716
|
"switch": {
|
|
51388
51717
|
durability: "restored",
|
|
51389
51718
|
volatileFields: ["lastChangedAt"]
|
|
@@ -57301,6 +57630,8 @@ exports.StreamProfileConfigSchema = StreamProfileConfigSchema;
|
|
|
57301
57630
|
exports.StreamProfileOptionsSchema = StreamProfileOptionsSchema;
|
|
57302
57631
|
exports.StreamProfilePatchSchema = StreamProfilePatchSchema;
|
|
57303
57632
|
exports.StreamProfileSchema = StreamProfileSchema;
|
|
57633
|
+
exports.StreamSignalSchema = StreamSignalSchema;
|
|
57634
|
+
exports.StreamSignalsStatusSchema = StreamSignalsStatusSchema;
|
|
57304
57635
|
exports.StreamSourceEntrySchema = require_sleep.StreamSourceEntrySchema;
|
|
57305
57636
|
exports.StreamSourceSchema = require_sleep.StreamSourceSchema;
|
|
57306
57637
|
exports.SubscribeAudioChunksInputSchema = require_sleep.SubscribeAudioChunksInputSchema;
|
|
@@ -57486,12 +57817,14 @@ exports.climateControlCapability = climateControlCapability;
|
|
|
57486
57817
|
exports.clusterModelSettingKey = clusterModelSettingKey;
|
|
57487
57818
|
exports.clusterStepSettingFieldsFor = clusterStepSettingFieldsFor;
|
|
57488
57819
|
exports.clusterStepSettingKey = clusterStepSettingKey;
|
|
57820
|
+
exports.codecIsAccepted = codecIsAccepted;
|
|
57489
57821
|
exports.collectHydratedFieldEntries = require_sleep.collectHydratedFieldEntries;
|
|
57490
57822
|
exports.collectHydratedFieldValues = require_sleep.collectHydratedFieldValues;
|
|
57491
57823
|
exports.collectSecretConfigKeys = collectSecretConfigKeys;
|
|
57492
57824
|
exports.colorCapability = colorCapability;
|
|
57493
57825
|
exports.colorForKind = colorForKind;
|
|
57494
57826
|
exports.commitWatchdogRestart = commitWatchdogRestart;
|
|
57827
|
+
exports.compareToTarget = compareToTarget;
|
|
57495
57828
|
exports.compileExpression = compileExpression;
|
|
57496
57829
|
exports.compileExpressionSafe = compileExpressionSafe;
|
|
57497
57830
|
exports.composeSwitchedOff = composeSwitchedOff;
|
|
@@ -57750,6 +58083,7 @@ exports.procedureAuthKey = procedureAuthKey;
|
|
|
57750
58083
|
exports.ptzAutotrackCapability = ptzAutotrackCapability;
|
|
57751
58084
|
exports.ptzCapability = ptzCapability;
|
|
57752
58085
|
exports.pythonScriptForBackend = pythonScriptForBackend;
|
|
58086
|
+
exports.rankStreams = rankStreams;
|
|
57753
58087
|
exports.readClusterStepModels = readClusterStepModels;
|
|
57754
58088
|
exports.readClusterStepSettings = readClusterStepSettings;
|
|
57755
58089
|
exports.readDetailCropConvention = readDetailCropConvention;
|
|
@@ -57834,6 +58168,7 @@ exports.streamCatalogCapability = streamCatalogCapability;
|
|
|
57834
58168
|
exports.streamParamsCapability = streamParamsCapability;
|
|
57835
58169
|
exports.streamPixels = streamPixels;
|
|
57836
58170
|
exports.streamQualityLabel = streamQualityLabel;
|
|
58171
|
+
exports.streamSignalsCapability = streamSignalsCapability;
|
|
57837
58172
|
exports.stripRetiredBandPreBufferSec = stripRetiredBandPreBufferSec;
|
|
57838
58173
|
exports.strippedOfLegacyReadOnly = strippedOfLegacyReadOnly;
|
|
57839
58174
|
exports.subKindsOf = subKindsOf;
|