@camstack/addon-provider-reolink 1.2.112 → 1.2.115

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 +245 -82
  2. package/dist/addon.mjs +245 -82
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -25,7 +25,7 @@ let fs_promises = require("fs/promises");
25
25
  fs_promises = require_chunk.__toESM(fs_promises, 1);
26
26
  let node_os = require("node:os");
27
27
  node_os = require_chunk.__toESM(node_os);
28
- //#region ../types/dist/event-category-CnLqLOKs.mjs
28
+ //#region ../types/dist/event-category-DAXzJeTX.mjs
29
29
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
30
30
  EventCategory["SystemBoot"] = "system.boot";
31
31
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -617,7 +617,6 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
617
617
  * issue. Payload `PipelineAnalyticsPlateGalleryChangedPayload`.
618
618
  */
619
619
  EventCategory["PipelineAnalyticsPlateGalleryChanged"] = "pipeline-analytics.plate-gallery-changed";
620
- EventCategory["FrigateLiveEvent"] = "frigate.live-event";
621
620
  EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
622
621
  /**
623
622
  * Stream-broker health watchdog. Per-broker (deviceId/profile) emission.
@@ -13229,7 +13228,7 @@ DeviceType.Camera;
13229
13228
  var SourceInfoSchema = object({
13230
13229
  /** Live dispatch key — mutable when the source system allows rename. */
13231
13230
  id: string(),
13232
- /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'frigate'. */
13231
+ /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'onvif'. */
13233
13232
  system: string(),
13234
13233
  /** Immutable upstream identifier when available (HA `unique_id`, … ).
13235
13234
  * Used to detect rename when `id` changes. */
@@ -13242,7 +13241,7 @@ var SourceInfoSchema = object({
13242
13241
  /**
13243
13242
  * Build the synthetic SourceInfo every existing provider falls back to
13244
13243
  * when no upstream value has been persisted. Keeps non-migrated providers
13245
- * (Reolink / Hikvision / ONVIF / Frigate / RTSP) functional without code
13244
+ * (Reolink / Hikvision / ONVIF / RTSP) functional without code
13246
13245
  * changes — `id` reuses the CamStack stableId, `system` reuses the addon
13247
13246
  * id. Real upstream identity replaces this once a provider migrates and
13248
13247
  * calls `updateSourceInfo()` with concrete values.
@@ -13904,7 +13903,7 @@ var deviceProviderCapability = {
13904
13903
  * - `device-management.router.ts` (deleted in Phase 2)
13905
13904
  * - `device-ops.router.ts` (compat layer — deleted; device-provider ops absorbed here)
13906
13905
  *
13907
- * All device provider addons (rtsp, onvif, frigate, …) are hub-local: they may
13906
+ * All device provider addons (rtsp, onvif, …) are hub-local: they may
13908
13907
  * fork into separate processes but never run on remote cluster agents. Therefore:
13909
13908
  * - No nodeId routing needed — this is a pure hub singleton.
13910
13909
  * - The hub's DeviceRegistry is the single source of truth for all live devices.
@@ -17992,7 +17991,7 @@ var NcRuleInputSchema = object({
17992
17991
  * The measured seven-person arrival on device 590 spans 110 s with every
17993
17992
  * internal gap under 30 s. A 12 s fixed window cuts it into three groups; an
17994
17993
  * idle cutoff holds it as one and ends it when the arrival actually ends.
17995
- * 30 is Frigate's shipped value for the same decision.
17994
+ * 30 s is a widely shipped value for the same decision.
17996
17995
  *
17997
17996
  * ### What it replaces
17998
17997
  *
@@ -20074,6 +20073,13 @@ var ZoneCrossingSchema = object({
20074
20073
  /** Zone display name at crossing time (falls back to the id). */
20075
20074
  zoneName: string().optional()
20076
20075
  });
