@camstack/addon-mqtt-broker 1.2.102 → 1.2.103

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.
@@ -5405,7 +5405,7 @@ var ZodIssueCode = {
5405
5405
  var ZodFirstPartyTypeKind;
5406
5406
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5407
5407
  //#endregion
5408
- //#region ../types/dist/sleep-DEuj7E3j.mjs
5408
+ //#region ../types/dist/sleep-BDR76Ykr.mjs
5409
5409
  /**
5410
5410
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5411
5411
  * window to float samples (D455).
@@ -17931,6 +17931,48 @@ var NcAlarmConfigSchema = object({
17931
17931
  settings: NcAlarmSettingsSchema,
17932
17932
  coverage: array(NcAlarmModeCoverageSchema)
17933
17933
  });
17934
+ /**
17935
+ * ONE rule's demand on ONE camera's clip ring.
17936
+ *
17937
+ * A rule, not a camera: the broker takes the MAX over the asks that reach a
17938
+ * camera, so the answer stays a faithful description of the rules and the
17939
+ * bounding (the byte budget, the broker's own ceiling) stays where the cost
17940
+ * lives. A camera that appears in no ask is being told **nothing** — which is
17941
+ * a different thing from never having been told, and only the envelope
17942
+ * (`rulesLoaded`) can tell those apart.
17943
+ */
17944
+ var NcClipRetentionAskSchema = object({
17945
+ /**
17946
+ * The camera this ask is about. **Absent = every camera**, which is what a
17947
+ * rule with no `conditions.devices` means: it can fire anywhere, so it is
17948
+ * asking everywhere.
17949
+ */
17950
+ deviceId: number().int().nonnegative().optional(),
17951
+ /** Seconds of history the rule needs held for it. Never zero — a rule that
17952
+ * needs nothing produces no ask at all. */
17953
+ seconds: number().min(0).max(60),
17954
+ /** The profile the rule cuts from, when it named one. Absent = the cheapest
17955
+ * assigned, which is what the cut defaults to. */
17956
+ profile: CamProfileSchema.optional(),
17957
+ /** Who is asking — so a retention the operator did not expect names a rule. */
17958
+ ruleId: string(),
17959
+ ruleName: string(),
17960
+ /** Why this many seconds: the rule's own window, or the occupancy ceiling. */
17961
+ reason: _enum(["window", "occupancy"])
17962
+ });
17963
+ /**
17964
+ * The whole answer, with the one bit that keeps absence from reading as zero.
17965
+ *
17966
+ * `rulesLoaded` is false until the rule ledger has completed a load at least
17967
+ * once. A centre that is up but has not read its rules yet answers with an
17968
+ * empty ask list that means nothing at all — applying it would drop every
17969
+ * camera's ring to zero for the first events after a restart, which is the
17970
+ * footage this mechanism exists to keep.
17971
+ */
17972
+ var NcClipRetentionPlanSchema = object({
17973
+ rulesLoaded: boolean(),
17974
+ asks: array(NcClipRetentionAskSchema).readonly()
17975
+ });
17934
17976
  method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), method(object({ ruleId: string() }), object({ rule: NcRuleSchema.nullable() }), { auth: "admin" }), method(object({ rule: NcRuleInputSchema }), object({ rule: NcRuleSchema }), {
17935
17977
  kind: "mutation",
17936
17978
  auth: "admin",
@@ -17951,7 +17993,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
17951
17993
  }), object({ success: literal(true) }), {
17952
17994
  kind: "mutation",
17953
17995
  auth: "admin"
17954
- }), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
17996
+ }), method(object({}), NcClipRetentionPlanSchema, { auth: "admin" }), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
17955
17997
  deviceId: number().int(),
17956
17998
  muted: boolean()
17957
17999
  }), object({ success: literal(true) }), {
@@ -20485,6 +20527,33 @@ var RunReplayFrameProcessorResultSchema = object({ tracks: array(object({
20485
20527
  * solid is this track", cheaper than re-deriving it from a trajectory. */
20486
20528
  framesMatched: number().int()
20487
20529
  })).readonly() });
20530
+ /**
20531
+ * The long-term series a camera produces. A closed union so a typo cannot
20532
+ * invent one, and the ONE declaration of it — the addon's `AnalyticsLtsSeries`
20533
+ * is an alias of this, not a second list.
20534
+ */
20535
+ var AnalyticsLtsSeriesSchema = _enum([
20536
+ "motion",
20537
+ "audio-dbfs",
20538
+ "battery",
20539
+ "occupancy"
20540
+ ]);
20541
+ /**
20542
+ * One closed 5-minute bucket of a long-term series.
20543
+ *
20544
+ * `samples` is how many readings the bucket folded — it is the ACTIVITY for a
20545
+ * series like `motion`, and the confidence for one like `occupancy`. A bucket
20546
+ * that exists at all was measured; a bucket that is absent was not, which is a
20547
+ * different claim from zero and the reason this is a sparse series.
20548
+ */
20549
+ var LtsBucketSchema = object({
20550
+ scope: string(),
20551
+ bucketStart: number().int(),
20552
+ samples: number().int().nonnegative(),
20553
+ sum: number(),
20554
+ min: number(),
20555
+ max: number()
20556
+ });
20488
20557
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
20489
20558
  deviceId: number(),
20490
20559
  trackId: string()
@@ -20531,6 +20600,14 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20531
20600
  kinds: array(string()).optional(),
20532
20601
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
20533
20602
  }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
