@camstack/addon-provider-dreo 0.2.90 → 0.2.93

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 +246 -22
  2. package/dist/addon.mjs +246 -22
  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
  /**
@@ -19933,7 +19968,17 @@ var EventDensityBucketSchema = object({
19933
19968
  bucketStart: number(),
19934
19969
  motion: number().int(),
19935
19970
  object: number().int(),
19936
- audio: number().int()
19971
+ audio: number().int(),
19972
+ /**
19973
+ * Energy-weighted mean dBFS of the audio events in this bucket — the LEVEL,
19974
+ * as opposed to `audio`, which is their COUNT (D431).
19975
+ *
19976
+ * ABSENT when the bucket holds no audio event. Absence means "nothing was
19977
+ * measured", which is not the same claim as silence, and a lane that cannot
19978
+ * tell them apart paints a quiet hour at full scale — which is exactly what
19979
+ * the count-on-a-level-meter did.
19980
+ */
19981
+ audioDbfs: number().optional()
19937
19982
  });
19938
19983
  /**
19939
19984
  * One camera's row in a `getEventDensityBatch` answer.
@@ -20580,6 +20625,60 @@ var NativeCropResultSchema = object({
20580
20625
  */
20581
20626
  tier: _enum(["native", "ram-fullframe"]).optional()
20582
20627
  });
20628
+ /**
20629
+ * What a PARKED frame is for — the `kind` half of the `(deviceId, trackId,
20630
+ * kind)` key ([D422](../../../docs/decisions/adr-0422-a-relevant-frame-is-parked-when-it-becomes-relevant.md)).
20631
+ *
20632
+ * - `keyFrame` — the track's clean best FULL frame at native width (capped by
20633
+ * the caller's `maxWidth`). What `keyFrame` + `keyFrameSmall` are written
20634
+ * from, and what the detail plane's full-frame rung cuts from.
20635
+ * - `thumbnail` — the best-shot subject slab: the caller's 16:9 central-square
20636
+ * window, cut by the runner at uncapped native resolution.
20637
+ *
20638
+ * The runner never interprets `trackId`; it is an opaque key the hub owns.
20639
+ */
20640
+ var ParkedFrameKindSchema = _enum(["keyFrame", "thumbnail"]);
20641
+ /**
20642
+ * Why a park did NOT happen. Every one is logged on the runner with
20643
+ * `tags: { deviceId }` and the handle's measured age, because a handle already
20644
+ * gone at park time IS the defect the 150–500 ms window exists to prevent —
20645
+ * never a reason to retry in a loop.
20646
+ *
20647
+ * - `handle-gone` — the native surface no longer resolves this handle (never
20648
+ * registered, past the lease bound, or the worker released it). `detail`
20649
+ * carries the runner's own miss reason.
20650
+ * - `encode-failed` — pixels resolved and the JPEG encode threw.
20651
+ */
20652
+ var ParkRefusalSchema = _enum(["handle-gone", "encode-failed"]);
20653
+ /** Answer to `parkTrackFrame` — a discriminated union, so "not parked" always names why. */
20654
+ var ParkTrackFrameResultSchema = discriminatedUnion("parked", [object({
20655
+ parked: literal(true),
20656
+ /** The node holding the parcel — what a later retrieval must be pinned to. */
20657
+ nodeId: string(),
20658
+ width: number().int().positive(),
20659
+ height: number().int().positive(),
20660
+ /** JPEG bytes the parcel costs on the node. */
20661
+ bytes: number().int().nonnegative(),
20662
+ /** Whether this park replaced a parcel already held for the key. */
20663
+ replaced: boolean()
20664
+ }), object({
20665
+ parked: literal(false),
20666
+ nodeId: string(),
20667
+ reason: ParkRefusalSchema,
20668
+ /** Age of the handle at the park, on the runner's clock; null if unknown. */
20669
+ handleAgeMs: number().nullable(),
20670
+ detail: string().optional()
20671
+ })]);
20672
+ /** A retrieved parcel — the runner's own JPEG, base64 for the wire. */
20673
+ var ParkedTrackFrameSchema = object({
20674
+ jpeg: string(),
20675
+ width: number().int().positive(),
20676
+ height: number().int().positive(),
20677
+ /** The frame instant the parcel shows (the caller's clock, echoed back). */
20678
+ timestamp: number(),
20679
+ /** When the node stored it (the node's clock). */
20680
+ parkedAt: number()
20681
+ });
20583
20682
  /** Parent detection context passed to `runDetailSubtree` — the crop's
20584
20683
  * originating detection, in FRAME-space coordinates. Reuses
20585
20684
  * `NativeCropBboxSchema`'s `{x,y,w,h}` shape (same numeric fields; here
@@ -20969,7 +21068,7 @@ var RunnerCameraConfigSchema = object({
20969
21068
  * - `analyzer`: ML/frame-diff motion detection (motion-wasm or other
20970
21069
  * analyzer providers) running on decoded frames in this runner.
20971
21070
  * - `onboard`: camera-native motion pushes (Reolink Baichuan, ONVIF
20972
- * analytics, Frigate motion). The provider emits ProviderMotion
21071
+ * analytics). The provider emits ProviderMotion
20973
21072
  * events; the orchestrator forwards to `reportMotion`.
20974
21073
  */