20076
+ /** One zone a box was stamped with, and how much of the BOX fell inside it. */
20077
+ var ZoneOverlapSchema = object({
20078
+ zoneId: string(),
20079
+ zoneName: string().optional(),
20080
+ /** Percentage of the BOX area inside the zone, 0–100. */
20081
+ overlapPct: number()
20082
+ });
20077
20083
  var ObjectEventSchema = object({
20078
20084
  ...BaseEventFields,
20079
20085
  kind: literal("object"),
@@ -20098,6 +20104,35 @@ var ObjectEventSchema = object({
20098
20104
  bbox: BoundingBoxSchema.optional(),
20099
20105
  /** Heavy JSON — omitted in slim projection. */
20100
20106
  zones: array(string()).readonly().optional(),
20107
+ /**
20108
+ * How far inside each stamped zone the box actually was, as a percentage of
20109
+ * the BOX area.
20110
+ *
20111
+ * Membership alone cannot express "at least a third of it inside", which is
20112
+ * the bar an operator argues with — and the number already existed: the
20113
+ * engine computed it, the pipeline logged it, and then discarded it. It
20114
+ * matters most on the events that carry no track, where a rule has nothing
20115
+ * else to judge by.
20116
+ */
20117
+ zoneOverlaps: array(ZoneOverlapSchema).readonly().optional(),
20118
+ /**
20119
+ * Present when this event describes a detection the TRACKER refused, so it
20120
+ * has no track and never will.
20121
+ *
20122
+ * A zone-scoped immediate rule asks "is there an animal in the flowerbed
20123
+ * NOW?"; tracking answers a different question ("is this the same subject as
20124
+ * before?") whose confirmation threshold presumes continuity. On 2026-09-09 a
20125
+ * dog at 0.7729, 100% inside a watched zone, was refused three times in six
20126
+ * seconds with three different candidate ids — seven analysed frames in the
20127
+ * window, the subject in a handful of them, metres apart — and produced no
20128
+ * event at all.
20129
+ *
20130
+ * It is NAMED rather than merely implied by a missing `trackId`: an event
20131
+ * with no track and no reason is indistinguishable from a track whose media
20132
+ * went missing, and a consumer that asks this one for a best shot or a
20133
+ * history is asking for something that never existed.
20134
+ */
20135
+ spawnRefused: string().optional(),
20101
20136
  /** Omitted in slim projection. */
20102
20137
  state: TrackStateSchema.optional(),
20103
20138
  /**
@@ -21171,6 +21206,60 @@ var NativeCropResultSchema = object({
21171
21206
  */
21172
21207
  tier: _enum(["native", "ram-fullframe"]).optional()
21173
21208
  });
21209
+ /**
21210
+ * What a PARKED frame is for — the `kind` half of the `(deviceId, trackId,
21211
+ * kind)` key ([D422](../../../docs/decisions/adr-0422-a-relevant-frame-is-parked-when-it-becomes-relevant.md)).
21212
+ *
21213
+ * - `keyFrame` — the track's clean best FULL frame at native width (capped by
21214
+ * the caller's `maxWidth`). What `keyFrame` + `keyFrameSmall` are written
21215
+ * from, and what the detail plane's full-frame rung cuts from.
21216
+ * - `thumbnail` — the best-shot subject slab: the caller's 16:9 central-square
21217
+ * window, cut by the runner at uncapped native resolution.
21218
+ *
21219
+ * The runner never interprets `trackId`; it is an opaque key the hub owns.
21220
+ */
21221
+ var ParkedFrameKindSchema = _enum(["keyFrame", "thumbnail"]);
21222
+ /**
21223
+ * Why a park did NOT happen. Every one is logged on the runner with
21224
+ * `tags: { deviceId }` and the handle's measured age, because a handle already
21225
+ * gone at park time IS the defect the 150–500 ms window exists to prevent —
21226
+ * never a reason to retry in a loop.
21227
+ *
21228
+ * - `handle-gone` — the native surface no longer resolves this handle (never
21229
+ * registered, past the lease bound, or the worker released it). `detail`
21230
+ * carries the runner's own miss reason.
21231
+ * - `encode-failed` — pixels resolved and the JPEG encode threw.
21232
+ */
21233
+ var ParkRefusalSchema = _enum(["handle-gone", "encode-failed"]);
21234
+ /** Answer to `parkTrackFrame` — a discriminated union, so "not parked" always names why. */
21235
+ var ParkTrackFrameResultSchema = discriminatedUnion("parked", [object({
21236
+ parked: literal(true),
21237
+ /** The node holding the parcel — what a later retrieval must be pinned to. */
21238
+ nodeId: string(),
21239
+ width: number().int().positive(),
21240
+ height: number().int().positive(),
21241
+ /** JPEG bytes the parcel costs on the node. */
21242
+ bytes: number().int().nonnegative(),
21243
+ /** Whether this park replaced a parcel already held for the key. */
21244
+ replaced: boolean()
21245
+ }), object({
21246
+ parked: literal(false),
21247
+ nodeId: string(),
21248
+ reason: ParkRefusalSchema,
21249
+ /** Age of the handle at the park, on the runner's clock; null if unknown. */
21250
+ handleAgeMs: number().nullable(),
21251
+ detail: string().optional()
21252
+ })]);
21253
+ /** A retrieved parcel — the runner's own JPEG, base64 for the wire. */
21254
+ var ParkedTrackFrameSchema = object({
21255
+ jpeg: string(),
21256
+ width: number().int().positive(),
21257
+ height: number().int().positive(),
21258
+ /** The frame instant the parcel shows (the caller's clock, echoed back). */
21259
+ timestamp: number(),
21260
+ /** When the node stored it (the node's clock). */
21261
+ parkedAt: number()
21262
+ });
21174
21263
  /** Parent detection context passed to `runDetailSubtree` — the crop's
21175
21264
  * originating detection, in FRAME-space coordinates. Reuses
21176
21265
  * `NativeCropBboxSchema`'s `{x,y,w,h}` shape (same numeric fields; here
@@ -21560,7 +21649,7 @@ var RunnerCameraConfigSchema = object({
21560
21649
  * - `analyzer`: ML/frame-diff motion detection (motion-wasm or other
21561
21650
  * analyzer providers) running on decoded frames in this runner.
21562
21651
  * - `onboard`: camera-native motion pushes (Reolink Baichuan, ONVIF
21563
- * analytics, Frigate motion). The provider emits ProviderMotion
21652
+ * analytics). The provider emits ProviderMotion
21564
21653
  * events; the orchestrator forwards to `reportMotion`.
21565
21654
  */
21566
21655
  motionSources: MotionSourcesSchema.default(["analyzer"]),
@@ -21802,7 +21891,26 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
21802
21891
  className: string(),
21803
21892
  /** Camera the pixels came from. Diagnostics + log tags ONLY — never routing. */
21804
21893
  sourceDeviceId: number()
21805
- }), RunStatelessStepResultSchema, { kind: "mutation" });
21894
+ }), RunStatelessStepResultSchema, { kind: "mutation" }), method(object({
21895
+ deviceId: number(),
21896
+ /** Opaque to the runner — the hub's track id. */
21897
+ trackId: string(),
21898
+ kind: ParkedFrameKindSchema,
21899
+ handle: FrameHandleSchema,
21900
+ /** Normalized [0,1] window to cut. `{0,0,1,1}` for a full frame. */
21901
+ bbox: NativeCropBboxSchema,
21902
+ /** Width cap for a full-frame park (the `keyFrame` native cap). */
21903
+ maxWidth: number().int().positive().optional(),
21904
+ /** The frame instant (caller's clock) — orders parks for one key. */
21905
+ timestamp: number()
21906
+ }), ParkTrackFrameResultSchema, { kind: "mutation" }), method(object({
21907
+ deviceId: number(),
21908
+ trackId: string(),
21909
+ kind: ParkedFrameKindSchema
21910
+ }), ParkedTrackFrameSchema.nullable()), method(object({
21911
+ deviceId: number(),
21912
+ trackId: string()
21913
+ }), object({ released: number().int().nonnegative() }), { kind: "mutation" });
21806
21914
  var CameraPipelineConfigSchema = object({
21807
21915
  engine: PipelineEngineChoiceSchema.optional(),
21808
21916
  steps: array(PipelineStepInputSchema).readonly(),
@@ -24976,8 +25084,8 @@ onStatusChanged: { data: object({
24976
25084
  /**
24977
25085
  * Runtime-state slice — every provider that registers this cap
24978
25086
  * stores the same shape under `device.runtimeState[battery]`.
24979
- * Cross-provider uniformity: a Reolink Argus, a Frigate sensor
24980
- * proxy, an ONVIF battery cam all read/write the same keys.
25087
+ * Cross-provider uniformity: a Reolink Argus, a sensor proxy,
25088
+ * an ONVIF battery cam all read/write the same keys.
24981
25089
  * Consumers (BatteryBadge, snapshot wrapper sleep gate) read once
24982
25090
  * via `device.runtimeState.getCapState('battery')` regardless of
24983
25091
  * the underlying driver.
@@ -25110,7 +25218,7 @@ DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _v
25110
25218
  *
25111
25219
  * Implementations:
25112
25220
  * - Each camera-providing addon registers this cap natively per
25113
- * device (Reolink, Hikvision, RTSP, Frigate, ONVIF self).
25221
+ * device (Reolink, Hikvision, RTSP, ONVIF self).
25114
25222
  * - Returns a snapshot from the persisted device config blob — no
25115
25223
  * extra round-trip to the camera, no probing.
25116
25224
  * - The cap is admin-only (`auth: 'admin'`) — credentials must not
@@ -27202,7 +27310,7 @@ var AvailableIntegrationTypeSchema = object({
27202
27310
  * Which integration-marker cap the addon declared, so the wizard can
27203
27311
  * branch on CAP — never on addon name. `device-adoption` integrations
27204
27312
  * (Home Assistant, …) route through the broker step (Approach A);
27205
- * `device-provider` integrations (Reolink/Frigate/ONVIF) keep the
27313
+ * `device-provider` integrations (Reolink/ONVIF) keep the
27206
27314
  * legacy config → discovery flow.
27207
27315
  */
27208
27316
  kind: _enum(["device-adoption", "device-provider"]),
@@ -30733,13 +30841,31 @@ var RecordingManifestSchema = object({
30733
30841
  */
30734
30842
  playbackEndpoints: array(string())
30735
30843
  });
30736
- /** Recording storage usage for one camera (bytes on disk across all its
30737
- * profiles/subtrees/locations on this node). */
30844
+ /**
30845
+ * Recording storage usage for one camera — what the ARCHIVE holds for it,
30846
+ * across every profile and every resolvable location on this node.
30847
+ *
30848
+ * ## Archive vs residency, which is the whole point of this shape (D418)
30849
+ *
30850
+ * `usedBytes` / `oldestMs` / `segments` are the durable hour ledger's numbers:
30851
+ * the same quantity `RecordingLocationUsage.usedBytes` reports for the volume
30852
+ * this camera writes to, so the two can be added up and compared.
30853
+ *
30854
+ * They were not always. Until D418 this row came from the recorder's in-RAM
30855
+ * `RecordingIndex`, which since D287 holds only the live tail plus the hours
30856
+ * somebody hydrated — so a hub with 1.9 TB and 3.3 days of footage reported
30857
+ * 60.5 GB and "oldest: three hours ago" on the same page as the volume row
30858
+ * that said otherwise, and two people read it as retention eating recent
30859
+ * footage. Residency is still reported, under `residentBytes` /
30860
+ * `residentOldestMs`, which answer a different question and say so.
30861
+ */
30738
30862
  var RecordingDeviceUsageSchema = object({
30739
30863
  deviceId: number(),
30864
+ /** Bytes the ARCHIVE holds for this camera on this node's resolvable
30865
+ * recordings locations. */
30740
30866
  usedBytes: number(),
30741
30867
  /**
30742
- * Start of this camera's OLDEST indexed segment, across every profile and
30868
+ * Start of this camera's OLDEST ARCHIVED segment, across every profile and
30743
30869
  * location — the "Oldest footage" column in Recordings → Storage, and the
30744
30870
  * only honest answer to "is retention actually holding?" per camera.
30745
30871
  *
@@ -30749,7 +30875,24 @@ var RecordingDeviceUsageSchema = object({
30749
30875
  * (types) and the addon ship on different trains, and the addon is usually
30750
30876
  * the later of the two.
30751
30877
  */
30752
- oldestMs: number().nullable().optional()
30878
+ oldestMs: number().nullable().optional(),
30879
+ /** Segments the archive holds for this camera — the "M" beside `usedBytes`.
30880
+ * OPTIONAL for the same train-skew reason as `oldestMs`. */
30881
+ segments: number().optional(),
30882
+ /**
30883
+ * RESIDENCY, not the archive: bytes the recorder process currently holds in
30884
+ * its RAM index — the live tail plus the hours a reader hydrated.
30885
+ *
30886
+ * It answers "what can be served without touching the disk", never "how much
30887
+ * footage this camera has". A UI that renders it must label it as such, and
30888
+ * must never total it as storage. OPTIONAL: a recorder predating D418 omits
30889
+ * it, and so does a node whose ledger could not answer.
30890
+ */
30891
+ residentBytes: number().optional(),
30892
+ /** The residency counterpart of `oldestMs` — the oldest segment currently in
30893
+ * RAM, which on a healthy recorder is minutes ago and means nothing about
30894
+ * retention. `null` = nothing resident. */
30895
+ residentOldestMs: number().nullable().optional()
30753
30896
  });
30754
30897
  /** Recording storage usage + capacity for one storage location. */
30755
30898
  var RecordingLocationUsageSchema = object({
@@ -30787,6 +30930,17 @@ var RecordingStorageUsageSchema = object({
30787
30930
  totalUsedBytes: number(),
30788
30931
  devices: array(RecordingDeviceUsageSchema),
30789
30932
  /**
30933
+ * WHERE `devices[]` came from, so an empty table is never mistaken for empty
30934
+ * disks (D418).
30935
+ *
30936
+ * `archive` = the durable hour ledger answered. `unavailable` = this node
30937
+ * declared no ledger and the recorder REFUSED rather than answering the
30938
+ * question with the resident index (D295 rule 3) — the rows are UNKNOWN, not
30939
+ * zero, and a UI must say so. OPTIONAL: a recorder predating D418 omits it,
30940
+ * and its rows are residency.
30941
+ */
30942
+ devicesRead: _enum(["archive", "unavailable"]).optional(),
30943
+ /**
30790
30944
  * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
30791
30945
  * of the payload without it: the hub serves this method through a BAKED
30792
30946
  * router, so a hub whose framework train predates a change to this array
@@ -34879,7 +35033,7 @@ function toDeviceSummary(device, addonId) {
34879
35033
  };
34880
35034
  }
34881
35035
  /**
34882
- * Base class for device-provider addons (rtsp, onvif, frigate).
35036
+ * Base class for device-provider addons (rtsp, onvif).
34883
35037
  *
34884
35038
  * Provides default implementations for the common device-provider cap
34885
35039
  * methods (`start`, `stop`, `getStatus`, `getDevices`, `supportsDiscovery`,
@@ -39698,6 +39852,24 @@ Object.freeze({
39698
39852
  addonId: null,
39699
39853
  access: "view"
39700
39854
  },
39855
+ "pipelineRunner.getParkedTrackFrame": {
39856
+ capName: "pipeline-runner",
39857
+ capScope: "system",
39858
+ addonId: null,
39859
+ access: "view"
39860
+ },
39861
+ "pipelineRunner.parkTrackFrame": {
39862
+ capName: "pipeline-runner",
39863
+ capScope: "system",
39864
+ addonId: null,
39865
+ access: "create"
39866
+ },
39867
+ "pipelineRunner.releaseParkedTrackFrames": {
39868
+ capName: "pipeline-runner",
39869
+ capScope: "system",
39870
+ addonId: null,
39871
+ access: "create"
39872
+ },
39701
39873
  "pipelineRunner.reportMotion": {
39702
39874
  capName: "pipeline-runner",
39703
39875
  capScope: "system",
@@ -42975,6 +43147,21 @@ Object.freeze({
42975
43147
  form: "single",
42976
43148
  optional: false
42977
43149
  }],
43150
+ "pipelineRunner.getParkedTrackFrame": [{
43151
+ name: "deviceId",
43152
+ form: "single",
43153
+ optional: false
43154
+ }],
43155
+ "pipelineRunner.parkTrackFrame": [{
43156
+ name: "deviceId",
43157
+ form: "single",
43158
+ optional: false
43159
+ }],
43160
+ "pipelineRunner.releaseParkedTrackFrames": [{
43161
+ name: "deviceId",
43162
+ form: "single",
43163
+ optional: false
43164
+ }],
42978
43165
  "pipelineRunner.reportMotion": [{
42979
43166
  name: "deviceId",
42980
43167
  form: "single",
@@ -43666,7 +43853,7 @@ var PreviewTextField = string().max(200);
43666
43853
  * Whether the notification's preview is a STILL or a short animation.
43667
43854
  *
43668
43855
  * The operator's ask, verbatim: *"inviato come gif o video (come per le altre
43669
- * rule)"* — his Scrypted advanced-notifier has a `gifRule`, and a ten-hour
43856
+ * rule)"* — the other rules already offer an animated preview, and a ten-hour
43670
43857
  * night reads better as three seconds of motion than as one frame of it. Both
43671
43858
  * modes get the SAME treatment (blurred frame, large centred title); `'gif'`
43672
43859
  * simply applies it to a dozen frames sampled across the render and assembles
@@ -145536,12 +145723,10 @@ function mapNetworkReadout(readout, now) {
145536
145723
  //#region src/error-classifier.ts
145537
145724
  /**
145538
145725
  * Recognise transient Baichuan/socket failures that are expected to
145539
- * recover on the next reconnect cycle. Mirrors
145540
- * `scrypted-reolink-native/src/camera.ts isRecoverableBaichuanError`
145541
- * keeping parity so the same wire-level conditions are treated the
145542
- * same way across plugins.
145726
+ * recover on the next reconnect cycle — the same wire-level conditions
145727
+ * are always treated the same way.
145543
145728
  *
145544
- * Ported categories:
145729
+ * Categories:
145545
145730
  * - Baichuan-specific transport closes (`Baichuan socket closed`,
145546
145731
  * `Baichuan UDP stream closed`, `Baichuan TCP socket is not
145547
145732
  * connected`)
@@ -145611,9 +145796,7 @@ var resolveZoomStep = (travel, zoom) => {
145611
145796
  /**
145612
145797
  * IMA ADPCM (DVI4) encoder — Reolink Baichuan talk-back wire format.
145613
145798
  *
145614
- * Ported from `scrypted-reolink-native/src/intercom.ts` (BSD-2 like the
145615
- * rest of the cross-cam Scrypted infra). Reolink cameras expect ADPCM
145616
- * blocks of (4 + N) bytes:
145799
+ * Reolink cameras expect ADPCM blocks of (4 + N) bytes:
145617
145800
  * - 2 bytes: little-endian Int16 predictor (first PCM sample of the
145618
145801
  * block, used to seed the decoder state on the camera side)
145619
145802
  * - 1 byte: index into the IMA step table (always 0 — we re-seed
@@ -145997,14 +146180,12 @@ var ReolinkIntercomSession = class {
145997
146180
  }
145998
146181
  }
145999
146182
  };
146000
- /** Clamp `blocksPerPayload` into the lib's accepted range. Mirrors
146001
- * scrypted-reolink-native's `intercom-mixin.ts:96-101` clamp. */
146183
+ /** Clamp `blocksPerPayload` into the lib's accepted range. */
146002
146184
  function clampBlocks(value) {
146003
146185
  if (value === void 0 || !Number.isFinite(value)) return DEFAULT_BLOCKS_PER_PAYLOAD;
146004
146186
  return Math.max(1, Math.min(8, Math.floor(value)));
146005
146187
  }
146006
- /** Clamp `outputGain` into the operator-safe range. Same `[0.1, 10]`
146007
- * band as Scrypted (`intercom-mixin.ts:104-107`). */
146188
+ /** Clamp `outputGain` into the operator-safe `[0.1, 10]` band. */
146008
146189
  function clampGain(value) {
146009
146190
  if (value === void 0 || !Number.isFinite(value)) return DEFAULT_GAIN;
146010
146191
  return Math.max(MIN_GAIN, Math.min(MAX_GAIN, value));
@@ -146603,9 +146784,8 @@ var ReolinkUdpDiscoveryMethodSchema = _enum([
146603
146784
  "map"
146604
146785
  ]);
146605
146786
  /**
146606
- * Lib-side socket debug categories. Mirrors scrypted-reolink-native's
146607
- * `DebugLogOption` (debug-options.ts) same names, same semantics, so
146608
- * users coming from Scrypted recognize them. Each flag is forwarded
146787
+ * Lib-side socket debug categories the names and semantics the lib
146788
+ * itself uses, so they are recognizable. Each flag is forwarded
146609
146789
  * verbatim into the `DebugOptions` blob `ReolinkBaichuanApi` accepts.
146610
146790
  */
146611
146791
  var ReolinkSocketDebugFlagSchema = _enum([
@@ -147007,14 +147187,12 @@ var reolinkCameraSchema = object({
147007
147187
  }).loose().optional(),
147008
147188
  /**
147009
147189
  * Generic Baichuan debug logs. Forwarded as `DebugOptions.general`
147010
- * on the lib's `ReolinkBaichuanApi` constructor. Mirrors Scrypted's
147011
- * `debugLogs` boolean.
147190
+ * on the lib's `ReolinkBaichuanApi` constructor.
147012
147191
  */
147013
147192
  debugGeneral: boolean().default(false),
147014
147193
  /**
147015
147194
  * Lib-side socket trace categories. Each flag flips the matching
147016
- * `DebugOptions.<flag>` on the api. Mirrors Scrypted's
147017
- * `socketApiDebugLogs` multi-select.
147195
+ * `DebugOptions.<flag>` on the api.
147018
147196
  */
147019
147197
  debugSocketLogs: array(ReolinkSocketDebugFlagSchema).default([]),
147020
147198
  /**
@@ -147269,21 +147447,20 @@ var reolinkCameraSchema = object({
147269
147447
  /**
147270
147448
  * Intercom (browser→cam talk channel) tuning. Only consumed
147271
147449
  * when the camera advertises two-way audio (`hasIntercom` in the
147272
- * feature-probe slice). Mirrors scrypted-reolink-native's
147273
- * `intercom-mixin.ts` storage settings same names, same ranges,
147274
- * same defaults so the user-facing latency matches Scrypted's.
147450
+ * feature-probe slice). The names, ranges and defaults are the ones
147451
+ * the lib itself documents, so the user-facing latency is predictable.
147275
147452
  *
147276
147453
  * `intercomBlocksPerPayload` — number of IMA-ADPCM blocks bundled
147277
147454
  * into each `sendAudio` call against the lib's dedicated talk
147278
147455
  * session. Lower = smaller chunks, more sends/sec, lower latency.
147279
147456
  * Higher = fewer sends, less per-call socket overhead. Default 1
147280
- * = lowest latency, matching Scrypted.
147457
+ * = lowest latency.
147281
147458
  */
147282
147459
  intercomBlocksPerPayload: number().int().min(1).max(8).optional(),
147283
147460
  /**
147284
147461
  * `intercomMaxBacklogMs` — bound on the PCM backlog before the
147285
- * pump drops oldest samples to cap latency. Default 120ms matches
147286
- * Scrypted; raise via the Settings UI on systems where the cross-
147462
+ * pump drops oldest samples to cap latency. Default 120ms; raise
147463
+ * via the Settings UI on systems where the cross-
147287
147464
  * process Opus→PCM path is bursty enough to trigger frequent
147288
147465
  * `intercom backlog clamped` warnings.
147289
147466
  */
@@ -147303,8 +147480,7 @@ var reolinkCameraSchema = object({
147303
147480
  * instances; this schema only carries connection + debug + the cached
147304
147481
  * channels summary so we can re-render the Discovery panel offline.
147305
147482
  *
147306
- * Mirrors `scrypted-reolink-native`'s `ReolinkNativeNvrDevice`
147307
- * `storageSettings` (`nvr.ts:49-178`): same scope, same fields, just
147483
+ * The NVR device's own settings scope and fields, just
147308
147484
  * adapted to the Zod conventions used elsewhere in this package.
147309
147485
  */
147310
147486
  var reolinkHubSchema = object({
@@ -148085,8 +148261,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
148085
148261
  objectDetectionsSubscribed = false;
148086
148262
  /**
148087
148263
  * Plugin-level event-health interval — fallback layer over the
148088
- * lib's internal 5-minute watchdog. Mirrors Scrypted's
148089
- * `startEventCheck` (`baichuan-base.ts:719-783`): every 60 s,
148264
+ * lib's internal 5-minute watchdog. The rule: every 60 s,
148090
148265
  * if `eventSubscriptionActive` is false on a live socket OR no
148091
148266
  * event has arrived in 10 min, force a full unsub/resub cycle.
148092
148267
  */
@@ -148201,9 +148376,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
148201
148376
  /** Aux accessory references registered by spawned children
148202
148377
  * (siren / floodlight / pir). The parent's centralised align
148203
148378
  * method iterates these to refresh their slices on a single
148204
- * cadence — replaces the old per-accessory setInterval. Mirrors
148205
- * scrypted-reolink-native's `motionSiren` / `floodlight` / `pir`
148206
- * field refs + their `alignAuxDevicesState` method. */
148379
+ * cadence — replaces the old per-accessory setInterval. */
148207
148380
  auxAccessoryRefs = /* @__PURE__ */ new Map();
148208
148381
  /**
148209
148382
  * Reliability watchdogs (Slice 10). Both timers are armed after a
@@ -149008,8 +149181,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
149008
149181
  * Single-flight guard for snapshot fetches (Slice 10). When two
149009
149182
  * consumers hit `getSnapshot` concurrently we issue ONE Baichuan
149010
149183
  * request and let both await its result — saves a doubled wake on
149011
- * battery cams and halves load for regular ones. Pattern mirrors
149012
- * `takePictureInFlight` in scrypted-reolink-native.
149184
+ * battery cams and halves load for regular ones.
149013
149185
  */
149014
149186
  snapshotInFlight = null;
149015
149187
  /**
@@ -151342,9 +151514,8 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
151342
151514
  };
151343
151515
  }
151344
151516
  /**
151345
- * Battery-cam pre-wake hook for the intercom orchestrator. Mirrors
151346
- * scrypted-reolink-native's `intercom.ts:90-106`: ask the lib for
151347
- * the current sleep status (passive — no traffic), and if the cam
151517
+ * Battery-cam pre-wake hook for the intercom orchestrator: ask the
151518
+ * lib for the current sleep status (passive — no traffic), and if the cam
151348
151519
  * is asleep issue an explicit `wakeUp(channel, {waitAfterWakeMs:2000})`
151349
151520
  * + a 1s settle delay before letting the talk-session handshake
151350
151521
  * fire. Without this, the handshake against a sleeping UDP/battery
@@ -151884,7 +152055,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
151884
152055
  /**
151885
152056
  * Synthesize the HTTP-FLV pull URL for a (channel, profile) pair. FLV is
151886
152057
  * served off the Reolink Baichuan media port (default 1935, app `bcs`) as
151887
- * `channel<channel>_<profile>.bcs` — the same shape Frigate/go2rtc use. The
152058
+ * `channel<channel>_<profile>.bcs` — the same shape go2rtc uses. The
151888
152059
  * lib does NOT surface this URL, so we build it from the SAME connection
151889
152060
  * creds the RTSP/native paths use (`host`/`username`/`password` on the
151890
152061
  * device config), URL-encoding the credentials.
@@ -152735,9 +152906,8 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
152735
152906
  /**
152736
152907
  * Shared wake-transition handler invoked by both the simpleEvent
152737
152908
  * `awake` push (canonical fast path) and the sleep poll's
152738
- * `sleeping → awake` flip (backstop). Mirrors Scrypted's
152739
- * `updateSleepingState`'s wake branch (camera.ts:3543-3560) fan
152740
- * out the refreshes that depend on the camera being responsive:
152909
+ * `sleeping → awake` flip (backstop). Fan out the refreshes that
152910
+ * depend on the camera being responsive:
152741
152911
  * - aux accessories (siren/floodlight/PIR) re-read their
152742
152912
  * firmware state via `alignAuxDevicesState('wake')`
152743
152913
  * - parent settings snapshot (image / motion / AI / enc / mask /
@@ -152886,8 +153056,6 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
152886
153056
  * 3. Register via `onSimpleEvent(handleSimpleEventBound)`.
152887
153057
  * Lib starts/refreshes its own subscribe + 5min watchdog.
152888
153058
  *
152889
- * Mirrors `subscribeToEventsInternal` in Scrypted's
152890
- * `baichuan-base.ts:837-888` — same pattern, same rationale.
152891
153059
  */
152892
153060
  async resubscribeSimpleEvents(api, reason) {
152893
153061
  try {
@@ -153764,7 +153932,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
153764
153932
  type: "slider",
153765
153933
  key: "intercomBlocksPerPayload",
153766
153934
  label: "Blocks per payload",
153767
- description: "Lower = lower latency (more sendAudio calls/sec). Higher = fewer calls, less per-call socket overhead. Range 1..8. Default 1 (Scrypted-parity, lowest latency).",
153935
+ description: "Lower = lower latency (more sendAudio calls/sec). Higher = fewer calls, less per-call socket overhead. Range 1..8. Default 1 (lowest latency).",
153768
153936
  min: 1,
153769
153937
  max: 8,
153770
153938
  step: 1,
@@ -153776,7 +153944,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
153776
153944
  type: "slider",
153777
153945
  key: "intercomMaxBacklogMs",
153778
153946
  label: "Max backlog (ms)",
153779
- description: "Maximum PCM backlog before dropping oldest samples to cap latency. Higher = more stable on slow systems but more latency. Range 20..5000. Default 120 (Scrypted-parity).",
153947
+ description: "Maximum PCM backlog before dropping oldest samples to cap latency. Higher = more stable on slow systems but more latency. Range 20..5000. Default 120.",
153780
153948
  min: 20,
153781
153949
  max: 5e3,
153782
153950
  step: 20,
@@ -153911,7 +154079,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
153911
154079
  id: "debug",
153912
154080
  tab: "advanced",
153913
154081
  title: "Debug",
153914
- description: "Lib-side Baichuan tracing. Mirrors scrypted-reolink-native. Changing these resets the control-channel client (active streams keep running) so new debug options take effect on the next dial.",
154082
+ description: "Lib-side Baichuan tracing. Changing these resets the control-channel client (active streams keep running) so new debug options take effect on the next dial.",
153915
154083
  columns: 1,
153916
154084
  fields: [{
153917
154085
  type: "boolean",
@@ -154605,9 +154773,8 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
154605
154773
  return this.loginPromise;
154606
154774
  }
154607
154775
  /**
154608
- * Bridge Reolink hardware events to the camstack event bus. Mirrors
154609
- * the dispatch shape of scrypted-reolink-native's `onSimpleEvent`
154610
- * (`camera.ts:1979-2033`): online and sleeping are TWO ORTHOGONAL
154776
+ * Bridge Reolink hardware events to the camstack event bus:
154777
+ * online and sleeping are TWO ORTHOGONAL
154611
154778
  * axes, each driven exclusively by their own firmware push events.
154612
154779
  *
154613
154780
  * - 'motion' + AI types (people, vehicle, animal, face, package):
@@ -154636,8 +154803,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
154636
154803
  }
154637
154804
  /**
154638
154805
  * Build the `DebugOptions` blob forwarded to `ReolinkBaichuanApi`'s
154639
- * constructor. Mirrors Scrypted's `getBaichuanDebugOptions` at
154640
- * `camera.ts:1778-1786`. Returns `undefined` when no flags are set
154806
+ * constructor. Returns `undefined` when no flags are set
154641
154807
  * so the lib falls back to its own defaults instead of an empty
154642
154808
  * object override.
154643
154809
  */
@@ -154896,8 +155062,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
154896
155062
  * idle UDP socket on its own, the camera is asleep, and there is
154897
155063
  * nothing to reconnect to. Aggressively reconnecting in that state
154898
155064
  * is what was waking up the doorbell every ~60 s in production
154899
- * logs. Mirror Scrypted's reolink-native handler: drop the api
154900
- * reference, but DO NOT schedule a reconnect timer. The next
155065
+ * logs. Drop the api reference, but DO NOT schedule a reconnect timer. The next
154901
155066
  * `awake` push (or a snapshot demand from the operator) will
154902
155067
  * lazily call `ensureApi()` and re-establish the client.
154903
155068
  */
@@ -155010,8 +155175,8 @@ function isHubLike(d) {
155010
155175
  * starts at 60s) OR the subscription is genuinely broken / nothing is
155011
155176
  * happening in front of the cam (counter rises → backoff lengthens).
155012
155177
  *
155013
- * Schedule: 60s → 2min → 5min → 15min capped. Mirrors the long-poll
155014
- * backoff curves in scrypted-reolink-native + reolink_aio.
155178
+ * Schedule: 60s → 2min → 5min → 15min capped the long-poll backoff
155179
+ * curve this hardware is known to tolerate.
155015
155180
  */
155016
155181
  function computeHealthCheckBackoffMs(consecutive) {
155017
155182
  if (consecutive <= 1) return 6e4;
@@ -155155,7 +155320,7 @@ var HUB_RECONNECT_MAX_MS = 3e4;
155155
155320
  /**
155156
155321
  * Reolink Hub / NVR — single Baichuan socket fronting N channels.
155157
155322
  *
155158
- * Mirrors the Scrypted `ReolinkNativeNvrDevice` pattern:
155323
+ * The NVR pattern:
155159
155324
  * - One persistent control + event socket on the parent (no
155160
155325
  * per-channel login).
155161
155326
  * - `device-discovery` cap exposes the channel list with status,
@@ -155308,8 +155473,7 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
155308
155473
  }
155309
155474
  }
155310
155475
  /**
155311
- * Hub-specific settings form. Mirrors the Scrypted
155312
- * `ReolinkNativeNvrDevice.storageSettings` block (`nvr.ts:49-178`):
155476
+ * Hub-specific settings form:
155313
155477
  * connection (host / port / credentials) on the General tab,
155314
155478
  * Baichuan debug flags on Advanced. Per-channel settings (image,
155315
155479
  * motion, AI sensitivity, …) live on the adopted child cameras —
@@ -155401,7 +155565,7 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
155401
155565
  id: "debug",
155402
155566
  tab: "advanced",
155403
155567
  title: "Debug",
155404
- description: "Lib-side Baichuan tracing. Mirrors `scrypted-reolink-native`. Changing these resets the control-channel client (active streams keep running) so new debug options take effect on the next dial.",
155568
+ description: "Lib-side Baichuan tracing. Changing these resets the control-channel client (active streams keep running) so new debug options take effect on the next dial.",
155405
155569
  columns: 1,
155406
155570
  fields: [{
155407
155571
  type: "boolean",
@@ -155564,8 +155728,8 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
155564
155728
  }
155565
155729
  /**
155566
155730
  * Pull the channel list from the Hub via the lib's
155567
- * `getNvrChannelsSummary({ source: 'cgi' })`. CGI source mirrors
155568
- * Scrypted's choice: `GetChannelstatus` returns the channel list
155731
+ * `getNvrChannelsSummary({ source: 'cgi' })`. CGI is chosen because
155732
+ * `GetChannelstatus` returns the channel list
155569
155733
  * synchronously (no race against the cmd_id 145 push cache that
155570
155734
  * can be empty right after login).
155571
155735
  *
@@ -155957,8 +156121,7 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
155957
156121
  }
155958
156122
  /**
155959
156123
  * Dispatches a Hub-received simpleEvent to the matching child's
155960
- * `handleSimpleEvent`. Mirrors Scrypted's
155961
- * `ReolinkNativeNvrDevice.onSimpleEvent` (`nvr.ts:288-319`).
156124
+ * `handleSimpleEvent`.
155962
156125
  */
155963
156126
  routeSimpleEvent(ev) {
155964
156127
  const channel = ev?.channel;
@@ -156116,7 +156279,7 @@ function computeChildNativeId(parentStableId, channel, uid) {
156116
156279
  }
156117
156280
  /**
156118
156281
  * Map the lib's per-channel summary booleans to our `DiscoveredChildStatus`.
156119
- * Order of precedence mirrors the Scrypted handling:
156282
+ * Order of precedence:
156120
156283
  * 1. `online === false` → offline
156121
156284
  * 2. `sleeping === true` → sleeping
156122
156285
  * 3. anything else with non-empty state → online