20603
+ deviceId: number(),
20604
+ series: AnalyticsLtsSeriesSchema,
20605
+ /** Omit for EVERY scope of this series in the range. */
20606
+ scope: string().optional(),
20607
+ from: number(),
20608
+ to: number(),
20609
+ limit: number().int().positive().optional()
20610
+ }), array(LtsBucketSchema).readonly()), method(object({
20534
20611
  deviceId: number(),
20535
20612
  since: number(),
20536
20613
  until: number(),
@@ -21149,6 +21226,14 @@ var MotionSourceEnum = _enum([
21149
21226
  */
21150
21227
  var MotionSourcesSchema = array(MotionSourceEnum);
21151
21228
  /**
21229
+ * Which root detectors a camera runs. Deliberately the SAME vocabulary
21230
+ * post-analysis already tags every detection with (`DetectionSource`) rather
21231
+ * than a second spelling of the same two ideas — the value an operator picks
21232
+ * here is the value that comes back on the track, the overlay and the debug
21233
+ * row.
21234
+ */
21235
+ var DetectionSourcesSchema = array(DetectionSourceSchema);
21236
+ /**
21152
21237
  * Input shape for `pipeline-runner.reportMotion` cap method. Exported
21153
21238
  * so cap-side consumers (the orchestrator forward, the runner addon's
21154
21239
  * cap implementation, tests) can reuse the type instead of redeclaring
@@ -21299,6 +21384,35 @@ var RunnerCameraConfigSchema = object({
21299
21384
  * events; the orchestrator forwards to `reportMotion`.
21300
21385
  */
21301
21386
  motionSources: MotionSourcesSchema.default(["analyzer"]),
21387
+ /**
21388
+ * WHICH root detector runs for this camera. The detection counterpart of
21389
+ * {@link motionSources}, same shape and same discipline: a per-camera list
21390
+ * of sources the system understands, vendor-agnostic, owned here.
21391
+ *
21392
+ * - `pipeline` — this runner's own root step on decoded pixels (`steps`).
21393
+ * - `onboard` — the CAMERA's own detector, admitted only when it is named
21394
+ * here. Its boxes reach post-analysis on the same
21395
+ * `PipelineInferenceResult` payload as the pipeline's, so an onboard-born
21396
+ * track gets the same tracker, the same overlay and the same detail
21397
+ * subtree (face / plate / embedding).
21398
+ *
21399
+ * `['onboard']` alone is the REPLACEMENT: this runner's root step does not
21400
+ * run, and that is where a camera with a usable onboard detector buys back
21401
+ * its share of the accelerator. It saves the root inference, not the decode
21402
+ * — the detail subtree still needs pixels to cut crops from.
21403
+ *
21404
+ * A camera whose onboard detections carry no geometry cannot serve as a root
21405
+ * step at all (there is nothing to track), and the cap that knows this says
21406
+ * so on `native-object-detection.getOptions().geometry`. On Reolink that is
21407
+ * every battery camera: the boxes ride a sub-stream a battery camera never
21408
+ * attaches, so such a camera stays on `['pipeline']` and its onboard AI is
21409
+ * worth exactly what it already is — a wake signal with a class.
21410
+ *
21411
+ * Defaults to `['pipeline']`, which is byte-for-byte today's behaviour: a
21412
+ * vendor never turns a detector on for the operator, and neither does a
21413
+ * default.
21414
+ */
21415
+ detectionSources: DetectionSourcesSchema.default(["pipeline"]),
21302
21416
  pipelineEnabled: boolean().default(true),
21303
21417
  /** Ordered tree of video steps. Absent → runner skips video detection. */
21304
21418
  steps: array(PipelineStepInputSchema).readonly().optional(),
@@ -21480,7 +21594,47 @@ var RunnerLocalMetricsSchema = object({
21480
21594
  queueDepth: number(),
21481
21595
  frameLazy: FrameLazyMetricsSchema.optional()
21482
21596
  });
21597
+ /**
21598
+ * Pipeline Runner capability — runtime detection workhorse.
21599
+ *
21600
+ * One instance per node. Receives camera assignments from
21601
+ * `addon-pipeline-orchestrator`, subscribes to the local stream-broker for
21602
+ * decoded frames, drains motion + detection queues, calls the local
21603
+ * `motion-detection` and `pipeline-executor` capabilities, and emits typed
21604
+ * `pipeline.inference-result` and `detection.motion-analysis` events on
21605
+ * the bus.
21606
+ *
21607
+ * Distinct from `pipeline-orchestrator` (the hub-side load balancer) — the
21608
+ * runner has zero knowledge of other agents, no global state, and never
21609
+ * makes assignment decisions itself.
21610
+ */
21611
+ /**
21612
+ * Why a runner declined to look at a camera out of band. Named rather than a
21613
+ * bare `false`: a camera asleep on battery (D173), one attached elsewhere, one
21614
+ * already mid-session and one already bursting are four different facts, and a
21615
+ * caller told only "no" cannot tell a protection from a bug.
21616
+ */
21617
+ var OccupancyBurstRefusalSchema = _enum([
21618
+ "not-attached",
21619
+ "battery-asleep",
21620
+ "not-watching",
21621
+ "already-bursting"
21622
+ ]);
21483
21623
  method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mutation" }), method(object({ deviceId: number() }), object({ success: literal(true) }), { kind: "mutation" }), method(ReportMotionInputSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), RunnerLocalLoadSchema), method(_void(), RunnerLocalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()), method(_void(), array(number()).readonly()), method(object({
21624
+ deviceId: number(),
21625
+ /** How many frames to collect. The caller sizes this against what its
21626
+ * own confirmation needs; the runner clamps it to the field's range. */
21627
+ frames: number().int().positive().optional(),
21628
+ /** Why, for the log. A burst nobody can attribute is a cost nobody can
21629
+ * defend — the periodic one at least has a timer to point at. */
21630
+ reason: string()
21631
+ }), object({
21632
+ started: boolean(),
21633
+ refusedBecause: OccupancyBurstRefusalSchema.optional()
21634
+ }), {
21635
+ auth: "admin",
21636
+ kind: "mutation"
21637
+ }), method(object({
21484
21638
  handle: FrameHandleSchema,
21485
21639
  bbox: NativeCropBboxSchema,
21486
21640
  maxWidth: number().int().positive().optional(),
@@ -26949,7 +27103,7 @@ var NativeDetectionSchema = object({
26949
27103
  /** Firmware-provided confidence [0..1]. Reolink pushes don't carry it → undefined. */
26950
27104
  confidence: number().min(0).max(1).optional()
26951
27105
  });
26952
- object({
27106
+ var NativeObjectDetectionStatusSchema = object({
26953
27107
  /**
26954
27108
  * Last observed instance per class. Missing entries mean the class
26955
27109
  * is supported but nothing has been seen since the provider started.
@@ -26966,14 +27120,56 @@ object({
26966
27120
  supportedClasses: array(NativeObjectClassEnum).readonly(),
26967
27121
  /**
26968
27122
  * Whether forwarding of onboard AI detections is enabled for this device.
26969
- * Default FALSE (opt-in, cold-start) — onboard AI pushes are noisy/sparse and
26970
- * churn the tracker, so forwarding stays off until the operator enables it.
27123
+ *
27124
+ * Cold-start OFF on every camera. A vendor does not decide which detector a
27125
+ * camera runs — that is the operator's choice, and it is made where every
27126
+ * other detection choice is made. `geometry` on
27127
+ * {@link NativeObjectDetectionOptionsSchema} is how the form says what the
27128
+ * choice would buy this particular camera.
26971
27129
  */
26972
27130
  enabled: boolean()
26973
- }).extend({
27131
+ });
27132
+ /**
27133
+ * WHEN a camera's onboard detections carry geometry.
27134
+ *
27135
+ * - `boxed` — always. The provider holds a standing channel for the boxes, so
27136
+ * a detection is a trackable subject whenever the camera sees one.
27137
+ * - `while-streaming` — only while some stream of this camera is already open.
27138
+ * The boxes ride the video's own side-channel rather than a feed of their
27139
+ * own, so they cost nothing and they exist only when something is pulling.
27140
+ * On Reolink this is every BATTERY camera: a persistent second feed is real
27141
+ * radio drain, but a camera that is awake is awake precisely because
27142
+ * something is in front of it, and its stream is already being pulled.
27143
+ * - `flag-only` — never. The firmware ships the class and nothing else; the
27144
+ * pipeline needs geometry to make a subject, so such a push reaches the
27145
+ * tracker as motion and no further.
27146
+ *
27147
+ * Reported and not inferred, because the operator's question in front of the
27148
+ * picker is "what do I get", and these are three different answers (D14: the
27149
+ * derived form is only as honest as `getOptions`).
27150
+ */
27151
+ var NativeObjectGeometryEnum = _enum([
27152
+ "boxed",
27153
+ "while-streaming",
27154
+ "flag-only"
27155
+ ]);
27156
+ var NativeObjectDetectionOptionsSchema = object({
27157
+ /** Classes this firmware can detect — the same list the status reports. */
27158
+ supportedClasses: array(NativeObjectClassEnum).readonly(),
27159
+ /** What a detection from this camera carries. */
27160
+ geometry: NativeObjectGeometryEnum
27161
+ });
27162
+ var NativeObjectDetectionSettingsPatchSchema = object({ enabled: boolean().optional() });
27163
+ NativeObjectDetectionStatusSchema.extend({
26974
27164
  /** Required by createRuntimeStateBridge — epoch ms of last refresh. */
26975
27165
  lastFetchedAt: number() });
26976
- DeviceType.Camera, method(object({
27166
+ DeviceType.Camera, method(object({ deviceId: number() }), NativeObjectDetectionOptionsSchema), method(object({
27167
+ deviceId: number(),
27168
+ settings: NativeObjectDetectionSettingsPatchSchema
27169
+ }), _void(), {
27170
+ kind: "mutation",
27171
+ auth: "admin"
27172
+ }), method(object({
26977
27173
  deviceId: number(),
26978
27174
  enabled: boolean()
26979
27175
  }), _void(), {
@@ -30772,7 +30968,28 @@ var PerScopeBreakdownSchema = object({
30772
30968
  /** Total tracked objects in this scope (frame / zone / unzoned). */
30773
30969
  totalObjects: number().int().nonnegative(),
30774
30970
  /** Per-class count. Keys are macro class names (e.g. `person`, `car`). */
30775
- byClass: record(string(), number().int().nonnegative())
30971
+ byClass: record(string(), number().int().nonnegative()),
30972
+ /**
30973
+ * Of `totalObjects`, how many are STANDING — objects the census holds,
30974
+ * present but not going anywhere (a parked car, a bin, a statue).
30975
+ *
30976
+ * The operator's question about a scene has two halves and they answer
30977
+ * different things: "what is parked here" is stable for hours and must not
30978
+ * flap, while "who is walking through" is the thing an alert is about. One
30979
+ * number could only serve one of them, and `totalObjects` served the first
30980
+ * badly — a passer-by moved it and every occupancy rule saw a change.
30981
+ *
30982
+ * `standing + transient === totalObjects`, always. The total keeps its old
30983
+ * meaning exactly, so no rule written against it changes behaviour.
30984
+ *
30985
+ * ABSENT means UNKNOWN, never zero (D393): a snapshot from a node older than
30986
+ * this field cannot say, and a reader that folds absence in as 0 reports "no
30987
+ * parked objects here" about a camera whose lot is full.
30988
+ */
30989
+ standingObjects: number().int().nonnegative().optional(),
30990
+ /** Of `totalObjects`, how many are passing through. See `standingObjects` —
30991
+ * same absence rule. */
30992
+ transientObjects: number().int().nonnegative().optional()
30776
30993
  });
30777
30994
  var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
30778
30995
  zoneId: string(),
@@ -33711,12 +33928,24 @@ Object.freeze({
33711
33928
  addonId: null,
33712
33929
  access: "create"
33713
33930
  },
33931
+ "nativeObjectDetection.getOptions": {
33932
+ capName: "native-object-detection",
33933
+ capScope: "device",
33934
+ addonId: null,
33935
+ access: "view"
33936
+ },
33714
33937
  "nativeObjectDetection.setEnabled": {
33715
33938
  capName: "native-object-detection",
33716
33939
  capScope: "device",
33717
33940
  addonId: null,
33718
33941
  access: "create"
33719
33942
  },
33943
+ "nativeObjectDetection.setSettings": {
33944
+ capName: "native-object-detection",
33945
+ capScope: "device",
33946
+ addonId: null,
33947
+ access: "create"
33948
+ },
33720
33949
  "navigation.getFeatures": {
33721
33950
  capName: "navigation",
33722
33951
  capScope: "device",
@@ -33981,6 +34210,12 @@ Object.freeze({
33981
34210
  addonId: null,
33982
34211
  access: "view"
33983
34212
  },
34213
+ "notificationRules.getClipRetentionAsks": {
34214
+ capName: "notification-rules",
34215
+ capScope: "system",
34216
+ addonId: null,
34217
+ access: "view"
34218
+ },
33984
34219
  "notificationRules.getConditionCatalog": {
33985
34220
  capName: "notification-rules",
33986
34221
  capScope: "system",
@@ -34491,6 +34726,12 @@ Object.freeze({
34491
34726
  addonId: null,
34492
34727
  access: "create"
34493
34728
  },
34729
+ "pipelineAnalytics.readLongTermSeries": {
34730
+ capName: "pipeline-analytics",
34731
+ capScope: "device",
34732
+ addonId: null,
34733
+ access: "view"
34734
+ },
34494
34735
  "pipelineAnalytics.rebuildObjectEmbeddings": {
34495
34736
  capName: "pipeline-analytics",
34496
34737
  capScope: "device",
@@ -35163,6 +35404,12 @@ Object.freeze({
35163
35404
  addonId: null,
35164
35405
  access: "create"
35165
35406
  },
35407
+ "pipelineRunner.requestOccupancyBurst": {
35408
+ capName: "pipeline-runner",
35409
+ capScope: "system",
35410
+ addonId: null,
35411
+ access: "create"
35412
+ },
35166
35413
  "pipelineRunner.runDetailSubtree": {
35167
35414
  capName: "pipeline-runner",
35168
35415
  capScope: "system",
@@ -37943,11 +38190,21 @@ Object.freeze({
37943
38190
  form: "single",
37944
38191
  optional: false
37945
38192
  }],
38193
+ "nativeObjectDetection.getOptions": [{
38194
+ name: "deviceId",
38195
+ form: "single",
38196
+ optional: false
38197
+ }],
37946
38198
  "nativeObjectDetection.setEnabled": [{
37947
38199
  name: "deviceId",
37948
38200
  form: "single",
37949
38201
  optional: false
37950
38202
  }],
38203
+ "nativeObjectDetection.setSettings": [{
38204
+ name: "deviceId",
38205
+ form: "single",
38206
+ optional: false
38207
+ }],
37951
38208
  "navigation.getFeatures": [{
37952
38209
  name: "deviceId",
37953
38210
  form: "single",
@@ -38297,6 +38554,11 @@ Object.freeze({
38297
38554
  form: "single",
38298
38555
  optional: false
38299
38556
  }],
38557
+ "pipelineAnalytics.readLongTermSeries": [{
38558
+ name: "deviceId",
38559
+ form: "single",
38560
+ optional: false
38561
+ }],
38300
38562
  "pipelineAnalytics.rebuildObjectEmbeddings": [{
38301
38563
  name: "deviceId",
38302
38564
  form: "single",
@@ -38487,6 +38749,11 @@ Object.freeze({
38487
38749
  form: "single",
38488
38750
  optional: false
38489
38751
  }],
38752
+ "pipelineRunner.requestOccupancyBurst": [{
38753
+ name: "deviceId",
38754
+ form: "single",
38755
+ optional: false
38756
+ }],
38490
38757
  "pipelineRunner.runDetailSubtree": [{
38491
38758
  name: "deviceId",
38492
38759
  form: "single",
@@ -5400,7 +5400,7 @@ var ZodIssueCode = {
5400
5400
  var ZodFirstPartyTypeKind;
5401
5401
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5402
5402
  //#endregion
5403
- //#region ../types/dist/sleep-DEuj7E3j.mjs
5403
+ //#region ../types/dist/sleep-BDR76Ykr.mjs
5404
5404
  /**
5405
5405
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5406
5406
  * window to float samples (D455).
@@ -17926,6 +17926,48 @@ var NcAlarmConfigSchema = object({
17926
17926
  settings: NcAlarmSettingsSchema,
17927
17927
  coverage: array(NcAlarmModeCoverageSchema)
17928
17928
  });
17929
+ /**
17930
+ * ONE rule's demand on ONE camera's clip ring.
17931
+ *
17932
+ * A rule, not a camera: the broker takes the MAX over the asks that reach a
17933
+ * camera, so the answer stays a faithful description of the rules and the
17934
+ * bounding (the byte budget, the broker's own ceiling) stays where the cost
17935
+ * lives. A camera that appears in no ask is being told **nothing** — which is
17936
+ * a different thing from never having been told, and only the envelope
17937
+ * (`rulesLoaded`) can tell those apart.
17938
+ */
17939
+ var NcClipRetentionAskSchema = object({
17940
+ /**
17941
+ * The camera this ask is about. **Absent = every camera**, which is what a
17942
+ * rule with no `conditions.devices` means: it can fire anywhere, so it is
17943
+ * asking everywhere.
17944
+ */
17945
+ deviceId: number().int().nonnegative().optional(),
17946
+ /** Seconds of history the rule needs held for it. Never zero — a rule that
17947
+ * needs nothing produces no ask at all. */
17948
+ seconds: number().min(0).max(60),
17949
+ /** The profile the rule cuts from, when it named one. Absent = the cheapest
17950
+ * assigned, which is what the cut defaults to. */
17951
+ profile: CamProfileSchema.optional(),
17952
+ /** Who is asking — so a retention the operator did not expect names a rule. */
17953
+ ruleId: string(),
17954
+ ruleName: string(),
17955
+ /** Why this many seconds: the rule's own window, or the occupancy ceiling. */
17956
+ reason: _enum(["window", "occupancy"])
17957
+ });
17958
+ /**
17959
+ * The whole answer, with the one bit that keeps absence from reading as zero.
17960
+ *
17961
+ * `rulesLoaded` is false until the rule ledger has completed a load at least
17962
+ * once. A centre that is up but has not read its rules yet answers with an
17963
+ * empty ask list that means nothing at all — applying it would drop every
17964
+ * camera's ring to zero for the first events after a restart, which is the
17965
+ * footage this mechanism exists to keep.
17966
+ */
17967
+ var NcClipRetentionPlanSchema = object({
17968
+ rulesLoaded: boolean(),
17969
+ asks: array(NcClipRetentionAskSchema).readonly()
17970
+ });
17929
17971
  method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), method(object({ ruleId: string() }), object({ rule: NcRuleSchema.nullable() }), { auth: "admin" }), method(object({ rule: NcRuleInputSchema }), object({ rule: NcRuleSchema }), {
17930
17972
  kind: "mutation",
17931
17973
  auth: "admin",
@@ -17946,7 +17988,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
17946
17988
  }), object({ success: literal(true) }), {
17947
17989
  kind: "mutation",
17948
17990
  auth: "admin"
17949
- }), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
17991
+ }), method(object({}), NcClipRetentionPlanSchema, { auth: "admin" }), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
17950
17992
  deviceId: number().int(),
17951
17993
  muted: boolean()
17952
17994
  }), object({ success: literal(true) }), {
@@ -20480,6 +20522,33 @@ var RunReplayFrameProcessorResultSchema = object({ tracks: array(object({
20480
20522
  * solid is this track", cheaper than re-deriving it from a trajectory. */
20481
20523
  framesMatched: number().int()
20482
20524
  })).readonly() });
20525
+ /**
20526
+ * The long-term series a camera produces. A closed union so a typo cannot
20527
+ * invent one, and the ONE declaration of it — the addon's `AnalyticsLtsSeries`
20528
+ * is an alias of this, not a second list.
20529
+ */
20530
+ var AnalyticsLtsSeriesSchema = _enum([
20531
+ "motion",
20532
+ "audio-dbfs",
20533
+ "battery",
20534
+ "occupancy"
20535
+ ]);
20536
+ /**
20537
+ * One closed 5-minute bucket of a long-term series.
20538
+ *
20539
+ * `samples` is how many readings the bucket folded — it is the ACTIVITY for a
20540
+ * series like `motion`, and the confidence for one like `occupancy`. A bucket
20541
+ * that exists at all was measured; a bucket that is absent was not, which is a
20542
+ * different claim from zero and the reason this is a sparse series.
20543
+ */
20544
+ var LtsBucketSchema = object({
20545
+ scope: string(),
20546
+ bucketStart: number().int(),
20547
+ samples: number().int().nonnegative(),
20548
+ sum: number(),
20549
+ min: number(),
20550
+ max: number()
20551
+ });
20483
20552
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
20484
20553
  deviceId: number(),
20485
20554
  trackId: string()
@@ -20526,6 +20595,14 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20526
20595
  kinds: array(string()).optional(),
20527
20596
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
20528
20597
  }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
20598
+ deviceId: number(),
20599
+ series: AnalyticsLtsSeriesSchema,
20600
+ /** Omit for EVERY scope of this series in the range. */
20601
+ scope: string().optional(),
20602
+ from: number(),
20603
+ to: number(),
20604
+ limit: number().int().positive().optional()
20605
+ }), array(LtsBucketSchema).readonly()), method(object({
20529
20606
  deviceId: number(),
20530
20607
  since: number(),
20531
20608
  until: number(),
@@ -21144,6 +21221,14 @@ var MotionSourceEnum = _enum([
21144
21221
  */
21145
21222
  var MotionSourcesSchema = array(MotionSourceEnum);
21146
21223
  /**
21224
+ * Which root detectors a camera runs. Deliberately the SAME vocabulary
21225
+ * post-analysis already tags every detection with (`DetectionSource`) rather
21226
+ * than a second spelling of the same two ideas — the value an operator picks
21227
+ * here is the value that comes back on the track, the overlay and the debug
21228
+ * row.
21229
+ */
21230
+ var DetectionSourcesSchema = array(DetectionSourceSchema);
21231
+ /**
21147
21232
  * Input shape for `pipeline-runner.reportMotion` cap method. Exported
21148
21233
  * so cap-side consumers (the orchestrator forward, the runner addon's
21149
21234
  * cap implementation, tests) can reuse the type instead of redeclaring
@@ -21294,6 +21379,35 @@ var RunnerCameraConfigSchema = object({
21294
21379
  * events; the orchestrator forwards to `reportMotion`.
21295
21380
  */
21296
21381
  motionSources: MotionSourcesSchema.default(["analyzer"]),
21382
+ /**
21383
+ * WHICH root detector runs for this camera. The detection counterpart of
21384
+ * {@link motionSources}, same shape and same discipline: a per-camera list
21385
+ * of sources the system understands, vendor-agnostic, owned here.
21386
+ *
21387
+ * - `pipeline` — this runner's own root step on decoded pixels (`steps`).
21388
+ * - `onboard` — the CAMERA's own detector, admitted only when it is named
21389
+ * here. Its boxes reach post-analysis on the same
21390
+ * `PipelineInferenceResult` payload as the pipeline's, so an onboard-born
21391
+ * track gets the same tracker, the same overlay and the same detail
21392
+ * subtree (face / plate / embedding).
21393
+ *
21394
+ * `['onboard']` alone is the REPLACEMENT: this runner's root step does not
21395
+ * run, and that is where a camera with a usable onboard detector buys back
21396
+ * its share of the accelerator. It saves the root inference, not the decode
21397
+ * — the detail subtree still needs pixels to cut crops from.
21398
+ *
21399
+ * A camera whose onboard detections carry no geometry cannot serve as a root
21400
+ * step at all (there is nothing to track), and the cap that knows this says
21401
+ * so on `native-object-detection.getOptions().geometry`. On Reolink that is
21402
+ * every battery camera: the boxes ride a sub-stream a battery camera never
21403
+ * attaches, so such a camera stays on `['pipeline']` and its onboard AI is
21404
+ * worth exactly what it already is — a wake signal with a class.
21405
+ *
21406
+ * Defaults to `['pipeline']`, which is byte-for-byte today's behaviour: a
21407
+ * vendor never turns a detector on for the operator, and neither does a
21408
+ * default.
21409
+ */
21410
+ detectionSources: DetectionSourcesSchema.default(["pipeline"]),
21297
21411
  pipelineEnabled: boolean().default(true),
21298
21412
  /** Ordered tree of video steps. Absent → runner skips video detection. */
21299
21413
  steps: array(PipelineStepInputSchema).readonly().optional(),
@@ -21475,7 +21589,47 @@ var RunnerLocalMetricsSchema = object({
21475
21589
  queueDepth: number(),
21476
21590
  frameLazy: FrameLazyMetricsSchema.optional()
21477
21591
  });
21592
+ /**
21593
+ * Pipeline Runner capability — runtime detection workhorse.
21594
+ *
21595
+ * One instance per node. Receives camera assignments from
21596
+ * `addon-pipeline-orchestrator`, subscribes to the local stream-broker for
21597
+ * decoded frames, drains motion + detection queues, calls the local
21598
+ * `motion-detection` and `pipeline-executor` capabilities, and emits typed
21599
+ * `pipeline.inference-result` and `detection.motion-analysis` events on
21600
+ * the bus.
21601
+ *
21602
+ * Distinct from `pipeline-orchestrator` (the hub-side load balancer) — the
21603
+ * runner has zero knowledge of other agents, no global state, and never
21604
+ * makes assignment decisions itself.
21605
+ */
21606
+ /**
21607
+ * Why a runner declined to look at a camera out of band. Named rather than a
21608
+ * bare `false`: a camera asleep on battery (D173), one attached elsewhere, one
21609
+ * already mid-session and one already bursting are four different facts, and a
21610
+ * caller told only "no" cannot tell a protection from a bug.
21611
+ */
21612
+ var OccupancyBurstRefusalSchema = _enum([
21613
+ "not-attached",
21614
+ "battery-asleep",
21615
+ "not-watching",
21616
+ "already-bursting"
21617
+ ]);
21478
21618
  method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mutation" }), method(object({ deviceId: number() }), object({ success: literal(true) }), { kind: "mutation" }), method(ReportMotionInputSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), RunnerLocalLoadSchema), method(_void(), RunnerLocalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()), method(_void(), array(number()).readonly()), method(object({
21619
+ deviceId: number(),
21620
+ /** How many frames to collect. The caller sizes this against what its
21621
+ * own confirmation needs; the runner clamps it to the field's range. */
21622
+ frames: number().int().positive().optional(),
21623
+ /** Why, for the log. A burst nobody can attribute is a cost nobody can
21624
+ * defend — the periodic one at least has a timer to point at. */
21625
+ reason: string()
21626
+ }), object({
21627
+ started: boolean(),
21628
+ refusedBecause: OccupancyBurstRefusalSchema.optional()
21629
+ }), {
21630
+ auth: "admin",
21631
+ kind: "mutation"
21632
+ }), method(object({
21479
21633
  handle: FrameHandleSchema,
21480
21634
  bbox: NativeCropBboxSchema,
21481
21635
  maxWidth: number().int().positive().optional(),
@@ -26944,7 +27098,7 @@ var NativeDetectionSchema = object({
26944
27098
  /** Firmware-provided confidence [0..1]. Reolink pushes don't carry it → undefined. */
26945
27099
  confidence: number().min(0).max(1).optional()
26946
27100
  });
26947
- object({
27101
+ var NativeObjectDetectionStatusSchema = object({
26948
27102
  /**
26949
27103
  * Last observed instance per class. Missing entries mean the class
26950
27104
  * is supported but nothing has been seen since the provider started.
@@ -26961,14 +27115,56 @@ object({
26961
27115
  supportedClasses: array(NativeObjectClassEnum).readonly(),
26962
27116
  /**
26963
27117
  * Whether forwarding of onboard AI detections is enabled for this device.
26964
- * Default FALSE (opt-in, cold-start) — onboard AI pushes are noisy/sparse and
26965
- * churn the tracker, so forwarding stays off until the operator enables it.
27118
+ *
27119
+ * Cold-start OFF on every camera. A vendor does not decide which detector a
27120
+ * camera runs — that is the operator's choice, and it is made where every
27121
+ * other detection choice is made. `geometry` on
27122
+ * {@link NativeObjectDetectionOptionsSchema} is how the form says what the
27123
+ * choice would buy this particular camera.
26966
27124
  */
26967
27125
  enabled: boolean()
26968
- }).extend({
27126
+ });
27127
+ /**
27128
+ * WHEN a camera's onboard detections carry geometry.
27129
+ *
27130
+ * - `boxed` — always. The provider holds a standing channel for the boxes, so
27131
+ * a detection is a trackable subject whenever the camera sees one.
27132
+ * - `while-streaming` — only while some stream of this camera is already open.
27133
+ * The boxes ride the video's own side-channel rather than a feed of their
27134
+ * own, so they cost nothing and they exist only when something is pulling.
27135
+ * On Reolink this is every BATTERY camera: a persistent second feed is real
27136
+ * radio drain, but a camera that is awake is awake precisely because
27137
+ * something is in front of it, and its stream is already being pulled.
27138
+ * - `flag-only` — never. The firmware ships the class and nothing else; the
27139
+ * pipeline needs geometry to make a subject, so such a push reaches the
27140
+ * tracker as motion and no further.
27141
+ *
27142
+ * Reported and not inferred, because the operator's question in front of the
27143
+ * picker is "what do I get", and these are three different answers (D14: the
27144
+ * derived form is only as honest as `getOptions`).
27145
+ */
27146
+ var NativeObjectGeometryEnum = _enum([
27147
+ "boxed",
27148
+ "while-streaming",
27149
+ "flag-only"
27150
+ ]);
27151
+ var NativeObjectDetectionOptionsSchema = object({
27152
+ /** Classes this firmware can detect — the same list the status reports. */
27153
+ supportedClasses: array(NativeObjectClassEnum).readonly(),
27154
+ /** What a detection from this camera carries. */
27155
+ geometry: NativeObjectGeometryEnum
27156
+ });
27157
+ var NativeObjectDetectionSettingsPatchSchema = object({ enabled: boolean().optional() });
27158
+ NativeObjectDetectionStatusSchema.extend({
26969
27159
  /** Required by createRuntimeStateBridge — epoch ms of last refresh. */
26970
27160
  lastFetchedAt: number() });
26971
- DeviceType.Camera, method(object({
27161
+ DeviceType.Camera, method(object({ deviceId: number() }), NativeObjectDetectionOptionsSchema), method(object({
27162
+ deviceId: number(),
27163
+ settings: NativeObjectDetectionSettingsPatchSchema
27164
+ }), _void(), {
27165
+ kind: "mutation",
27166
+ auth: "admin"
27167
+ }), method(object({
26972
27168
  deviceId: number(),
26973
27169
  enabled: boolean()
26974
27170
  }), _void(), {
@@ -30767,7 +30963,28 @@ var PerScopeBreakdownSchema = object({
30767
30963
  /** Total tracked objects in this scope (frame / zone / unzoned). */
30768
30964
  totalObjects: number().int().nonnegative(),
30769
30965
  /** Per-class count. Keys are macro class names (e.g. `person`, `car`). */
30770
- byClass: record(string(), number().int().nonnegative())
30966
+ byClass: record(string(), number().int().nonnegative()),
30967
+ /**
30968
+ * Of `totalObjects`, how many are STANDING — objects the census holds,
30969
+ * present but not going anywhere (a parked car, a bin, a statue).
30970
+ *
30971
+ * The operator's question about a scene has two halves and they answer
30972
+ * different things: "what is parked here" is stable for hours and must not
30973
+ * flap, while "who is walking through" is the thing an alert is about. One
30974
+ * number could only serve one of them, and `totalObjects` served the first
30975
+ * badly — a passer-by moved it and every occupancy rule saw a change.
30976
+ *
30977
+ * `standing + transient === totalObjects`, always. The total keeps its old
30978
+ * meaning exactly, so no rule written against it changes behaviour.
30979
+ *
30980
+ * ABSENT means UNKNOWN, never zero (D393): a snapshot from a node older than
30981
+ * this field cannot say, and a reader that folds absence in as 0 reports "no
30982
+ * parked objects here" about a camera whose lot is full.
30983
+ */
30984
+ standingObjects: number().int().nonnegative().optional(),
30985
+ /** Of `totalObjects`, how many are passing through. See `standingObjects` —
30986
+ * same absence rule. */
30987
+ transientObjects: number().int().nonnegative().optional()
30771
30988
  });
30772
30989
  var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
30773
30990
  zoneId: string(),
@@ -33706,12 +33923,24 @@ Object.freeze({
33706
33923
  addonId: null,
33707
33924
  access: "create"
33708
33925
  },
33926
+ "nativeObjectDetection.getOptions": {
33927
+ capName: "native-object-detection",
33928
+ capScope: "device",
33929
+ addonId: null,
33930
+ access: "view"
33931
+ },
33709
33932
  "nativeObjectDetection.setEnabled": {
33710
33933
  capName: "native-object-detection",
33711
33934
  capScope: "device",
33712
33935
  addonId: null,
33713
33936
  access: "create"
33714
33937
  },
33938
+ "nativeObjectDetection.setSettings": {
33939
+ capName: "native-object-detection",
33940
+ capScope: "device",
33941
+ addonId: null,
33942
+ access: "create"
33943
+ },
33715
33944
  "navigation.getFeatures": {
33716
33945
  capName: "navigation",
33717
33946
  capScope: "device",
@@ -33976,6 +34205,12 @@ Object.freeze({
33976
34205
  addonId: null,
33977
34206
  access: "view"
33978
34207
  },
34208
+ "notificationRules.getClipRetentionAsks": {
34209
+ capName: "notification-rules",
34210
+ capScope: "system",
34211
+ addonId: null,
34212
+ access: "view"
34213
+ },
33979
34214
  "notificationRules.getConditionCatalog": {
33980
34215
  capName: "notification-rules",
33981
34216
  capScope: "system",
@@ -34486,6 +34721,12 @@ Object.freeze({
34486
34721
  addonId: null,
34487
34722
  access: "create"
34488
34723
  },
34724
+ "pipelineAnalytics.readLongTermSeries": {
34725
+ capName: "pipeline-analytics",
34726
+ capScope: "device",
34727
+ addonId: null,
34728
+ access: "view"
34729
+ },
34489
34730
  "pipelineAnalytics.rebuildObjectEmbeddings": {
34490
34731
  capName: "pipeline-analytics",
34491
34732
  capScope: "device",
@@ -35158,6 +35399,12 @@ Object.freeze({
35158
35399
  addonId: null,
35159
35400
  access: "create"
35160
35401
  },
35402
+ "pipelineRunner.requestOccupancyBurst": {
35403
+ capName: "pipeline-runner",
35404
+ capScope: "system",
35405
+ addonId: null,
35406
+ access: "create"
35407
+ },
35161
35408
  "pipelineRunner.runDetailSubtree": {
35162
35409
  capName: "pipeline-runner",
35163
35410
  capScope: "system",
@@ -37938,11 +38185,21 @@ Object.freeze({
37938
38185
  form: "single",
37939
38186
  optional: false
37940
38187
  }],
38188
+ "nativeObjectDetection.getOptions": [{
38189
+ name: "deviceId",
38190
+ form: "single",
38191
+ optional: false
38192
+ }],
37941
38193
  "nativeObjectDetection.setEnabled": [{
37942
38194
  name: "deviceId",
37943
38195
  form: "single",
37944
38196
  optional: false
37945
38197
  }],
38198
+ "nativeObjectDetection.setSettings": [{
38199
+ name: "deviceId",
38200
+ form: "single",
38201
+ optional: false
38202
+ }],
37946
38203
  "navigation.getFeatures": [{
37947
38204
  name: "deviceId",
37948
38205
  form: "single",
@@ -38292,6 +38549,11 @@ Object.freeze({
38292
38549
  form: "single",
38293
38550
  optional: false
38294
38551
  }],
38552
+ "pipelineAnalytics.readLongTermSeries": [{
38553
+ name: "deviceId",
38554
+ form: "single",
38555
+ optional: false
38556
+ }],
38295
38557
  "pipelineAnalytics.rebuildObjectEmbeddings": [{
38296
38558
  name: "deviceId",
38297
38559
  form: "single",
@@ -38482,6 +38744,11 @@ Object.freeze({
38482
38744
  form: "single",
38483
38745
  optional: false
38484
38746
  }],
38747
+ "pipelineRunner.requestOccupancyBurst": [{
38748
+ name: "deviceId",
38749
+ form: "single",
38750
+ optional: false
38751
+ }],
38485
38752
  "pipelineRunner.runDetailSubtree": [{
38486
38753
  name: "deviceId",
38487
38754
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-mqtt-broker",
3
- "version": "1.2.102",
3
+ "version": "1.2.103",
4
4
  "description": "MQTT broker registry addon for CamStack — manages external broker entries + an optional embedded aedes broker. Consumers spin up their own `mqtt.js` clients via the `mqtt-broker` cap.",
5
5
  "keywords": [
6
6
  "camstack",