@camstack/addon-provider-dreo 0.2.89 → 0.2.92

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.
Files changed (3) hide show
  1. package/dist/addon.js +205 -18
  2. package/dist/addon.mjs +205 -18
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -35,7 +35,7 @@ var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["modu
35
35
  //#endregion
36
36
  let crypto$1 = require("crypto");
37
37
  let events = require("events");
38
- //#region ../types/dist/event-category-CnLqLOKs.mjs
38
+ //#region ../types/dist/event-category-DAXzJeTX.mjs
39
39
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
40
40
  EventCategory["SystemBoot"] = "system.boot";
41
41
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -627,7 +627,6 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
627
627
  * issue. Payload `PipelineAnalyticsPlateGalleryChangedPayload`.
628
628
  */
629
629
  EventCategory["PipelineAnalyticsPlateGalleryChanged"] = "pipeline-analytics.plate-gallery-changed";
630
- EventCategory["FrigateLiveEvent"] = "frigate.live-event";
631
630
  EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
632
631
  /**
633
632
  * Stream-broker health watchdog. Per-broker (deviceId/profile) emission.
@@ -12661,7 +12660,7 @@ DeviceType.Camera;
12661
12660
  var SourceInfoSchema = object({
12662
12661
  /** Live dispatch key — mutable when the source system allows rename. */
12663
12662
  id: string(),
12664
- /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'frigate'. */
12663
+ /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'onvif'. */
12665
12664
  system: string(),
12666
12665
  /** Immutable upstream identifier when available (HA `unique_id`, … ).
12667
12666
  * Used to detect rename when `id` changes. */
@@ -12674,7 +12673,7 @@ var SourceInfoSchema = object({
12674
12673
  /**
12675
12674
  * Build the synthetic SourceInfo every existing provider falls back to
12676
12675
  * when no upstream value has been persisted. Keeps non-migrated providers
12677
- * (Reolink / Hikvision / ONVIF / Frigate / RTSP) functional without code
12676
+ * (Reolink / Hikvision / ONVIF / RTSP) functional without code
12678
12677
  * changes — `id` reuses the CamStack stableId, `system` reuses the addon
12679
12678
  * id. Real upstream identity replaces this once a provider migrates and
12680
12679
  * calls `updateSourceInfo()` with concrete values.
@@ -13353,7 +13352,7 @@ var deviceProviderCapability = {
13353
13352
  * - `device-management.router.ts` (deleted in Phase 2)
13354
13353
  * - `device-ops.router.ts` (compat layer — deleted; device-provider ops absorbed here)
13355
13354
  *
13356
- * All device provider addons (rtsp, onvif, frigate, …) are hub-local: they may
13355
+ * All device provider addons (rtsp, onvif, …) are hub-local: they may
13357
13356
  * fork into separate processes but never run on remote cluster agents. Therefore:
13358
13357
  * - No nodeId routing needed — this is a pure hub singleton.
13359
13358
  * - The hub's DeviceRegistry is the single source of truth for all live devices.
@@ -17401,7 +17400,7 @@ var NcRuleInputSchema = object({
17401
17400
  * The measured seven-person arrival on device 590 spans 110 s with every
17402
17401
  * internal gap under 30 s. A 12 s fixed window cuts it into three groups; an
17403
17402
  * idle cutoff holds it as one and ends it when the arrival actually ends.
17404
- * 30 is Frigate's shipped value for the same decision.
17403
+ * 30 s is a widely shipped value for the same decision.
17405
17404
  *
17406
17405
  * ### What it replaces
17407
17406
  *
@@ -19483,6 +19482,13 @@ var ZoneCrossingSchema = object({
19483
19482
  /** Zone display name at crossing time (falls back to the id). */
19484
19483
  zoneName: string().optional()
19485
19484
  });
19485
+ /** One zone a box was stamped with, and how much of the BOX fell inside it. */
19486
+ var ZoneOverlapSchema = object({
19487
+ zoneId: string(),
19488
+ zoneName: string().optional(),
19489
+ /** Percentage of the BOX area inside the zone, 0–100. */
19490
+ overlapPct: number()
19491
+ });
19486
19492
  var ObjectEventSchema = object({
19487
19493
  ...BaseEventFields,
19488
19494
  kind: literal("object"),
@@ -19507,6 +19513,35 @@ var ObjectEventSchema = object({
19507
19513
  bbox: BoundingBoxSchema.optional(),
19508
19514
  /** Heavy JSON — omitted in slim projection. */
19509
19515
  zones: array(string()).readonly().optional(),
19516
+ /**
19517
+ * How far inside each stamped zone the box actually was, as a percentage of
19518
+ * the BOX area.
19519
+ *
19520
+ * Membership alone cannot express "at least a third of it inside", which is
19521
+ * the bar an operator argues with — and the number already existed: the
19522
+ * engine computed it, the pipeline logged it, and then discarded it. It
19523
+ * matters most on the events that carry no track, where a rule has nothing
19524
+ * else to judge by.
19525
+ */
19526
+ zoneOverlaps: array(ZoneOverlapSchema).readonly().optional(),
19527
+ /**
19528
+ * Present when this event describes a detection the TRACKER refused, so it
19529
+ * has no track and never will.
19530
+ *
19531
+ * A zone-scoped immediate rule asks "is there an animal in the flowerbed
19532
+ * NOW?"; tracking answers a different question ("is this the same subject as
19533
+ * before?") whose confirmation threshold presumes continuity. On 2026-09-09 a
19534
+ * dog at 0.7729, 100% inside a watched zone, was refused three times in six
19535
+ * seconds with three different candidate ids — seven analysed frames in the
19536
+ * window, the subject in a handful of them, metres apart — and produced no
19537
+ * event at all.
19538
+ *
19539
+ * It is NAMED rather than merely implied by a missing `trackId`: an event
19540
+ * with no track and no reason is indistinguishable from a track whose media
19541
+ * went missing, and a consumer that asks this one for a best shot or a
19542
+ * history is asking for something that never existed.
19543
+ */
19544
+ spawnRefused: string().optional(),
19510
19545
  /** Omitted in slim projection. */
19511
19546
  state: TrackStateSchema.optional(),
19512
19547
  /**
@@ -20580,6 +20615,60 @@ var NativeCropResultSchema = object({
20580
20615
  */
20581
20616
  tier: _enum(["native", "ram-fullframe"]).optional()
20582
20617
  });
20618
+ /**
20619
+ * What a PARKED frame is for — the `kind` half of the `(deviceId, trackId,
20620
+ * kind)` key ([D422](../../../docs/decisions/adr-0422-a-relevant-frame-is-parked-when-it-becomes-relevant.md)).
20621
+ *
20622
+ * - `keyFrame` — the track's clean best FULL frame at native width (capped by
20623
+ * the caller's `maxWidth`). What `keyFrame` + `keyFrameSmall` are written
20624
+ * from, and what the detail plane's full-frame rung cuts from.
20625
+ * - `thumbnail` — the best-shot subject slab: the caller's 16:9 central-square
20626
+ * window, cut by the runner at uncapped native resolution.
20627
+ *
20628
+ * The runner never interprets `trackId`; it is an opaque key the hub owns.
20629
+ */
20630
+ var ParkedFrameKindSchema = _enum(["keyFrame", "thumbnail"]);
20631
+ /**
20632
+ * Why a park did NOT happen. Every one is logged on the runner with
20633
+ * `tags: { deviceId }` and the handle's measured age, because a handle already
20634
+ * gone at park time IS the defect the 150–500 ms window exists to prevent —
20635
+ * never a reason to retry in a loop.
20636
+ *
20637
+ * - `handle-gone` — the native surface no longer resolves this handle (never
20638
+ * registered, past the lease bound, or the worker released it). `detail`
20639
+ * carries the runner's own miss reason.
20640
+ * - `encode-failed` — pixels resolved and the JPEG encode threw.
20641
+ */
20642
+ var ParkRefusalSchema = _enum(["handle-gone", "encode-failed"]);
20643
+ /** Answer to `parkTrackFrame` — a discriminated union, so "not parked" always names why. */
20644
+ var ParkTrackFrameResultSchema = discriminatedUnion("parked", [object({
20645
+ parked: literal(true),
20646
+ /** The node holding the parcel — what a later retrieval must be pinned to. */
20647
+ nodeId: string(),
20648
+ width: number().int().positive(),
20649
+ height: number().int().positive(),
20650
+ /** JPEG bytes the parcel costs on the node. */
20651
+ bytes: number().int().nonnegative(),
20652
+ /** Whether this park replaced a parcel already held for the key. */
20653
+ replaced: boolean()
20654
+ }), object({
20655
+ parked: literal(false),
20656
+ nodeId: string(),
20657
+ reason: ParkRefusalSchema,
20658
+ /** Age of the handle at the park, on the runner's clock; null if unknown. */
20659
+ handleAgeMs: number().nullable(),
20660
+ detail: string().optional()
20661
+ })]);
20662
+ /** A retrieved parcel — the runner's own JPEG, base64 for the wire. */
20663
+ var ParkedTrackFrameSchema = object({
20664
+ jpeg: string(),
20665
+ width: number().int().positive(),
20666
+ height: number().int().positive(),
20667
+ /** The frame instant the parcel shows (the caller's clock, echoed back). */
20668
+ timestamp: number(),
20669
+ /** When the node stored it (the node's clock). */
20670
+ parkedAt: number()
20671
+ });
20583
20672
  /** Parent detection context passed to `runDetailSubtree` — the crop's
20584
20673
  * originating detection, in FRAME-space coordinates. Reuses
20585
20674
  * `NativeCropBboxSchema`'s `{x,y,w,h}` shape (same numeric fields; here
@@ -20969,7 +21058,7 @@ var RunnerCameraConfigSchema = object({
20969
21058
  * - `analyzer`: ML/frame-diff motion detection (motion-wasm or other
20970
21059
  * analyzer providers) running on decoded frames in this runner.
20971
21060
  * - `onboard`: camera-native motion pushes (Reolink Baichuan, ONVIF
20972
- * analytics, Frigate motion). The provider emits ProviderMotion
21061
+ * analytics). The provider emits ProviderMotion
20973
21062
  * events; the orchestrator forwards to `reportMotion`.
20974
21063
  */
20975
21064
  motionSources: MotionSourcesSchema.default(["analyzer"]),
@@ -21211,7 +21300,26 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
21211
21300
  className: string(),
21212
21301
  /** Camera the pixels came from. Diagnostics + log tags ONLY — never routing. */
21213
21302
  sourceDeviceId: number()
21214
- }), RunStatelessStepResultSchema, { kind: "mutation" });
21303
+ }), RunStatelessStepResultSchema, { kind: "mutation" }), method(object({
21304
+ deviceId: number(),
21305
+ /** Opaque to the runner — the hub's track id. */
21306
+ trackId: string(),
21307
+ kind: ParkedFrameKindSchema,
21308
+ handle: FrameHandleSchema,
21309
+ /** Normalized [0,1] window to cut. `{0,0,1,1}` for a full frame. */
21310
+ bbox: NativeCropBboxSchema,
21311
+ /** Width cap for a full-frame park (the `keyFrame` native cap). */
21312
+ maxWidth: number().int().positive().optional(),
21313
+ /** The frame instant (caller's clock) — orders parks for one key. */
21314
+ timestamp: number()
21315
+ }), ParkTrackFrameResultSchema, { kind: "mutation" }), method(object({
21316
+ deviceId: number(),
21317
+ trackId: string(),
21318
+ kind: ParkedFrameKindSchema
21319
+ }), ParkedTrackFrameSchema.nullable()), method(object({
21320
+ deviceId: number(),
21321
+ trackId: string()
21322
+ }), object({ released: number().int().nonnegative() }), { kind: "mutation" });
21215
21323
  var CameraPipelineConfigSchema = object({
21216
21324
  engine: PipelineEngineChoiceSchema.optional(),
21217
21325
  steps: array(PipelineStepInputSchema).readonly(),
@@ -24281,8 +24389,8 @@ onStatusChanged: { data: object({
24281
24389
  /**
24282
24390
  * Runtime-state slice — every provider that registers this cap
24283
24391
  * stores the same shape under `device.runtimeState[battery]`.
24284
- * Cross-provider uniformity: a Reolink Argus, a Frigate sensor
24285
- * proxy, an ONVIF battery cam all read/write the same keys.
24392
+ * Cross-provider uniformity: a Reolink Argus, a sensor proxy,
24393
+ * an ONVIF battery cam all read/write the same keys.
24286
24394
  * Consumers (BatteryBadge, snapshot wrapper sleep gate) read once
24287
24395
  * via `device.runtimeState.getCapState('battery')` regardless of
24288
24396
  * the underlying driver.
@@ -24415,7 +24523,7 @@ DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _v
24415
24523
  *
24416
24524
  * Implementations:
24417
24525
  * - Each camera-providing addon registers this cap natively per
24418
- * device (Reolink, Hikvision, RTSP, Frigate, ONVIF self).
24526
+ * device (Reolink, Hikvision, RTSP, ONVIF self).
24419
24527
  * - Returns a snapshot from the persisted device config blob — no
24420
24528
  * extra round-trip to the camera, no probing.
24421
24529
  * - The cap is admin-only (`auth: 'admin'`) — credentials must not
@@ -26515,7 +26623,7 @@ var AvailableIntegrationTypeSchema = object({
26515
26623
  * Which integration-marker cap the addon declared, so the wizard can
26516
26624
  * branch on CAP — never on addon name. `device-adoption` integrations
26517
26625
  * (Home Assistant, …) route through the broker step (Approach A);
26518
- * `device-provider` integrations (Reolink/Frigate/ONVIF) keep the
26626
+ * `device-provider` integrations (Reolink/ONVIF) keep the
26519
26627
  * legacy config → discovery flow.
26520
26628
  */
26521
26629
  kind: _enum(["device-adoption", "device-provider"]),
@@ -29939,13 +30047,31 @@ var RecordingManifestSchema = object({
29939
30047
  */
29940
30048
  playbackEndpoints: array(string())
29941
30049
  });
29942
- /** Recording storage usage for one camera (bytes on disk across all its
29943
- * profiles/subtrees/locations on this node). */
30050
+ /**
30051
+ * Recording storage usage for one camera — what the ARCHIVE holds for it,
30052
+ * across every profile and every resolvable location on this node.
30053
+ *
30054
+ * ## Archive vs residency, which is the whole point of this shape (D418)
30055
+ *
30056
+ * `usedBytes` / `oldestMs` / `segments` are the durable hour ledger's numbers:
30057
+ * the same quantity `RecordingLocationUsage.usedBytes` reports for the volume
30058
+ * this camera writes to, so the two can be added up and compared.
30059
+ *
30060
+ * They were not always. Until D418 this row came from the recorder's in-RAM
30061
+ * `RecordingIndex`, which since D287 holds only the live tail plus the hours
30062
+ * somebody hydrated — so a hub with 1.9 TB and 3.3 days of footage reported
30063
+ * 60.5 GB and "oldest: three hours ago" on the same page as the volume row
30064
+ * that said otherwise, and two people read it as retention eating recent
30065
+ * footage. Residency is still reported, under `residentBytes` /
30066
+ * `residentOldestMs`, which answer a different question and say so.
30067
+ */
29944
30068
  var RecordingDeviceUsageSchema = object({
29945
30069
  deviceId: number(),
30070
+ /** Bytes the ARCHIVE holds for this camera on this node's resolvable
30071
+ * recordings locations. */
29946
30072
  usedBytes: number(),
29947
30073
  /**
29948
- * Start of this camera's OLDEST indexed segment, across every profile and
30074
+ * Start of this camera's OLDEST ARCHIVED segment, across every profile and
29949
30075
  * location — the "Oldest footage" column in Recordings → Storage, and the
29950
30076
  * only honest answer to "is retention actually holding?" per camera.
29951
30077
  *
@@ -29955,7 +30081,24 @@ var RecordingDeviceUsageSchema = object({
29955
30081
  * (types) and the addon ship on different trains, and the addon is usually
29956
30082
  * the later of the two.
29957
30083
  */
29958
- oldestMs: number().nullable().optional()
30084
+ oldestMs: number().nullable().optional(),
30085
+ /** Segments the archive holds for this camera — the "M" beside `usedBytes`.
30086
+ * OPTIONAL for the same train-skew reason as `oldestMs`. */
30087
+ segments: number().optional(),
30088
+ /**
30089
+ * RESIDENCY, not the archive: bytes the recorder process currently holds in
30090
+ * its RAM index — the live tail plus the hours a reader hydrated.
30091
+ *
30092
+ * It answers "what can be served without touching the disk", never "how much
30093
+ * footage this camera has". A UI that renders it must label it as such, and
30094
+ * must never total it as storage. OPTIONAL: a recorder predating D418 omits
30095
+ * it, and so does a node whose ledger could not answer.
30096
+ */
30097
+ residentBytes: number().optional(),
30098
+ /** The residency counterpart of `oldestMs` — the oldest segment currently in
30099
+ * RAM, which on a healthy recorder is minutes ago and means nothing about
30100
+ * retention. `null` = nothing resident. */
30101
+ residentOldestMs: number().nullable().optional()
29959
30102
  });
29960
30103
  /** Recording storage usage + capacity for one storage location. */
29961
30104
  var RecordingLocationUsageSchema = object({
@@ -29993,6 +30136,17 @@ var RecordingStorageUsageSchema = object({
29993
30136
  totalUsedBytes: number(),
29994
30137
  devices: array(RecordingDeviceUsageSchema),
29995
30138
  /**
30139
+ * WHERE `devices[]` came from, so an empty table is never mistaken for empty
30140
+ * disks (D418).
30141
+ *
30142
+ * `archive` = the durable hour ledger answered. `unavailable` = this node
30143
+ * declared no ledger and the recorder REFUSED rather than answering the
30144
+ * question with the resident index (D295 rule 3) — the rows are UNKNOWN, not
30145
+ * zero, and a UI must say so. OPTIONAL: a recorder predating D418 omits it,
30146
+ * and its rows are residency.
30147
+ */
30148
+ devicesRead: _enum(["archive", "unavailable"]).optional(),
30149
+ /**
29996
30150
  * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
29997
30151
  * of the payload without it: the hub serves this method through a BAKED
29998
30152
  * router, so a hub whose framework train predates a change to this array
@@ -33872,7 +34026,7 @@ function toDeviceSummary(device, addonId) {
33872
34026
  };
33873
34027
  }
33874
34028
  /**
33875
- * Base class for device-provider addons (rtsp, onvif, frigate).
34029
+ * Base class for device-provider addons (rtsp, onvif).
33876
34030
  *
33877
34031
  * Provides default implementations for the common device-provider cap
33878
34032
  * methods (`start`, `stop`, `getStatus`, `getDevices`, `supportsDiscovery`,
@@ -38528,6 +38682,24 @@ Object.freeze({
38528
38682
  addonId: null,
38529
38683
  access: "view"
38530
38684
  },
38685
+ "pipelineRunner.getParkedTrackFrame": {
38686
+ capName: "pipeline-runner",
38687
+ capScope: "system",
38688
+ addonId: null,
38689
+ access: "view"
38690
+ },
38691
+ "pipelineRunner.parkTrackFrame": {
38692
+ capName: "pipeline-runner",
38693
+ capScope: "system",
38694
+ addonId: null,
38695
+ access: "create"
38696
+ },
38697
+ "pipelineRunner.releaseParkedTrackFrames": {
38698
+ capName: "pipeline-runner",
38699
+ capScope: "system",
38700
+ addonId: null,
38701
+ access: "create"
38702
+ },
38531
38703
  "pipelineRunner.reportMotion": {
38532
38704
  capName: "pipeline-runner",
38533
38705
  capScope: "system",
@@ -41805,6 +41977,21 @@ Object.freeze({
41805
41977
  form: "single",
41806
41978
  optional: false
41807
41979
  }],
41980
+ "pipelineRunner.getParkedTrackFrame": [{
41981
+ name: "deviceId",
41982
+ form: "single",
41983
+ optional: false
41984
+ }],
41985
+ "pipelineRunner.parkTrackFrame": [{
41986
+ name: "deviceId",
41987
+ form: "single",
41988
+ optional: false
41989
+ }],
41990
+ "pipelineRunner.releaseParkedTrackFrames": [{
41991
+ name: "deviceId",
41992
+ form: "single",
41993
+ optional: false
41994
+ }],
41808
41995
  "pipelineRunner.reportMotion": [{
41809
41996
  name: "deviceId",
41810
41997
  form: "single",
@@ -42496,7 +42683,7 @@ var PreviewTextField = string().max(200);
42496
42683
  * Whether the notification's preview is a STILL or a short animation.
42497
42684
  *
42498
42685
  * The operator's ask, verbatim: *"inviato come gif o video (come per le altre
42499
- * rule)"* — his Scrypted advanced-notifier has a `gifRule`, and a ten-hour
42686
+ * rule)"* — the other rules already offer an animated preview, and a ten-hour
42500
42687
  * night reads better as three seconds of motion than as one frame of it. Both
42501
42688
  * modes get the SAME treatment (blurred frame, large centred title); `'gif'`
42502
42689
  * simply applies it to a dozen frames sampled across the render and assembles
package/dist/addon.mjs CHANGED
@@ -36,7 +36,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
36
36
  var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
37
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
38
38
  //#endregion
39
- //#region ../types/dist/event-category-CnLqLOKs.mjs
39
+ //#region ../types/dist/event-category-DAXzJeTX.mjs
40
40
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
41
41
  EventCategory["SystemBoot"] = "system.boot";
42
42
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -628,7 +628,6 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
628
628
  * issue. Payload `PipelineAnalyticsPlateGalleryChangedPayload`.
629
629
  */
630
630
  EventCategory["PipelineAnalyticsPlateGalleryChanged"] = "pipeline-analytics.plate-gallery-changed";
631
- EventCategory["FrigateLiveEvent"] = "frigate.live-event";
632
631
  EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
633
632
  /**
634
633
  * Stream-broker health watchdog. Per-broker (deviceId/profile) emission.
@@ -12662,7 +12661,7 @@ DeviceType.Camera;
12662
12661
  var SourceInfoSchema = object({
12663
12662
  /** Live dispatch key — mutable when the source system allows rename. */
12664
12663
  id: string(),
12665
- /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'frigate'. */
12664
+ /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'onvif'. */
12666
12665
  system: string(),
12667
12666
  /** Immutable upstream identifier when available (HA `unique_id`, … ).
12668
12667
  * Used to detect rename when `id` changes. */
@@ -12675,7 +12674,7 @@ var SourceInfoSchema = object({
12675
12674
  /**
12676
12675
  * Build the synthetic SourceInfo every existing provider falls back to
12677
12676
  * when no upstream value has been persisted. Keeps non-migrated providers
12678
- * (Reolink / Hikvision / ONVIF / Frigate / RTSP) functional without code
12677
+ * (Reolink / Hikvision / ONVIF / RTSP) functional without code
12679
12678
  * changes — `id` reuses the CamStack stableId, `system` reuses the addon
12680
12679
  * id. Real upstream identity replaces this once a provider migrates and
12681
12680
  * calls `updateSourceInfo()` with concrete values.
@@ -13354,7 +13353,7 @@ var deviceProviderCapability = {
13354
13353
  * - `device-management.router.ts` (deleted in Phase 2)
13355
13354
  * - `device-ops.router.ts` (compat layer — deleted; device-provider ops absorbed here)
13356
13355
  *
13357
- * All device provider addons (rtsp, onvif, frigate, …) are hub-local: they may
13356
+ * All device provider addons (rtsp, onvif, …) are hub-local: they may
13358
13357
  * fork into separate processes but never run on remote cluster agents. Therefore:
13359
13358
  * - No nodeId routing needed — this is a pure hub singleton.
13360
13359
  * - The hub's DeviceRegistry is the single source of truth for all live devices.
@@ -17402,7 +17401,7 @@ var NcRuleInputSchema = object({
17402
17401
  * The measured seven-person arrival on device 590 spans 110 s with every
17403
17402
  * internal gap under 30 s. A 12 s fixed window cuts it into three groups; an
17404
17403
  * idle cutoff holds it as one and ends it when the arrival actually ends.
17405
- * 30 is Frigate's shipped value for the same decision.
17404
+ * 30 s is a widely shipped value for the same decision.
17406
17405
  *
17407
17406
  * ### What it replaces
17408
17407
  *
@@ -19484,6 +19483,13 @@ var ZoneCrossingSchema = object({
19484
19483
  /** Zone display name at crossing time (falls back to the id). */
19485
19484
  zoneName: string().optional()
19486
19485
  });
19486
+ /** One zone a box was stamped with, and how much of the BOX fell inside it. */
19487
+ var ZoneOverlapSchema = object({
19488
+ zoneId: string(),
19489
+ zoneName: string().optional(),
19490
+ /** Percentage of the BOX area inside the zone, 0–100. */
19491
+ overlapPct: number()
19492
+ });
19487
19493
  var ObjectEventSchema = object({
19488
19494
  ...BaseEventFields,
19489
19495
  kind: literal("object"),
@@ -19508,6 +19514,35 @@ var ObjectEventSchema = object({
19508
19514
  bbox: BoundingBoxSchema.optional(),
19509
19515
  /** Heavy JSON — omitted in slim projection. */
19510
19516
  zones: array(string()).readonly().optional(),
19517
+ /**
19518
+ * How far inside each stamped zone the box actually was, as a percentage of
19519
+ * the BOX area.
19520
+ *
19521
+ * Membership alone cannot express "at least a third of it inside", which is
19522
+ * the bar an operator argues with — and the number already existed: the
19523
+ * engine computed it, the pipeline logged it, and then discarded it. It
19524
+ * matters most on the events that carry no track, where a rule has nothing
19525
+ * else to judge by.
19526
+ */
19527
+ zoneOverlaps: array(ZoneOverlapSchema).readonly().optional(),
19528
+ /**
19529
+ * Present when this event describes a detection the TRACKER refused, so it
19530
+ * has no track and never will.
19531
+ *
19532
+ * A zone-scoped immediate rule asks "is there an animal in the flowerbed
19533
+ * NOW?"; tracking answers a different question ("is this the same subject as
19534
+ * before?") whose confirmation threshold presumes continuity. On 2026-09-09 a
19535
+ * dog at 0.7729, 100% inside a watched zone, was refused three times in six
19536
+ * seconds with three different candidate ids — seven analysed frames in the
19537
+ * window, the subject in a handful of them, metres apart — and produced no
19538
+ * event at all.
19539
+ *
19540
+ * It is NAMED rather than merely implied by a missing `trackId`: an event
19541
+ * with no track and no reason is indistinguishable from a track whose media
19542
+ * went missing, and a consumer that asks this one for a best shot or a
19543
+ * history is asking for something that never existed.
19544
+ */
19545
+ spawnRefused: string().optional(),
19511
19546
  /** Omitted in slim projection. */
19512
19547
  state: TrackStateSchema.optional(),
19513
19548
  /**
@@ -20581,6 +20616,60 @@ var NativeCropResultSchema = object({
20581
20616
  */
20582
20617
  tier: _enum(["native", "ram-fullframe"]).optional()
20583
20618
  });
20619
+ /**
20620
+ * What a PARKED frame is for — the `kind` half of the `(deviceId, trackId,
20621
+ * kind)` key ([D422](../../../docs/decisions/adr-0422-a-relevant-frame-is-parked-when-it-becomes-relevant.md)).
20622
+ *
20623
+ * - `keyFrame` — the track's clean best FULL frame at native width (capped by
20624
+ * the caller's `maxWidth`). What `keyFrame` + `keyFrameSmall` are written
20625
+ * from, and what the detail plane's full-frame rung cuts from.
20626
+ * - `thumbnail` — the best-shot subject slab: the caller's 16:9 central-square
20627
+ * window, cut by the runner at uncapped native resolution.
20628
+ *
20629
+ * The runner never interprets `trackId`; it is an opaque key the hub owns.
20630
+ */
20631
+ var ParkedFrameKindSchema = _enum(["keyFrame", "thumbnail"]);
20632
+ /**
20633
+ * Why a park did NOT happen. Every one is logged on the runner with
20634
+ * `tags: { deviceId }` and the handle's measured age, because a handle already
20635
+ * gone at park time IS the defect the 150–500 ms window exists to prevent —
20636
+ * never a reason to retry in a loop.
20637
+ *
20638
+ * - `handle-gone` — the native surface no longer resolves this handle (never
20639
+ * registered, past the lease bound, or the worker released it). `detail`
20640
+ * carries the runner's own miss reason.
20641
+ * - `encode-failed` — pixels resolved and the JPEG encode threw.
20642
+ */
20643
+ var ParkRefusalSchema = _enum(["handle-gone", "encode-failed"]);
20644
+ /** Answer to `parkTrackFrame` — a discriminated union, so "not parked" always names why. */
20645
+ var ParkTrackFrameResultSchema = discriminatedUnion("parked", [object({
20646
+ parked: literal(true),
20647
+ /** The node holding the parcel — what a later retrieval must be pinned to. */
20648
+ nodeId: string(),
20649
+ width: number().int().positive(),
20650
+ height: number().int().positive(),
20651
+ /** JPEG bytes the parcel costs on the node. */
20652
+ bytes: number().int().nonnegative(),
20653
+ /** Whether this park replaced a parcel already held for the key. */
20654
+ replaced: boolean()
20655
+ }), object({
20656
+ parked: literal(false),
20657
+ nodeId: string(),
20658
+ reason: ParkRefusalSchema,
20659
+ /** Age of the handle at the park, on the runner's clock; null if unknown. */
20660
+ handleAgeMs: number().nullable(),
20661
+ detail: string().optional()
20662
+ })]);
20663
+ /** A retrieved parcel — the runner's own JPEG, base64 for the wire. */
20664
+ var ParkedTrackFrameSchema = object({
20665
+ jpeg: string(),
20666
+ width: number().int().positive(),
20667
+ height: number().int().positive(),
20668
+ /** The frame instant the parcel shows (the caller's clock, echoed back). */
20669
+ timestamp: number(),
20670
+ /** When the node stored it (the node's clock). */
20671
+ parkedAt: number()
20672
+ });
20584
20673
  /** Parent detection context passed to `runDetailSubtree` — the crop's
20585
20674
  * originating detection, in FRAME-space coordinates. Reuses
20586
20675
  * `NativeCropBboxSchema`'s `{x,y,w,h}` shape (same numeric fields; here
@@ -20970,7 +21059,7 @@ var RunnerCameraConfigSchema = object({
20970
21059
  * - `analyzer`: ML/frame-diff motion detection (motion-wasm or other
20971
21060
  * analyzer providers) running on decoded frames in this runner.
20972
21061
  * - `onboard`: camera-native motion pushes (Reolink Baichuan, ONVIF
20973
- * analytics, Frigate motion). The provider emits ProviderMotion
21062
+ * analytics). The provider emits ProviderMotion
20974
21063
  * events; the orchestrator forwards to `reportMotion`.
20975
21064
  */
20976
21065
  motionSources: MotionSourcesSchema.default(["analyzer"]),
@@ -21212,7 +21301,26 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
21212
21301
  className: string(),
21213
21302
  /** Camera the pixels came from. Diagnostics + log tags ONLY — never routing. */
21214
21303
  sourceDeviceId: number()
21215
- }), RunStatelessStepResultSchema, { kind: "mutation" });
21304
+ }), RunStatelessStepResultSchema, { kind: "mutation" }), method(object({
21305
+ deviceId: number(),
21306
+ /** Opaque to the runner — the hub's track id. */
21307
+ trackId: string(),
21308
+ kind: ParkedFrameKindSchema,
21309
+ handle: FrameHandleSchema,
21310
+ /** Normalized [0,1] window to cut. `{0,0,1,1}` for a full frame. */
21311
+ bbox: NativeCropBboxSchema,
21312
+ /** Width cap for a full-frame park (the `keyFrame` native cap). */
21313
+ maxWidth: number().int().positive().optional(),
21314
+ /** The frame instant (caller's clock) — orders parks for one key. */
21315
+ timestamp: number()
21316
+ }), ParkTrackFrameResultSchema, { kind: "mutation" }), method(object({
21317
+ deviceId: number(),
21318
+ trackId: string(),
21319
+ kind: ParkedFrameKindSchema
21320
+ }), ParkedTrackFrameSchema.nullable()), method(object({
21321
+ deviceId: number(),
21322
+ trackId: string()
21323
+ }), object({ released: number().int().nonnegative() }), { kind: "mutation" });
21216
21324
  var CameraPipelineConfigSchema = object({
21217
21325
  engine: PipelineEngineChoiceSchema.optional(),
21218
21326
  steps: array(PipelineStepInputSchema).readonly(),
@@ -24282,8 +24390,8 @@ onStatusChanged: { data: object({
24282
24390
  /**
24283
24391
  * Runtime-state slice — every provider that registers this cap
24284
24392
  * stores the same shape under `device.runtimeState[battery]`.
24285
- * Cross-provider uniformity: a Reolink Argus, a Frigate sensor
24286
- * proxy, an ONVIF battery cam all read/write the same keys.
24393
+ * Cross-provider uniformity: a Reolink Argus, a sensor proxy,
24394
+ * an ONVIF battery cam all read/write the same keys.
24287
24395
  * Consumers (BatteryBadge, snapshot wrapper sleep gate) read once
24288
24396
  * via `device.runtimeState.getCapState('battery')` regardless of
24289
24397
  * the underlying driver.
@@ -24416,7 +24524,7 @@ DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _v
24416
24524
  *
24417
24525
  * Implementations:
24418
24526
  * - Each camera-providing addon registers this cap natively per
24419
- * device (Reolink, Hikvision, RTSP, Frigate, ONVIF self).
24527
+ * device (Reolink, Hikvision, RTSP, ONVIF self).
24420
24528
  * - Returns a snapshot from the persisted device config blob — no
24421
24529
  * extra round-trip to the camera, no probing.
24422
24530
  * - The cap is admin-only (`auth: 'admin'`) — credentials must not
@@ -26516,7 +26624,7 @@ var AvailableIntegrationTypeSchema = object({
26516
26624
  * Which integration-marker cap the addon declared, so the wizard can
26517
26625
  * branch on CAP — never on addon name. `device-adoption` integrations
26518
26626
  * (Home Assistant, …) route through the broker step (Approach A);
26519
- * `device-provider` integrations (Reolink/Frigate/ONVIF) keep the
26627
+ * `device-provider` integrations (Reolink/ONVIF) keep the
26520
26628
  * legacy config → discovery flow.
26521
26629
  */
26522
26630
  kind: _enum(["device-adoption", "device-provider"]),
@@ -29940,13 +30048,31 @@ var RecordingManifestSchema = object({
29940
30048
  */
29941
30049
  playbackEndpoints: array(string())
29942
30050
  });
29943
- /** Recording storage usage for one camera (bytes on disk across all its
29944
- * profiles/subtrees/locations on this node). */
30051
+ /**
30052
+ * Recording storage usage for one camera — what the ARCHIVE holds for it,
30053
+ * across every profile and every resolvable location on this node.
30054
+ *
30055
+ * ## Archive vs residency, which is the whole point of this shape (D418)
30056
+ *
30057
+ * `usedBytes` / `oldestMs` / `segments` are the durable hour ledger's numbers:
30058
+ * the same quantity `RecordingLocationUsage.usedBytes` reports for the volume
30059
+ * this camera writes to, so the two can be added up and compared.
30060
+ *
30061
+ * They were not always. Until D418 this row came from the recorder's in-RAM
30062
+ * `RecordingIndex`, which since D287 holds only the live tail plus the hours
30063
+ * somebody hydrated — so a hub with 1.9 TB and 3.3 days of footage reported
30064
+ * 60.5 GB and "oldest: three hours ago" on the same page as the volume row
30065
+ * that said otherwise, and two people read it as retention eating recent
30066
+ * footage. Residency is still reported, under `residentBytes` /
30067
+ * `residentOldestMs`, which answer a different question and say so.
30068
+ */
29945
30069
  var RecordingDeviceUsageSchema = object({
29946
30070
  deviceId: number(),
30071
+ /** Bytes the ARCHIVE holds for this camera on this node's resolvable
30072
+ * recordings locations. */
29947
30073
  usedBytes: number(),
29948
30074
  /**
29949
- * Start of this camera's OLDEST indexed segment, across every profile and
30075
+ * Start of this camera's OLDEST ARCHIVED segment, across every profile and
29950
30076
  * location — the "Oldest footage" column in Recordings → Storage, and the
29951
30077
  * only honest answer to "is retention actually holding?" per camera.
29952
30078
  *
@@ -29956,7 +30082,24 @@ var RecordingDeviceUsageSchema = object({
29956
30082
  * (types) and the addon ship on different trains, and the addon is usually
29957
30083
  * the later of the two.
29958
30084
  */
29959
- oldestMs: number().nullable().optional()
30085
+ oldestMs: number().nullable().optional(),
30086
+ /** Segments the archive holds for this camera — the "M" beside `usedBytes`.
30087
+ * OPTIONAL for the same train-skew reason as `oldestMs`. */
30088
+ segments: number().optional(),
30089
+ /**
30090
+ * RESIDENCY, not the archive: bytes the recorder process currently holds in
30091
+ * its RAM index — the live tail plus the hours a reader hydrated.
30092
+ *
30093
+ * It answers "what can be served without touching the disk", never "how much
30094
+ * footage this camera has". A UI that renders it must label it as such, and
30095
+ * must never total it as storage. OPTIONAL: a recorder predating D418 omits
30096
+ * it, and so does a node whose ledger could not answer.
30097
+ */
30098
+ residentBytes: number().optional(),
30099
+ /** The residency counterpart of `oldestMs` — the oldest segment currently in
30100
+ * RAM, which on a healthy recorder is minutes ago and means nothing about
30101
+ * retention. `null` = nothing resident. */
30102
+ residentOldestMs: number().nullable().optional()
29960
30103
  });
29961
30104
  /** Recording storage usage + capacity for one storage location. */
29962
30105
  var RecordingLocationUsageSchema = object({
@@ -29994,6 +30137,17 @@ var RecordingStorageUsageSchema = object({
29994
30137
  totalUsedBytes: number(),
29995
30138
  devices: array(RecordingDeviceUsageSchema),
29996
30139
  /**
30140
+ * WHERE `devices[]` came from, so an empty table is never mistaken for empty
30141
+ * disks (D418).
30142
+ *
30143
+ * `archive` = the durable hour ledger answered. `unavailable` = this node
30144
+ * declared no ledger and the recorder REFUSED rather than answering the
30145
+ * question with the resident index (D295 rule 3) — the rows are UNKNOWN, not
30146
+ * zero, and a UI must say so. OPTIONAL: a recorder predating D418 omits it,
30147
+ * and its rows are residency.
30148
+ */
30149
+ devicesRead: _enum(["archive", "unavailable"]).optional(),
30150
+ /**
29997
30151
  * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
29998
30152
  * of the payload without it: the hub serves this method through a BAKED
29999
30153
  * router, so a hub whose framework train predates a change to this array
@@ -33873,7 +34027,7 @@ function toDeviceSummary(device, addonId) {
33873
34027
  };
33874
34028
  }
33875
34029
  /**
33876
- * Base class for device-provider addons (rtsp, onvif, frigate).
34030
+ * Base class for device-provider addons (rtsp, onvif).
33877
34031
  *
33878
34032
  * Provides default implementations for the common device-provider cap
33879
34033
  * methods (`start`, `stop`, `getStatus`, `getDevices`, `supportsDiscovery`,
@@ -38529,6 +38683,24 @@ Object.freeze({
38529
38683
  addonId: null,
38530
38684
  access: "view"
38531
38685
  },
38686
+ "pipelineRunner.getParkedTrackFrame": {
38687
+ capName: "pipeline-runner",
38688
+ capScope: "system",
38689
+ addonId: null,
38690
+ access: "view"
38691
+ },
38692
+ "pipelineRunner.parkTrackFrame": {
38693
+ capName: "pipeline-runner",
38694
+ capScope: "system",
38695
+ addonId: null,
38696
+ access: "create"
38697
+ },
38698
+ "pipelineRunner.releaseParkedTrackFrames": {
38699
+ capName: "pipeline-runner",
38700
+ capScope: "system",
38701
+ addonId: null,
38702
+ access: "create"
38703
+ },
38532
38704
  "pipelineRunner.reportMotion": {
38533
38705
  capName: "pipeline-runner",
38534
38706
  capScope: "system",
@@ -41806,6 +41978,21 @@ Object.freeze({
41806
41978
  form: "single",
41807
41979
  optional: false
41808
41980
  }],
41981
+ "pipelineRunner.getParkedTrackFrame": [{
41982
+ name: "deviceId",
41983
+ form: "single",
41984
+ optional: false
41985
+ }],
41986
+ "pipelineRunner.parkTrackFrame": [{
41987
+ name: "deviceId",
41988
+ form: "single",
41989
+ optional: false
41990
+ }],
41991
+ "pipelineRunner.releaseParkedTrackFrames": [{
41992
+ name: "deviceId",
41993
+ form: "single",
41994
+ optional: false
41995
+ }],
41809
41996
  "pipelineRunner.reportMotion": [{
41810
41997
  name: "deviceId",
41811
41998
  form: "single",
@@ -42497,7 +42684,7 @@ var PreviewTextField = string().max(200);
42497
42684
  * Whether the notification's preview is a STILL or a short animation.
42498
42685
  *
42499
42686
  * The operator's ask, verbatim: *"inviato come gif o video (come per le altre
42500
- * rule)"* — his Scrypted advanced-notifier has a `gifRule`, and a ten-hour
42687
+ * rule)"* — the other rules already offer an animated preview, and a ten-hour
42501
42688
  * night reads better as three seconds of motion than as one frame of it. Both
42502
42689
  * modes get the SAME treatment (blurred frame, large centred title); `'gif'`
42503
42690
  * simply applies it to a dozen frames sampled across the render and assembles
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-dreo",
3
- "version": "0.2.89",
3
+ "version": "0.2.92",
4
4
  "description": "Dreo smart-device (fan / air-circulator / purifier / heater / humidifier) device-provider addon for CamStack — wraps the @apocaliss92/nodedreo Dreo cloud client (REST + WebSocket)",
5
5
  "keywords": [
6
6
  "camstack",