@camstack/addon-provider-rtsp 1.2.101 → 1.2.102

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 CHANGED
@@ -23,7 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  //#endregion
24
24
  let node_net = require("node:net");
25
25
  node_net = __toESM(node_net);
26
- //#region ../types/dist/event-category-ZyX6jcse.mjs
26
+ //#region ../types/dist/event-category-BVDXG4tB.mjs
27
27
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
28
28
  EventCategory["SystemBoot"] = "system.boot";
29
29
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -640,6 +640,20 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
640
640
  * pull-reconcile from the provider's `getStatus` on reconnect. */
641
641
  EventCategory["MeshNetworkChanged"] = "network.mesh.changed";
642
642
  EventCategory["BackupCompleted"] = "backup.completed";
643
+ /**
644
+ * A whole backup RUN finished — every destination attempted, win or lose.
645
+ *
646
+ * `backup.completed` fires once per DESTINATION, which is the right grain for
647
+ * a progress UI and the wrong one for a notification: an operator with three
648
+ * destinations would be told three times. And a run where two of three
649
+ * destinations succeeded is not a clean success — a single "backup
650
+ * completed" that hid the failed one would be a lie, so the count of each is
651
+ * carried here and the message says both.
652
+ *
653
+ * Emitted by the backup orchestrator only after the destination loop, so a
654
+ * run that dies during the BUILD phase produces no completion at all.
655
+ */
656
+ EventCategory["BackupRunCompleted"] = "backup.run-completed";
643
657
  EventCategory["BackupRestored"] = "backup.restored";
644
658
  EventCategory["NotificationDispatched"] = "notification.dispatched";
645
659
  EventCategory["NotificationFailed"] = "notification.failed";