20975
21074
  motionSources: MotionSourcesSchema.default(["analyzer"]),
@@ -21211,7 +21310,26 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
21211
21310
  className: string(),
21212
21311
  /** Camera the pixels came from. Diagnostics + log tags ONLY — never routing. */
21213
21312
  sourceDeviceId: number()
21214
- }), RunStatelessStepResultSchema, { kind: "mutation" });
21313
+ }), RunStatelessStepResultSchema, { kind: "mutation" }), method(object({
21314
+ deviceId: number(),
21315
+ /** Opaque to the runner — the hub's track id. */
21316
+ trackId: string(),
21317
+ kind: ParkedFrameKindSchema,
21318
+ handle: FrameHandleSchema,
21319
+ /** Normalized [0,1] window to cut. `{0,0,1,1}` for a full frame. */
21320
+ bbox: NativeCropBboxSchema,
21321
+ /** Width cap for a full-frame park (the `keyFrame` native cap). */
21322
+ maxWidth: number().int().positive().optional(),
21323
+ /** The frame instant (caller's clock) — orders parks for one key. */
21324
+ timestamp: number()
21325
+ }), ParkTrackFrameResultSchema, { kind: "mutation" }), method(object({
21326
+ deviceId: number(),
21327
+ trackId: string(),
21328
+ kind: ParkedFrameKindSchema
21329
+ }), ParkedTrackFrameSchema.nullable()), method(object({
21330
+ deviceId: number(),
21331
+ trackId: string()
21332
+ }), object({ released: number().int().nonnegative() }), { kind: "mutation" });
21215
21333
  var CameraPipelineConfigSchema = object({
21216
21334
  engine: PipelineEngineChoiceSchema.optional(),
21217
21335
  steps: array(PipelineStepInputSchema).readonly(),
@@ -24281,8 +24399,8 @@ onStatusChanged: { data: object({
24281
24399
  /**
24282
24400
  * Runtime-state slice — every provider that registers this cap
24283
24401
  * 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.
24402
+ * Cross-provider uniformity: a Reolink Argus, a sensor proxy,
24403
+ * an ONVIF battery cam all read/write the same keys.
24286
24404
  * Consumers (BatteryBadge, snapshot wrapper sleep gate) read once
24287
24405
  * via `device.runtimeState.getCapState('battery')` regardless of
24288
24406
  * the underlying driver.
@@ -24415,7 +24533,7 @@ DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _v
24415
24533
  *
24416
24534
  * Implementations:
24417
24535
  * - Each camera-providing addon registers this cap natively per
24418
- * device (Reolink, Hikvision, RTSP, Frigate, ONVIF self).
24536
+ * device (Reolink, Hikvision, RTSP, ONVIF self).
24419
24537
  * - Returns a snapshot from the persisted device config blob — no
24420
24538
  * extra round-trip to the camera, no probing.
24421
24539
  * - The cap is admin-only (`auth: 'admin'`) — credentials must not
@@ -26515,7 +26633,7 @@ var AvailableIntegrationTypeSchema = object({
26515
26633
  * Which integration-marker cap the addon declared, so the wizard can
26516
26634
  * branch on CAP — never on addon name. `device-adoption` integrations
26517
26635
  * (Home Assistant, …) route through the broker step (Approach A);
26518
- * `device-provider` integrations (Reolink/Frigate/ONVIF) keep the
26636
+ * `device-provider` integrations (Reolink/ONVIF) keep the
26519
26637
  * legacy config → discovery flow.
26520
26638
  */
26521
26639
  kind: _enum(["device-adoption", "device-provider"]),
@@ -29883,7 +30001,23 @@ var RecordingRangeSchema = object({
29883
30001
  });
29884
30002
  var RecordingAvailabilitySchema = object({
29885
30003
  deviceId: number(),
29886
- ranges: array(RecordingRangeSchema)
30004
+ ranges: array(RecordingRangeSchema),
30005
+ /**
30006
+ * Every profile this camera has footage in — not only the one `ranges`
30007
+ * describes (D433).
30008
+ *
30009
+ * `ranges` answers for ONE profile by design: the timeline is a single bar,
30010
+ * and enumerating all of them triples the directory reads for a bar that
30011
+ * would look identical. But the answer used to say nothing about that, so a
30012
+ * caller asking "what can I export?" read the single preferred profile as
30013
+ * the only one that exists — on this deployment every camera records `high`
30014
+ * AND `low`, the low tree holding 14 GB for camera 615 alone, and the export
30015
+ * UI could not see it.
30016
+ *
30017
+ * Cheap: it is the same shallow directory read the profile CHOICE already
30018
+ * makes. Ask for a specific profile's ranges with the `profile` input.
30019
+ */
30020
+ profilesWithFootage: array(string())
29887
30021
  });
29888
30022
  var RecordingDaysSchema = object({
29889
30023
  deviceId: number(),
@@ -29909,7 +30043,11 @@ var RecordingDaysSchema = object({
29909
30043
  var RecordingAvailabilityForDeviceSchema = object({
29910
30044
  deviceId: number(),
29911
30045
  read: _enum(["read", "unreadable"]),
29912
- ranges: array(RecordingRangeSchema).readonly()
30046
+ ranges: array(RecordingRangeSchema).readonly(),
30047
+ /** Same field, same meaning, as the singular answer (D433). A row that
30048
+ * dropped it would tell a grid caller the archive holds one profile.
30049
+ * Empty on an `'unreadable'` row: nobody looked. */
30050
+ profilesWithFootage: array(string()).readonly()
29913
30051
  });
29914
30052
  /**
29915
30053
  * One camera's row in a `getDaysWithRecordingsBatch` answer. Same rule as
@@ -29939,13 +30077,31 @@ var RecordingManifestSchema = object({
29939
30077
  */
29940
30078
  playbackEndpoints: array(string())
29941
30079
  });
29942
- /** Recording storage usage for one camera (bytes on disk across all its
29943
- * profiles/subtrees/locations on this node). */
30080
+ /**
30081
+ * Recording storage usage for one camera — what the ARCHIVE holds for it,
30082
+ * across every profile and every resolvable location on this node.
30083
+ *
30084
+ * ## Archive vs residency, which is the whole point of this shape (D418)
30085
+ *
30086
+ * `usedBytes` / `oldestMs` / `segments` are the durable hour ledger's numbers:
30087
+ * the same quantity `RecordingLocationUsage.usedBytes` reports for the volume
30088
+ * this camera writes to, so the two can be added up and compared.
30089
+ *
30090
+ * They were not always. Until D418 this row came from the recorder's in-RAM
30091
+ * `RecordingIndex`, which since D287 holds only the live tail plus the hours
30092
+ * somebody hydrated — so a hub with 1.9 TB and 3.3 days of footage reported
30093
+ * 60.5 GB and "oldest: three hours ago" on the same page as the volume row
30094
+ * that said otherwise, and two people read it as retention eating recent
30095
+ * footage. Residency is still reported, under `residentBytes` /
30096
+ * `residentOldestMs`, which answer a different question and say so.
30097
+ */
29944
30098
  var RecordingDeviceUsageSchema = object({
29945
30099
  deviceId: number(),
30100
+ /** Bytes the ARCHIVE holds for this camera on this node's resolvable
30101
+ * recordings locations. */
29946
30102
  usedBytes: number(),
29947
30103
  /**
29948
- * Start of this camera's OLDEST indexed segment, across every profile and
30104
+ * Start of this camera's OLDEST ARCHIVED segment, across every profile and
29949
30105
  * location — the "Oldest footage" column in Recordings → Storage, and the
29950
30106
  * only honest answer to "is retention actually holding?" per camera.
29951
30107
  *
@@ -29955,7 +30111,24 @@ var RecordingDeviceUsageSchema = object({
29955
30111
  * (types) and the addon ship on different trains, and the addon is usually
29956
30112
  * the later of the two.
29957
30113
  */
29958
- oldestMs: number().nullable().optional()
30114
+ oldestMs: number().nullable().optional(),
30115
+ /** Segments the archive holds for this camera — the "M" beside `usedBytes`.
30116
+ * OPTIONAL for the same train-skew reason as `oldestMs`. */
30117
+ segments: number().optional(),
30118
+ /**
30119
+ * RESIDENCY, not the archive: bytes the recorder process currently holds in
30120
+ * its RAM index — the live tail plus the hours a reader hydrated.
30121
+ *
30122
+ * It answers "what can be served without touching the disk", never "how much
30123
+ * footage this camera has". A UI that renders it must label it as such, and
30124
+ * must never total it as storage. OPTIONAL: a recorder predating D418 omits
30125
+ * it, and so does a node whose ledger could not answer.
30126
+ */
30127
+ residentBytes: number().optional(),
30128
+ /** The residency counterpart of `oldestMs` — the oldest segment currently in
30129
+ * RAM, which on a healthy recorder is minutes ago and means nothing about
30130
+ * retention. `null` = nothing resident. */
30131
+ residentOldestMs: number().nullable().optional()
29959
30132
  });
29960
30133
  /** Recording storage usage + capacity for one storage location. */
29961
30134
  var RecordingLocationUsageSchema = object({
@@ -29993,6 +30166,17 @@ var RecordingStorageUsageSchema = object({
29993
30166
  totalUsedBytes: number(),
29994
30167
  devices: array(RecordingDeviceUsageSchema),
29995
30168
  /**
30169
+ * WHERE `devices[]` came from, so an empty table is never mistaken for empty
30170
+ * disks (D418).
30171
+ *
30172
+ * `archive` = the durable hour ledger answered. `unavailable` = this node
30173
+ * declared no ledger and the recorder REFUSED rather than answering the
30174
+ * question with the resident index (D295 rule 3) — the rows are UNKNOWN, not
30175
+ * zero, and a UI must say so. OPTIONAL: a recorder predating D418 omits it,
30176
+ * and its rows are residency.
30177
+ */
30178
+ devicesRead: _enum(["archive", "unavailable"]).optional(),
30179
+ /**
29996
30180
  * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
29997
30181
  * of the payload without it: the hub serves this method through a BAKED
29998
30182
  * router, so a hub whose framework train predates a change to this array
@@ -30153,7 +30337,14 @@ var ReadWindowBytesResultSchema = discriminatedUnion("kind", [object({
30153
30337
  method(object({
30154
30338
  deviceId: number(),
30155
30339
  fromMs: number(),
30156
- toMs: number()
30340
+ toMs: number(),
30341
+ /**
30342
+ * Answer for THIS profile instead of the preferred one (D433). Absent
30343
+ * keeps the timeline's behaviour — one bar, one profile, one set of
30344
+ * reads. `profilesWithFootage` on the answer says what may be asked
30345
+ * for.
30346
+ */
30347
+ profile: string().optional()
30157
30348
  }), RecordingAvailabilitySchema, {
30158
30349
  kind: "query",
30159
30350
  auth: "protected"
@@ -33872,7 +34063,7 @@ function toDeviceSummary(device, addonId) {
33872
34063
  };
33873
34064
  }
33874
34065
  /**
33875
- * Base class for device-provider addons (rtsp, onvif, frigate).
34066
+ * Base class for device-provider addons (rtsp, onvif).
33876
34067
  *
33877
34068
  * Provides default implementations for the common device-provider cap
33878
34069
  * methods (`start`, `stop`, `getStatus`, `getDevices`, `supportsDiscovery`,
@@ -38528,6 +38719,24 @@ Object.freeze({
38528
38719
  addonId: null,
38529
38720
  access: "view"
38530
38721
  },
38722
+ "pipelineRunner.getParkedTrackFrame": {
38723
+ capName: "pipeline-runner",
38724
+ capScope: "system",
38725
+ addonId: null,
38726
+ access: "view"
38727
+ },
38728
+ "pipelineRunner.parkTrackFrame": {
38729
+ capName: "pipeline-runner",
38730
+ capScope: "system",
38731
+ addonId: null,
38732
+ access: "create"
38733
+ },
38734
+ "pipelineRunner.releaseParkedTrackFrames": {
38735
+ capName: "pipeline-runner",
38736
+ capScope: "system",
38737
+ addonId: null,
38738
+ access: "create"
38739
+ },
38531
38740
  "pipelineRunner.reportMotion": {
38532
38741
  capName: "pipeline-runner",
38533
38742
  capScope: "system",
@@ -41805,6 +42014,21 @@ Object.freeze({
41805
42014
  form: "single",
41806
42015
  optional: false
41807
42016
  }],
42017
+ "pipelineRunner.getParkedTrackFrame": [{
42018
+ name: "deviceId",
42019
+ form: "single",
42020
+ optional: false
42021
+ }],
42022
+ "pipelineRunner.parkTrackFrame": [{
42023
+ name: "deviceId",
42024
+ form: "single",
42025
+ optional: false
42026
+ }],
42027
+ "pipelineRunner.releaseParkedTrackFrames": [{
42028
+ name: "deviceId",
42029
+ form: "single",
42030
+ optional: false
42031
+ }],
41808
42032
  "pipelineRunner.reportMotion": [{
41809
42033
  name: "deviceId",
41810
42034
  form: "single",
@@ -42496,7 +42720,7 @@ var PreviewTextField = string().max(200);
42496
42720
  * Whether the notification's preview is a STILL or a short animation.
42497
42721
  *
42498
42722
  * 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
42723
+ * rule)"* — the other rules already offer an animated preview, and a ten-hour
42500
42724
  * night reads better as three seconds of motion than as one frame of it. Both
42501
42725
  * modes get the SAME treatment (blurred frame, large centred title); `'gif'`
42502
42726
  * 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
  /**
@@ -19934,7 +19969,17 @@ var EventDensityBucketSchema = object({
19934
19969
  bucketStart: number(),
19935
19970
  motion: number().int(),
19936
19971
  object: number().int(),
19937
- audio: number().int()
19972
+ audio: number().int(),
19973
+ /**
19974
+ * Energy-weighted mean dBFS of the audio events in this bucket — the LEVEL,
19975
+ * as opposed to `audio`, which is their COUNT (D431).
19976
+ *
19977
+ * ABSENT when the bucket holds no audio event. Absence means "nothing was
19978
+ * measured", which is not the same claim as silence, and a lane that cannot
19979
+ * tell them apart paints a quiet hour at full scale — which is exactly what
19980
+ * the count-on-a-level-meter did.
19981
+ */
19982
+ audioDbfs: number().optional()
19938
19983
  });
19939
19984
  /**
19940
19985
  * One camera's row in a `getEventDensityBatch` answer.
@@ -20581,6 +20626,60 @@ var NativeCropResultSchema = object({
20581
20626
  */
20582
20627
  tier: _enum(["native", "ram-fullframe"]).optional()
20583
20628
  });
20629
+ /**
20630
+ * What a PARKED frame is for — the `kind` half of the `(deviceId, trackId,
20631
+ * kind)` key ([D422](../../../docs/decisions/adr-0422-a-relevant-frame-is-parked-when-it-becomes-relevant.md)).
20632
+ *
20633
+ * - `keyFrame` — the track's clean best FULL frame at native width (capped by
20634
+ * the caller's `maxWidth`). What `keyFrame` + `keyFrameSmall` are written
20635
+ * from, and what the detail plane's full-frame rung cuts from.
20636
+ * - `thumbnail` — the best-shot subject slab: the caller's 16:9 central-square
20637
+ * window, cut by the runner at uncapped native resolution.
20638
+ *
20639
+ * The runner never interprets `trackId`; it is an opaque key the hub owns.
20640
+ */
20641
+ var ParkedFrameKindSchema = _enum(["keyFrame", "thumbnail"]);
20642
+ /**
20643
+ * Why a park did NOT happen. Every one is logged on the runner with
20644
+ * `tags: { deviceId }` and the handle's measured age, because a handle already
20645
+ * gone at park time IS the defect the 150–500 ms window exists to prevent —
20646
+ * never a reason to retry in a loop.
20647
+ *
20648
+ * - `handle-gone` — the native surface no longer resolves this handle (never
20649
+ * registered, past the lease bound, or the worker released it). `detail`
20650
+ * carries the runner's own miss reason.
20651
+ * - `encode-failed` — pixels resolved and the JPEG encode threw.
20652
+ */
20653
+ var ParkRefusalSchema = _enum(["handle-gone", "encode-failed"]);
20654
+ /** Answer to `parkTrackFrame` — a discriminated union, so "not parked" always names why. */
20655
+ var ParkTrackFrameResultSchema = discriminatedUnion("parked", [object({
20656
+ parked: literal(true),
20657
+ /** The node holding the parcel — what a later retrieval must be pinned to. */
20658
+ nodeId: string(),
20659
+ width: number().int().positive(),
20660
+ height: number().int().positive(),
20661
+ /** JPEG bytes the parcel costs on the node. */
20662
+ bytes: number().int().nonnegative(),
20663
+ /** Whether this park replaced a parcel already held for the key. */
20664
+ replaced: boolean()
20665
+ }), object({
20666
+ parked: literal(false),
20667
+ nodeId: string(),
20668
+ reason: ParkRefusalSchema,
20669
+ /** Age of the handle at the park, on the runner's clock; null if unknown. */
20670
+ handleAgeMs: number().nullable(),
20671
+ detail: string().optional()
20672
+ })]);
20673
+ /** A retrieved parcel — the runner's own JPEG, base64 for the wire. */
20674
+ var ParkedTrackFrameSchema = object({
20675
+ jpeg: string(),
20676
+ width: number().int().positive(),
20677
+ height: number().int().positive(),
20678
+ /** The frame instant the parcel shows (the caller's clock, echoed back). */
20679
+ timestamp: number(),
20680
+ /** When the node stored it (the node's clock). */
20681
+ parkedAt: number()
20682
+ });
20584
20683
  /** Parent detection context passed to `runDetailSubtree` — the crop's
20585
20684
  * originating detection, in FRAME-space coordinates. Reuses
20586
20685
  * `NativeCropBboxSchema`'s `{x,y,w,h}` shape (same numeric fields; here
@@ -20970,7 +21069,7 @@ var RunnerCameraConfigSchema = object({
20970
21069
  * - `analyzer`: ML/frame-diff motion detection (motion-wasm or other
20971
21070
  * analyzer providers) running on decoded frames in this runner.
20972
21071
  * - `onboard`: camera-native motion pushes (Reolink Baichuan, ONVIF
20973
- * analytics, Frigate motion). The provider emits ProviderMotion
21072
+ * analytics). The provider emits ProviderMotion
20974
21073
  * events; the orchestrator forwards to `reportMotion`.
20975
21074
  */
20976
21075
  motionSources: MotionSourcesSchema.default(["analyzer"]),
@@ -21212,7 +21311,26 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
21212
21311
  className: string(),
21213
21312
  /** Camera the pixels came from. Diagnostics + log tags ONLY — never routing. */
21214
21313
  sourceDeviceId: number()
21215
- }), RunStatelessStepResultSchema, { kind: "mutation" });
21314
+ }), RunStatelessStepResultSchema, { kind: "mutation" }), method(object({
21315
+ deviceId: number(),
21316
+ /** Opaque to the runner — the hub's track id. */
21317
+ trackId: string(),
21318
+ kind: ParkedFrameKindSchema,
21319
+ handle: FrameHandleSchema,
21320
+ /** Normalized [0,1] window to cut. `{0,0,1,1}` for a full frame. */
21321
+ bbox: NativeCropBboxSchema,
21322
+ /** Width cap for a full-frame park (the `keyFrame` native cap). */
21323
+ maxWidth: number().int().positive().optional(),
21324
+ /** The frame instant (caller's clock) — orders parks for one key. */
21325
+ timestamp: number()
21326
+ }), ParkTrackFrameResultSchema, { kind: "mutation" }), method(object({
21327
+ deviceId: number(),
21328
+ trackId: string(),
21329
+ kind: ParkedFrameKindSchema
21330
+ }), ParkedTrackFrameSchema.nullable()), method(object({
21331
+ deviceId: number(),
21332
+ trackId: string()
21333
+ }), object({ released: number().int().nonnegative() }), { kind: "mutation" });
21216
21334
  var CameraPipelineConfigSchema = object({
21217
21335
  engine: PipelineEngineChoiceSchema.optional(),
21218
21336
  steps: array(PipelineStepInputSchema).readonly(),
@@ -24282,8 +24400,8 @@ onStatusChanged: { data: object({
24282
24400
  /**
24283
24401
  * Runtime-state slice — every provider that registers this cap
24284
24402
  * 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.
24403
+ * Cross-provider uniformity: a Reolink Argus, a sensor proxy,
24404
+ * an ONVIF battery cam all read/write the same keys.
24287
24405
  * Consumers (BatteryBadge, snapshot wrapper sleep gate) read once
24288
24406
  * via `device.runtimeState.getCapState('battery')` regardless of
24289
24407
  * the underlying driver.
@@ -24416,7 +24534,7 @@ DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _v
24416
24534
  *
24417
24535
  * Implementations:
24418
24536
  * - Each camera-providing addon registers this cap natively per
24419
- * device (Reolink, Hikvision, RTSP, Frigate, ONVIF self).
24537
+ * device (Reolink, Hikvision, RTSP, ONVIF self).
24420
24538
  * - Returns a snapshot from the persisted device config blob — no
24421
24539
  * extra round-trip to the camera, no probing.
24422
24540
  * - The cap is admin-only (`auth: 'admin'`) — credentials must not
@@ -26516,7 +26634,7 @@ var AvailableIntegrationTypeSchema = object({
26516
26634
  * Which integration-marker cap the addon declared, so the wizard can
26517
26635
  * branch on CAP — never on addon name. `device-adoption` integrations
26518
26636
  * (Home Assistant, …) route through the broker step (Approach A);
26519
- * `device-provider` integrations (Reolink/Frigate/ONVIF) keep the
26637
+ * `device-provider` integrations (Reolink/ONVIF) keep the
26520
26638
  * legacy config → discovery flow.
26521
26639
  */
26522
26640
  kind: _enum(["device-adoption", "device-provider"]),
@@ -29884,7 +30002,23 @@ var RecordingRangeSchema = object({
29884
30002
  });
29885
30003
  var RecordingAvailabilitySchema = object({
29886
30004
  deviceId: number(),
29887
- ranges: array(RecordingRangeSchema)
30005
+ ranges: array(RecordingRangeSchema),
30006
+ /**
30007
+ * Every profile this camera has footage in — not only the one `ranges`
30008
+ * describes (D433).
30009
+ *
30010
+ * `ranges` answers for ONE profile by design: the timeline is a single bar,
30011
+ * and enumerating all of them triples the directory reads for a bar that
30012
+ * would look identical. But the answer used to say nothing about that, so a
30013
+ * caller asking "what can I export?" read the single preferred profile as
30014
+ * the only one that exists — on this deployment every camera records `high`
30015
+ * AND `low`, the low tree holding 14 GB for camera 615 alone, and the export
30016
+ * UI could not see it.
30017
+ *
30018
+ * Cheap: it is the same shallow directory read the profile CHOICE already
30019
+ * makes. Ask for a specific profile's ranges with the `profile` input.
30020
+ */
30021
+ profilesWithFootage: array(string())
29888
30022
  });
29889
30023
  var RecordingDaysSchema = object({
29890
30024
  deviceId: number(),
@@ -29910,7 +30044,11 @@ var RecordingDaysSchema = object({
29910
30044
  var RecordingAvailabilityForDeviceSchema = object({
29911
30045
  deviceId: number(),
29912
30046
  read: _enum(["read", "unreadable"]),
29913
- ranges: array(RecordingRangeSchema).readonly()
30047
+ ranges: array(RecordingRangeSchema).readonly(),
30048
+ /** Same field, same meaning, as the singular answer (D433). A row that
30049
+ * dropped it would tell a grid caller the archive holds one profile.
30050
+ * Empty on an `'unreadable'` row: nobody looked. */
30051
+ profilesWithFootage: array(string()).readonly()
29914
30052
  });
29915
30053
  /**
29916
30054
  * One camera's row in a `getDaysWithRecordingsBatch` answer. Same rule as
@@ -29940,13 +30078,31 @@ var RecordingManifestSchema = object({
29940
30078
  */
29941
30079
  playbackEndpoints: array(string())
29942
30080
  });
29943
- /** Recording storage usage for one camera (bytes on disk across all its
29944
- * profiles/subtrees/locations on this node). */
30081
+ /**
30082
+ * Recording storage usage for one camera — what the ARCHIVE holds for it,
30083
+ * across every profile and every resolvable location on this node.
30084
+ *
30085
+ * ## Archive vs residency, which is the whole point of this shape (D418)
30086
+ *
30087
+ * `usedBytes` / `oldestMs` / `segments` are the durable hour ledger's numbers:
30088
+ * the same quantity `RecordingLocationUsage.usedBytes` reports for the volume
30089
+ * this camera writes to, so the two can be added up and compared.
30090
+ *
30091
+ * They were not always. Until D418 this row came from the recorder's in-RAM
30092
+ * `RecordingIndex`, which since D287 holds only the live tail plus the hours
30093
+ * somebody hydrated — so a hub with 1.9 TB and 3.3 days of footage reported
30094
+ * 60.5 GB and "oldest: three hours ago" on the same page as the volume row
30095
+ * that said otherwise, and two people read it as retention eating recent
30096
+ * footage. Residency is still reported, under `residentBytes` /
30097
+ * `residentOldestMs`, which answer a different question and say so.
30098
+ */
29945
30099
  var RecordingDeviceUsageSchema = object({
29946
30100
  deviceId: number(),
30101
+ /** Bytes the ARCHIVE holds for this camera on this node's resolvable
30102
+ * recordings locations. */
29947
30103
  usedBytes: number(),
29948
30104
  /**
29949
- * Start of this camera's OLDEST indexed segment, across every profile and
30105
+ * Start of this camera's OLDEST ARCHIVED segment, across every profile and
29950
30106
  * location — the "Oldest footage" column in Recordings → Storage, and the
29951
30107
  * only honest answer to "is retention actually holding?" per camera.
29952
30108
  *
@@ -29956,7 +30112,24 @@ var RecordingDeviceUsageSchema = object({
29956
30112
  * (types) and the addon ship on different trains, and the addon is usually
29957
30113
  * the later of the two.
29958
30114
  */
29959
- oldestMs: number().nullable().optional()
30115
+ oldestMs: number().nullable().optional(),
30116
+ /** Segments the archive holds for this camera — the "M" beside `usedBytes`.
30117
+ * OPTIONAL for the same train-skew reason as `oldestMs`. */
30118
+ segments: number().optional(),
30119
+ /**
30120
+ * RESIDENCY, not the archive: bytes the recorder process currently holds in
30121
+ * its RAM index — the live tail plus the hours a reader hydrated.
30122
+ *
30123
+ * It answers "what can be served without touching the disk", never "how much
30124
+ * footage this camera has". A UI that renders it must label it as such, and
30125
+ * must never total it as storage. OPTIONAL: a recorder predating D418 omits
30126
+ * it, and so does a node whose ledger could not answer.
30127
+ */
30128
+ residentBytes: number().optional(),
30129
+ /** The residency counterpart of `oldestMs` — the oldest segment currently in
30130
+ * RAM, which on a healthy recorder is minutes ago and means nothing about
30131
+ * retention. `null` = nothing resident. */
30132
+ residentOldestMs: number().nullable().optional()
29960
30133
  });
29961
30134
  /** Recording storage usage + capacity for one storage location. */
29962
30135
  var RecordingLocationUsageSchema = object({
@@ -29994,6 +30167,17 @@ var RecordingStorageUsageSchema = object({
29994
30167
  totalUsedBytes: number(),
29995
30168
  devices: array(RecordingDeviceUsageSchema),
29996
30169
  /**
30170
+ * WHERE `devices[]` came from, so an empty table is never mistaken for empty
30171
+ * disks (D418).
30172
+ *
30173
+ * `archive` = the durable hour ledger answered. `unavailable` = this node
30174
+ * declared no ledger and the recorder REFUSED rather than answering the
30175
+ * question with the resident index (D295 rule 3) — the rows are UNKNOWN, not
30176
+ * zero, and a UI must say so. OPTIONAL: a recorder predating D418 omits it,
30177
+ * and its rows are residency.
30178
+ */
30179
+ devicesRead: _enum(["archive", "unavailable"]).optional(),
30180
+ /**
29997
30181
  * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
29998
30182
  * of the payload without it: the hub serves this method through a BAKED
29999
30183
  * router, so a hub whose framework train predates a change to this array
@@ -30154,7 +30338,14 @@ var ReadWindowBytesResultSchema = discriminatedUnion("kind", [object({
30154
30338
  method(object({
30155
30339
  deviceId: number(),
30156
30340
  fromMs: number(),
30157
- toMs: number()
30341
+ toMs: number(),
30342
+ /**
30343
+ * Answer for THIS profile instead of the preferred one (D433). Absent
30344
+ * keeps the timeline's behaviour — one bar, one profile, one set of
30345
+ * reads. `profilesWithFootage` on the answer says what may be asked
30346
+ * for.
30347
+ */
30348
+ profile: string().optional()
30158
30349
  }), RecordingAvailabilitySchema, {
30159
30350
  kind: "query",
30160
30351
  auth: "protected"
@@ -33873,7 +34064,7 @@ function toDeviceSummary(device, addonId) {
33873
34064
  };
33874
34065
  }
33875
34066
  /**
33876
- * Base class for device-provider addons (rtsp, onvif, frigate).
34067
+ * Base class for device-provider addons (rtsp, onvif).
33877
34068
  *
33878
34069
  * Provides default implementations for the common device-provider cap
33879
34070
  * methods (`start`, `stop`, `getStatus`, `getDevices`, `supportsDiscovery`,
@@ -38529,6 +38720,24 @@ Object.freeze({
38529
38720
  addonId: null,
38530
38721
  access: "view"
38531
38722
  },
38723
+ "pipelineRunner.getParkedTrackFrame": {
38724
+ capName: "pipeline-runner",
38725
+ capScope: "system",
38726
+ addonId: null,
38727
+ access: "view"
38728
+ },
38729
+ "pipelineRunner.parkTrackFrame": {
38730
+ capName: "pipeline-runner",
38731
+ capScope: "system",
38732
+ addonId: null,
38733
+ access: "create"
38734
+ },
38735
+ "pipelineRunner.releaseParkedTrackFrames": {
38736
+ capName: "pipeline-runner",
38737
+ capScope: "system",
38738
+ addonId: null,
38739
+ access: "create"
38740
+ },
38532
38741
  "pipelineRunner.reportMotion": {
38533
38742
  capName: "pipeline-runner",
38534
38743
  capScope: "system",
@@ -41806,6 +42015,21 @@ Object.freeze({
41806
42015
  form: "single",
41807
42016
  optional: false
41808
42017
  }],
42018
+ "pipelineRunner.getParkedTrackFrame": [{
42019
+ name: "deviceId",
42020
+ form: "single",
42021
+ optional: false
42022
+ }],
42023
+ "pipelineRunner.parkTrackFrame": [{
42024
+ name: "deviceId",
42025
+ form: "single",
42026
+ optional: false
42027
+ }],
42028
+ "pipelineRunner.releaseParkedTrackFrames": [{
42029
+ name: "deviceId",
42030
+ form: "single",
42031
+ optional: false
42032
+ }],
41809
42033
  "pipelineRunner.reportMotion": [{
41810
42034
  name: "deviceId",
41811
42035
  form: "single",
@@ -42497,7 +42721,7 @@ var PreviewTextField = string().max(200);
42497
42721
  * Whether the notification's preview is a STILL or a short animation.
42498
42722
  *
42499
42723
  * 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
42724
+ * rule)"* — the other rules already offer an animated preview, and a ten-hour
42501
42725
  * night reads better as three seconds of motion than as one frame of it. Both
42502
42726
  * modes get the SAME treatment (blurred frame, large centred title); `'gif'`
42503
42727
  * 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.90",
3
+ "version": "0.2.93",
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",