@@ -5370,7 +5384,7 @@ var ZodIssueCode = {
5370
5384
  var ZodFirstPartyTypeKind;
5371
5385
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5372
5386
  //#endregion
5373
- //#region ../types/dist/sleep-DBKu2-U5.mjs
5387
+ //#region ../types/dist/sleep-DEuj7E3j.mjs
5374
5388
  /**
5375
5389
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5376
5390
  * window to float samples (D455).
@@ -11546,6 +11560,72 @@ method(ListInputSchema, array(BrokerInfoSchema$1)), method(GetInputSchema, Broke
11546
11560
  auth: "admin"
11547
11561
  }), method(GetStateInputSchema, unknown().nullable()), method(_void(), RegistryStatusSchema);
11548
11562
  DeviceType.Camera;
11563
+ /**
11564
+ * The signals a device can emit to WAKE its own stream.
11565
+ *
11566
+ * A camera whose stream is built on demand sleeps until something asks for it,
11567
+ * and "something" cannot be a consumer that is merely attached — a Frigate-style
11568
+ * puller holds a session open for ever, and treating that as demand would keep
11569
+ * a battery camera awake for ever, which is the whole thing the battery is for
11570
+ * (D173). So the wake has to come from the CAMERA: an event it noticed by
11571
+ * itself, with no stream running.
11572
+ *
11573
+ * ## The vocabulary is the PROVIDER'S, not ours
11574
+ *
11575
+ * Like `consumables`, this cap declares no vocabulary of its own. A provider
11576
+ * names each signal with a `code` it chooses and a `label` an operator reads.
11577
+ * Reolink offers motion and camera-native detection; another provider may offer
11578
+ * a tamper, a doorbell press, a PIR, or something no camera in this fleet has
11579
+ * yet. A fixed enum here would mean every new signal is a framework release.
11580
+ *
11581
+ * It is deliberately NOT derived from the caps a device already binds. Whether
11582
+ * a camera CAN push firmware motion is expressed by `motionSources` containing
11583
+ * `'onboard'`, and whether it does AI on-camera by the `native-object-detection`
11584
+ * binding — but both answer "what drives the detection pipeline", which is a
11585
+ * different question from "what may wake a sleeping stream". A camera can do
11586
+ * the first and not be trusted with the second, and the operator picks per
11587
+ * camera. Two questions, two authorities.
11588
+ *
11589
+ * ## Availability is not permission
11590
+ *
11591
+ * `listSignals` says what the device CAN emit. Whether a given signal actually
11592
+ * wakes the stream is the operator's per-camera choice, held by the broker
11593
+ * alongside the cooldown — see the stream-broker cap's wake settings. A
11594
+ * provider declaring a signal is not a provider enabling it.
11595
+ */
11596
+ /** One signal a device can emit. */
11597
+ var StreamSignalSchema = object({
11598
+ /** Stable id chosen by the provider, e.g. `'motion'`, `'person'`, `'tamper'`. */
11599
+ code: string().min(1),
11600
+ /** What an operator reads in the picker. The provider's own wording. */
11601
+ label: string().min(1),
11602
+ /**
11603
+ * Whether the provider recommends this signal ON when a camera is first set
11604
+ * up. A provider knows which of its signals are cheap and reliable; an
11605
+ * operator should not have to discover that by trial. Reolink recommends
11606
+ * both of its own.
11607
+ */
11608
+ recommended: boolean()
11609
+ });
11610
+ var StreamSignalsStatusSchema = object({
11611
+ signals: array(StreamSignalSchema),
11612
+ lastFetchedAt: number()
11613
+ });
11614
+ var streamSignalsCapability = {
11615
+ name: "stream-signals",
11616
+ scope: "device",
11617
+ deviceNative: true,
11618
+ mode: "singleton",
11619
+ deviceTypes: Object.values(DeviceType),
11620
+ runtimeState: StreamSignalsStatusSchema,
11621
+ methods: {
11622
+ /**
11623
+ * What this device can emit. Empty is a valid and common answer — most
11624
+ * cameras have nothing to offer here, and an empty list is what makes the
11625
+ * broker's picker show nothing rather than a false choice.
11626
+ */
11627
+ listSignals: method(_void(), array(StreamSignalSchema).readonly()) }
11628
+ };
11549
11629
  /** Stream delivery format. (Relocated from the retired `streaming-engine` cap.) */
11550
11630
  var StreamFormatSchema = _enum([
11551
11631
  "webrtc",
@@ -11942,6 +12022,22 @@ var EgressTranscodeSchema = object({
11942
12022
  camStreamId: string().nullable()
11943
12023
  });
11944
12024
  method(object({
12025
+ deviceId: number().int().nonnegative(),
12026
+ /** The provider's signal code. */
12027
+ code: string().min(1),
12028
+ /** Ms epoch. Absent ⇒ now. */
12029
+ at: number().optional()
12030
+ }), object({
12031
+ /** Whether the broker acted on it, and if not, why. */
12032
+ accepted: boolean(),
12033
+ reason: _enum([
12034
+ "woke",
12035
+ "hold-extended",
12036
+ "not-enabled",
12037
+ "no-consumer",
12038
+ "unknown-code"
12039
+ ])
12040
+ }), { kind: "mutation" }), method(object({
11945
12041
  deviceId: number().int().nonnegative(),
11946
12042
  camStreamId: string().min(1),
11947
12043
  kind: CamStreamKindSchema,
@@ -12196,6 +12292,16 @@ var PickStreamRequirementsSchema = object({
12196
12292
  acceptCodecs: array(StreamCodecSchema).readonly().optional(),
12197
12293
  /** Minimum vertical resolution. Streams shorter than this are dropped. */
12198
12294
  minHeight: number().int().positive().optional(),
12295
+ /**
12296
+ * Maximum vertical resolution. Streams TALLER than this are dropped.
12297
+ *
12298
+ * A consumer can have a ceiling as real as its floor: Alexa documents
12299
+ * 480p to 1080p, and a 4K stream is as unusable to an Echo as a 360p one.
12300
+ * Without this the ceiling had to be re-implemented by every caller — and
12301
+ * one caller implementing it privately is how a second scoring authority
12302
+ * gets born.
12303
+ */
12304
+ maxHeight: number().int().positive().optional(),
12199
12305
  /** Minimum horizontal resolution. */
12200
12306
  minWidth: number().int().positive().optional(),
12201
12307
  /**
@@ -12212,7 +12318,22 @@ var PickStreamRequirementsSchema = object({
12212
12318
  * transcoded" guard: if the device is already serving the consumer's
12213
12319
  * codec end-to-end, there's nothing to optimise.
12214
12320
  */
12215
- requireSiblingCodec: array(StreamCodecSchema).readonly().optional()
12321
+ requireSiblingCodec: array(StreamCodecSchema).readonly().optional(),
12322
+ /**
12323
+ * Whether a stream the consumer CANNOT decode may still be picked, on the
12324
+ * understanding that it will be transcoded.
12325
+ *
12326
+ * Default `false` — today's behaviour, and the right one for a bypass
12327
+ * question ("is there a stream I can forward untouched?"). Set `true` to
12328
+ * ask the larger question: "what is the best stream for me, transcoding if
12329
+ * I must?" A stream that satisfies `acceptCodecs` always outranks one that
12330
+ * does not, so a passthrough is never lost to a transcode; the answer says
12331
+ * which it is in {@link PickedCamStreamSchema.transcodes}.
12332
+ *
12333
+ * This is what lets one picker serve both the bypass and the full source
12334
+ * choice, instead of a consumer scoring privately when the bypass misses.
12335
+ */
12336
+ allowTranscode: boolean().optional()
12216
12337
  }).readonly();
12217
12338
  var PickStreamPreferencesSchema = object({
12218
12339
  /**
@@ -12226,12 +12347,32 @@ var PickStreamPreferencesSchema = object({
12226
12347
  * picks the tallest stream; `'lowest'` picks the shortest (used by
12227
12348
  * memory-constrained consumers / Apple Home guest sessions).
12228
12349
  */
12229
- resolutionPreference: _enum(["highest", "lowest"]).optional()
12350
+ resolutionPreference: _enum(["highest", "lowest"]).optional(),
12351
+ /**
12352
+ * The height the consumer actually wants to DELIVER.
12353
+ *
12354
+ * Not a constraint — an ordering. With it set, the SMALLEST stream at or
12355
+ * above the target wins, and only if nothing reaches it does the tallest
12356
+ * take over. Pulling 1296 lines to draw 720 on a 1280x800 Echo panel costs
12357
+ * a decode and buys nothing, and `resolutionPreference: 'highest'` cannot
12358
+ * express that: it says "as big as possible", which is a different wish.
12359
+ *
12360
+ * Ignored when absent, so every existing caller keeps its ordering.
12361
+ */
12362
+ targetHeight: number().int().positive().optional()
12230
12363
  }).readonly();
12231
12364
  var PickedCamStreamSchema = object({
12232
12365
  camStreamId: string(),
12233
12366
  codec: string().optional(),
12234
12367
  resolution: CamStreamResolutionSchema.optional(),
12368
+ /**
12369
+ * Whether serving this stream requires a decode + re-encode.
12370
+ *
12371
+ * `false` is a stream the consumer can take as it stands. Only ever `true`
12372
+ * when the caller asked for it with `allowTranscode`, so a caller that did
12373
+ * not ask cannot be handed a cost it never agreed to pay.
12374
+ */
12375
+ transcodes: boolean(),
12235
12376
  /** One-line explanation of why this stream won — for logs / debug UI. */
12236
12377
  reason: string()
12237
12378
  });
@@ -16748,6 +16889,8 @@ var NcSystemEventKindSchema = _enum([
16748
16889
  "device-enabled",
16749
16890
  "device-battery-low",
16750
16891
  "device-battery-normal",
16892
+ "device-consumable-low",
16893
+ "device-consumable-normal",
16751
16894
  "stream-online",
16752
16895
  "stream-offline",
16753
16896
  "node-online",
@@ -16766,6 +16909,7 @@ var NcSystemEventKindSchema = _enum([
16766
16909
  "addon-updated",
16767
16910
  "server-updated",
16768
16911
  "export-completed",
16912
+ "backup-completed",
16769
16913
  "camera-online",
16770
16914
  "camera-offline",
16771
16915
  "camera-disabled",
@@ -24151,10 +24295,28 @@ DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), ar
24151
24295
  }), boolean()), method(object({
24152
24296
  deviceId: number().int().nonnegative(),
24153
24297
  sessionId: string()
24154
- }), object({ pendingRenegotiation: object({
24155
- target: WebrtcStreamTargetSchema,
24156
- epoch: number()
24157
- }).nullable() }));
24298
+ }), object({
24299
+ pendingRenegotiation: object({
24300
+ target: WebrtcStreamTargetSchema,
24301
+ epoch: number()
24302
+ }).nullable(),
24303
+ /**
24304
+ * Whether the session still EXISTS.
24305
+ *
24306
+ * A consumer that holds a resource for the life of a session needs to
24307
+ * be able to ask, because a session does not always end the way it
24308
+ * began. Measured on this hub 2026-09-13: an Echo that got stuck never
24309
+ * sent `SessionDisconnected`, so the Alexa exporter's
24310
+ * `releaseEgressTranscode` never ran, and a 2304x1296 HEVC to 720p
24311
+ * H.264 transcode kept running for NOBODY for more than twenty
24312
+ * minutes. The WebRTC session had logged `WebRTC session closed`
24313
+ * minutes earlier — the broker knew; the holder had no way to ask.
24314
+ *
24315
+ * `false` for a session id the provider has never heard of, which is
24316
+ * the same answer as "it ended": either way nothing is holding it up.
24317
+ */
24318
+ alive: boolean()
24319
+ }));
24158
24320
  object({
24159
24321
  /** All accessory children of the parent. */
24160
24322
  childDeviceIds: array(number()).readonly(),
@@ -34081,6 +34243,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
34081
34243
  smoke: smokeCapability,
34082
34244
  streamCatalog: streamCatalogCapability,
34083
34245
  streamParams: streamParamsCapability,
34246
+ streamSignals: streamSignalsCapability,
34084
34247
  switch: switchCapability,
34085
34248
  tamper: tamperCapability,
34086
34249
  temperatureSensor: temperatureSensorCapability,
@@ -40720,6 +40883,12 @@ Object.freeze({
40720
40883
  addonId: null,
40721
40884
  access: "create"
40722
40885
  },
40886
+ "streamBroker.reportStreamSignal": {
40887
+ capName: "stream-broker",
40888
+ capScope: "system",
40889
+ addonId: null,
40890
+ access: "create"
40891
+ },
40723
40892
  "streamBroker.restartProfile": {
40724
40893
  capName: "stream-broker",
40725
40894
  capScope: "system",
@@ -40804,6 +40973,12 @@ Object.freeze({
40804
40973
  addonId: null,
40805
40974
  access: "create"
40806
40975
  },
40976
+ "streamSignals.listSignals": {
40977
+ capName: "stream-signals",
40978
+ capScope: "device",
40979
+ addonId: null,
40980
+ access: "view"
40981
+ },
40807
40982
  "switch.setState": {
40808
40983
  capName: "switch",
40809
40984
  capScope: "device",
@@ -43176,6 +43351,11 @@ Object.freeze({
43176
43351
  form: "single",
43177
43352
  optional: false
43178
43353
  }],
43354
+ "streamBroker.reportStreamSignal": [{
43355
+ name: "deviceId",
43356
+ form: "single",
43357
+ optional: false
43358
+ }],
43179
43359
  "streamBroker.restartProfile": [{
43180
43360
  name: "deviceId",
43181
43361
  form: "single",
package/dist/addon.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import net from "node:net";
2
- //#region ../types/dist/event-category-ZyX6jcse.mjs
2
+ //#region ../types/dist/event-category-BVDXG4tB.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -616,6 +616,20 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
616
616
  * pull-reconcile from the provider's `getStatus` on reconnect. */
617
617
  EventCategory["MeshNetworkChanged"] = "network.mesh.changed";
618
618
  EventCategory["BackupCompleted"] = "backup.completed";
619
+ /**
620
+ * A whole backup RUN finished — every destination attempted, win or lose.
621
+ *
622
+ * `backup.completed` fires once per DESTINATION, which is the right grain for
623
+ * a progress UI and the wrong one for a notification: an operator with three
624
+ * destinations would be told three times. And a run where two of three
625
+ * destinations succeeded is not a clean success — a single "backup
626
+ * completed" that hid the failed one would be a lie, so the count of each is
627
+ * carried here and the message says both.
628
+ *
629
+ * Emitted by the backup orchestrator only after the destination loop, so a
630
+ * run that dies during the BUILD phase produces no completion at all.
631
+ */
632
+ EventCategory["BackupRunCompleted"] = "backup.run-completed";
619
633
  EventCategory["BackupRestored"] = "backup.restored";
620
634
  EventCategory["NotificationDispatched"] = "notification.dispatched";
621
635
  EventCategory["NotificationFailed"] = "notification.failed";
@@ -5346,7 +5360,7 @@ var ZodIssueCode = {
5346
5360
  var ZodFirstPartyTypeKind;
5347
5361
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5348
5362
  //#endregion
5349
- //#region ../types/dist/sleep-DBKu2-U5.mjs
5363
+ //#region ../types/dist/sleep-DEuj7E3j.mjs
5350
5364
  /**
5351
5365
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5352
5366
  * window to float samples (D455).
@@ -11522,6 +11536,72 @@ method(ListInputSchema, array(BrokerInfoSchema$1)), method(GetInputSchema, Broke
11522
11536
  auth: "admin"
11523
11537
  }), method(GetStateInputSchema, unknown().nullable()), method(_void(), RegistryStatusSchema);
11524
11538
  DeviceType.Camera;
11539
+ /**
11540
+ * The signals a device can emit to WAKE its own stream.
11541
+ *
11542
+ * A camera whose stream is built on demand sleeps until something asks for it,
11543
+ * and "something" cannot be a consumer that is merely attached — a Frigate-style
11544
+ * puller holds a session open for ever, and treating that as demand would keep
11545
+ * a battery camera awake for ever, which is the whole thing the battery is for
11546
+ * (D173). So the wake has to come from the CAMERA: an event it noticed by
11547
+ * itself, with no stream running.
11548
+ *
11549
+ * ## The vocabulary is the PROVIDER'S, not ours
11550
+ *
11551
+ * Like `consumables`, this cap declares no vocabulary of its own. A provider
11552
+ * names each signal with a `code` it chooses and a `label` an operator reads.
11553
+ * Reolink offers motion and camera-native detection; another provider may offer
11554
+ * a tamper, a doorbell press, a PIR, or something no camera in this fleet has
11555
+ * yet. A fixed enum here would mean every new signal is a framework release.
11556
+ *
11557
+ * It is deliberately NOT derived from the caps a device already binds. Whether
11558
+ * a camera CAN push firmware motion is expressed by `motionSources` containing
11559
+ * `'onboard'`, and whether it does AI on-camera by the `native-object-detection`
11560
+ * binding — but both answer "what drives the detection pipeline", which is a
11561
+ * different question from "what may wake a sleeping stream". A camera can do
11562
+ * the first and not be trusted with the second, and the operator picks per
11563
+ * camera. Two questions, two authorities.
11564
+ *
11565
+ * ## Availability is not permission
11566
+ *
11567
+ * `listSignals` says what the device CAN emit. Whether a given signal actually
11568
+ * wakes the stream is the operator's per-camera choice, held by the broker
11569
+ * alongside the cooldown — see the stream-broker cap's wake settings. A
11570
+ * provider declaring a signal is not a provider enabling it.
11571
+ */
11572
+ /** One signal a device can emit. */
11573
+ var StreamSignalSchema = object({
11574
+ /** Stable id chosen by the provider, e.g. `'motion'`, `'person'`, `'tamper'`. */
11575
+ code: string().min(1),
11576
+ /** What an operator reads in the picker. The provider's own wording. */
11577
+ label: string().min(1),
11578
+ /**
11579
+ * Whether the provider recommends this signal ON when a camera is first set
11580
+ * up. A provider knows which of its signals are cheap and reliable; an
11581
+ * operator should not have to discover that by trial. Reolink recommends
11582
+ * both of its own.
11583
+ */
11584
+ recommended: boolean()
11585
+ });
11586
+ var StreamSignalsStatusSchema = object({
11587
+ signals: array(StreamSignalSchema),
11588
+ lastFetchedAt: number()
11589
+ });
11590
+ var streamSignalsCapability = {
11591
+ name: "stream-signals",
11592
+ scope: "device",
11593
+ deviceNative: true,
11594
+ mode: "singleton",
11595
+ deviceTypes: Object.values(DeviceType),
11596
+ runtimeState: StreamSignalsStatusSchema,
11597
+ methods: {
11598
+ /**
11599
+ * What this device can emit. Empty is a valid and common answer — most
11600
+ * cameras have nothing to offer here, and an empty list is what makes the
11601
+ * broker's picker show nothing rather than a false choice.
11602
+ */
11603
+ listSignals: method(_void(), array(StreamSignalSchema).readonly()) }
11604
+ };
11525
11605
  /** Stream delivery format. (Relocated from the retired `streaming-engine` cap.) */
11526
11606
  var StreamFormatSchema = _enum([
11527
11607
  "webrtc",
@@ -11918,6 +11998,22 @@ var EgressTranscodeSchema = object({
11918
11998
  camStreamId: string().nullable()
11919
11999
  });
11920
12000
  method(object({
12001
+ deviceId: number().int().nonnegative(),
12002
+ /** The provider's signal code. */
12003
+ code: string().min(1),
12004
+ /** Ms epoch. Absent ⇒ now. */
12005
+ at: number().optional()
12006
+ }), object({
12007
+ /** Whether the broker acted on it, and if not, why. */
12008
+ accepted: boolean(),
12009
+ reason: _enum([
12010
+ "woke",
12011
+ "hold-extended",
12012
+ "not-enabled",
12013
+ "no-consumer",
12014
+ "unknown-code"
12015
+ ])
12016
+ }), { kind: "mutation" }), method(object({
11921
12017
  deviceId: number().int().nonnegative(),
11922
12018
  camStreamId: string().min(1),
11923
12019
  kind: CamStreamKindSchema,
@@ -12172,6 +12268,16 @@ var PickStreamRequirementsSchema = object({
12172
12268
  acceptCodecs: array(StreamCodecSchema).readonly().optional(),
12173
12269
  /** Minimum vertical resolution. Streams shorter than this are dropped. */
12174
12270
  minHeight: number().int().positive().optional(),
12271
+ /**
12272
+ * Maximum vertical resolution. Streams TALLER than this are dropped.
12273
+ *
12274
+ * A consumer can have a ceiling as real as its floor: Alexa documents
12275
+ * 480p to 1080p, and a 4K stream is as unusable to an Echo as a 360p one.
12276
+ * Without this the ceiling had to be re-implemented by every caller — and
12277
+ * one caller implementing it privately is how a second scoring authority
12278
+ * gets born.
12279
+ */
12280
+ maxHeight: number().int().positive().optional(),
12175
12281
  /** Minimum horizontal resolution. */
12176
12282
  minWidth: number().int().positive().optional(),
12177
12283
  /**
@@ -12188,7 +12294,22 @@ var PickStreamRequirementsSchema = object({
12188
12294
  * transcoded" guard: if the device is already serving the consumer's
12189
12295
  * codec end-to-end, there's nothing to optimise.
12190
12296
  */
12191
- requireSiblingCodec: array(StreamCodecSchema).readonly().optional()
12297
+ requireSiblingCodec: array(StreamCodecSchema).readonly().optional(),
12298
+ /**
12299
+ * Whether a stream the consumer CANNOT decode may still be picked, on the
12300
+ * understanding that it will be transcoded.
12301
+ *
12302
+ * Default `false` — today's behaviour, and the right one for a bypass
12303
+ * question ("is there a stream I can forward untouched?"). Set `true` to
12304
+ * ask the larger question: "what is the best stream for me, transcoding if
12305
+ * I must?" A stream that satisfies `acceptCodecs` always outranks one that
12306
+ * does not, so a passthrough is never lost to a transcode; the answer says
12307
+ * which it is in {@link PickedCamStreamSchema.transcodes}.
12308
+ *
12309
+ * This is what lets one picker serve both the bypass and the full source
12310
+ * choice, instead of a consumer scoring privately when the bypass misses.
12311
+ */
12312
+ allowTranscode: boolean().optional()
12192
12313
  }).readonly();
12193
12314
  var PickStreamPreferencesSchema = object({
12194
12315
  /**
@@ -12202,12 +12323,32 @@ var PickStreamPreferencesSchema = object({
12202
12323
  * picks the tallest stream; `'lowest'` picks the shortest (used by
12203
12324
  * memory-constrained consumers / Apple Home guest sessions).
12204
12325
  */
12205
- resolutionPreference: _enum(["highest", "lowest"]).optional()
12326
+ resolutionPreference: _enum(["highest", "lowest"]).optional(),
12327
+ /**
12328
+ * The height the consumer actually wants to DELIVER.
12329
+ *
12330
+ * Not a constraint — an ordering. With it set, the SMALLEST stream at or
12331
+ * above the target wins, and only if nothing reaches it does the tallest
12332
+ * take over. Pulling 1296 lines to draw 720 on a 1280x800 Echo panel costs
12333
+ * a decode and buys nothing, and `resolutionPreference: 'highest'` cannot
12334
+ * express that: it says "as big as possible", which is a different wish.
12335
+ *
12336
+ * Ignored when absent, so every existing caller keeps its ordering.
12337
+ */
12338
+ targetHeight: number().int().positive().optional()
12206
12339
  }).readonly();
12207
12340
  var PickedCamStreamSchema = object({
12208
12341
  camStreamId: string(),
12209
12342
  codec: string().optional(),
12210
12343
  resolution: CamStreamResolutionSchema.optional(),
12344
+ /**
12345
+ * Whether serving this stream requires a decode + re-encode.
12346
+ *
12347
+ * `false` is a stream the consumer can take as it stands. Only ever `true`
12348
+ * when the caller asked for it with `allowTranscode`, so a caller that did
12349
+ * not ask cannot be handed a cost it never agreed to pay.
12350
+ */
12351
+ transcodes: boolean(),
12211
12352
  /** One-line explanation of why this stream won — for logs / debug UI. */
12212
12353
  reason: string()
12213
12354
  });
@@ -16724,6 +16865,8 @@ var NcSystemEventKindSchema = _enum([
16724
16865
  "device-enabled",
16725
16866
  "device-battery-low",
16726
16867
  "device-battery-normal",
16868
+ "device-consumable-low",
16869
+ "device-consumable-normal",
16727
16870
  "stream-online",
16728
16871
  "stream-offline",
16729
16872
  "node-online",
@@ -16742,6 +16885,7 @@ var NcSystemEventKindSchema = _enum([
16742
16885
  "addon-updated",
16743
16886
  "server-updated",
16744
16887
  "export-completed",
16888
+ "backup-completed",
16745
16889
  "camera-online",
16746
16890
  "camera-offline",
16747
16891
  "camera-disabled",
@@ -24127,10 +24271,28 @@ DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), ar
24127
24271
  }), boolean()), method(object({
24128
24272
  deviceId: number().int().nonnegative(),
24129
24273
  sessionId: string()
24130
- }), object({ pendingRenegotiation: object({
24131
- target: WebrtcStreamTargetSchema,
24132
- epoch: number()
24133
- }).nullable() }));
24274
+ }), object({
24275
+ pendingRenegotiation: object({
24276
+ target: WebrtcStreamTargetSchema,
24277
+ epoch: number()
24278
+ }).nullable(),
24279
+ /**
24280
+ * Whether the session still EXISTS.
24281
+ *
24282
+ * A consumer that holds a resource for the life of a session needs to
24283
+ * be able to ask, because a session does not always end the way it
24284
+ * began. Measured on this hub 2026-09-13: an Echo that got stuck never
24285
+ * sent `SessionDisconnected`, so the Alexa exporter's
24286
+ * `releaseEgressTranscode` never ran, and a 2304x1296 HEVC to 720p
24287
+ * H.264 transcode kept running for NOBODY for more than twenty
24288
+ * minutes. The WebRTC session had logged `WebRTC session closed`
24289
+ * minutes earlier — the broker knew; the holder had no way to ask.
24290
+ *
24291
+ * `false` for a session id the provider has never heard of, which is
24292
+ * the same answer as "it ended": either way nothing is holding it up.
24293
+ */
24294
+ alive: boolean()
24295
+ }));
24134
24296
  object({
24135
24297
  /** All accessory children of the parent. */
24136
24298
  childDeviceIds: array(number()).readonly(),
@@ -34057,6 +34219,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
34057
34219
  smoke: smokeCapability,
34058
34220
  streamCatalog: streamCatalogCapability,
34059
34221
  streamParams: streamParamsCapability,
34222
+ streamSignals: streamSignalsCapability,
34060
34223
  switch: switchCapability,
34061
34224
  tamper: tamperCapability,
34062
34225
  temperatureSensor: temperatureSensorCapability,
@@ -40696,6 +40859,12 @@ Object.freeze({
40696
40859
  addonId: null,
40697
40860
  access: "create"
40698
40861
  },
40862
+ "streamBroker.reportStreamSignal": {
40863
+ capName: "stream-broker",
40864
+ capScope: "system",
40865
+ addonId: null,
40866
+ access: "create"
40867
+ },
40699
40868
  "streamBroker.restartProfile": {
40700
40869
  capName: "stream-broker",
40701
40870
  capScope: "system",
@@ -40780,6 +40949,12 @@ Object.freeze({
40780
40949
  addonId: null,
40781
40950
  access: "create"
40782
40951
  },
40952
+ "streamSignals.listSignals": {
40953
+ capName: "stream-signals",
40954
+ capScope: "device",
40955
+ addonId: null,
40956
+ access: "view"
40957
+ },
40783
40958
  "switch.setState": {
40784
40959
  capName: "switch",
40785
40960
  capScope: "device",
@@ -43152,6 +43327,11 @@ Object.freeze({
43152
43327
  form: "single",
43153
43328
  optional: false
43154
43329
  }],
43330
+ "streamBroker.reportStreamSignal": [{
43331
+ name: "deviceId",
43332
+ form: "single",
43333
+ optional: false
43334
+ }],
43155
43335
  "streamBroker.restartProfile": [{
43156
43336
  name: "deviceId",
43157
43337
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rtsp",
3
- "version": "1.2.101",
3
+ "version": "1.2.102",
4
4
  "description": "Generic RTSP camera device provider addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",