@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.mjs CHANGED
@@ -20,7 +20,7 @@ import * as net2 from "net";
20
20
  import netImpl from "net";
21
21
  import { mkdir } from "fs/promises";
22
22
  import os from "node:os";
23
- //#region ../types/dist/event-category-CnLqLOKs.mjs
23
+ //#region ../types/dist/event-category-DAXzJeTX.mjs
24
24
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
25
25
  EventCategory["SystemBoot"] = "system.boot";
26
26
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -612,7 +612,6 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
612
612
  * issue. Payload `PipelineAnalyticsPlateGalleryChangedPayload`.
613
613
  */
614
614
  EventCategory["PipelineAnalyticsPlateGalleryChanged"] = "pipeline-analytics.plate-gallery-changed";
615
- EventCategory["FrigateLiveEvent"] = "frigate.live-event";
616
615
  EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
617
616
  /**
618
617
  * Stream-broker health watchdog. Per-broker (deviceId/profile) emission.
@@ -13224,7 +13223,7 @@ DeviceType.Camera;
13224
13223
  var SourceInfoSchema = object({
13225
13224
  /** Live dispatch key — mutable when the source system allows rename. */
13226
13225
  id: string(),
13227
- /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'frigate'. */
13226
+ /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'onvif'. */
13228
13227
  system: string(),
13229
13228
  /** Immutable upstream identifier when available (HA `unique_id`, … ).
13230
13229
  * Used to detect rename when `id` changes. */
@@ -13237,7 +13236,7 @@ var SourceInfoSchema = object({
13237
13236
  /**
13238
13237
  * Build the synthetic SourceInfo every existing provider falls back to
13239
13238
  * when no upstream value has been persisted. Keeps non-migrated providers
13240
- * (Reolink / Hikvision / ONVIF / Frigate / RTSP) functional without code
13239
+ * (Reolink / Hikvision / ONVIF / RTSP) functional without code
13241
13240
  * changes — `id` reuses the CamStack stableId, `system` reuses the addon
13242
13241
  * id. Real upstream identity replaces this once a provider migrates and
13243
13242
  * calls `updateSourceInfo()` with concrete values.
@@ -13899,7 +13898,7 @@ var deviceProviderCapability = {
13899
13898
  * - `device-management.router.ts` (deleted in Phase 2)
13900
13899
  * - `device-ops.router.ts` (compat layer — deleted; device-provider ops absorbed here)
13901
13900
  *
13902
- * All device provider addons (rtsp, onvif, frigate, …) are hub-local: they may
13901
+ * All device provider addons (rtsp, onvif, …) are hub-local: they may
13903
13902
  * fork into separate processes but never run on remote cluster agents. Therefore:
13904
13903
  * - No nodeId routing needed — this is a pure hub singleton.
13905
13904
  * - The hub's DeviceRegistry is the single source of truth for all live devices.
@@ -17987,7 +17986,7 @@ var NcRuleInputSchema = object({
17987
17986
  * The measured seven-person arrival on device 590 spans 110 s with every
17988
17987
  * internal gap under 30 s. A 12 s fixed window cuts it into three groups; an
17989
17988
  * idle cutoff holds it as one and ends it when the arrival actually ends.
17990
- * 30 is Frigate's shipped value for the same decision.
17989
+ * 30 s is a widely shipped value for the same decision.
17991
17990
  *
17992
17991
  * ### What it replaces
17993
17992
  *
@@ -20069,6 +20068,13 @@ var ZoneCrossingSchema = object({
20069
20068
  /** Zone display name at crossing time (falls back to the id). */
20070
20069
  zoneName: string().optional()
20071
20070
  });
20071
+ /** One zone a box was stamped with, and how much of the BOX fell inside it. */
20072
+ var ZoneOverlapSchema = object({
20073
+ zoneId: string(),
20074
+ zoneName: string().optional(),
20075
+ /** Percentage of the BOX area inside the zone, 0–100. */
20076
+ overlapPct: number()
20077
+ });
20072
20078
  var ObjectEventSchema = object({
20073
20079
  ...BaseEventFields,
20074
20080
  kind: literal("object"),
@@ -20093,6 +20099,35 @@ var ObjectEventSchema = object({
20093
20099
  bbox: BoundingBoxSchema.optional(),
20094
20100
  /** Heavy JSON — omitted in slim projection. */
20095
20101
  zones: array(string()).readonly().optional(),
20102
+ /**
20103
+ * How far inside each stamped zone the box actually was, as a percentage of
20104
+ * the BOX area.
20105
+ *
20106
+ * Membership alone cannot express "at least a third of it inside", which is
20107
+ * the bar an operator argues with — and the number already existed: the
20108
+ * engine computed it, the pipeline logged it, and then discarded it. It
20109
+ * matters most on the events that carry no track, where a rule has nothing
20110
+ * else to judge by.
20111
+ */
20112
+ zoneOverlaps: array(ZoneOverlapSchema).readonly().optional(),
20113
+ /**
20114
+ * Present when this event describes a detection the TRACKER refused, so it
20115
+ * has no track and never will.
20116
+ *
20117
+ * A zone-scoped immediate rule asks "is there an animal in the flowerbed
20118
+ * NOW?"; tracking answers a different question ("is this the same subject as
20119
+ * before?") whose confirmation threshold presumes continuity. On 2026-09-09 a
20120
+ * dog at 0.7729, 100% inside a watched zone, was refused three times in six
20121
+ * seconds with three different candidate ids — seven analysed frames in the
20122
+ * window, the subject in a handful of them, metres apart — and produced no
20123
+ * event at all.
20124
+ *
20125
+ * It is NAMED rather than merely implied by a missing `trackId`: an event
20126
+ * with no track and no reason is indistinguishable from a track whose media
20127
+ * went missing, and a consumer that asks this one for a best shot or a
20128
+ * history is asking for something that never existed.
20129
+ */
20130
+ spawnRefused: string().optional(),
20096
20131
  /** Omitted in slim projection. */
20097
20132
  state: TrackStateSchema.optional(),
20098
20133
  /**
@@ -21166,6 +21201,60 @@ var NativeCropResultSchema = object({
21166
21201
  */
21167
21202
  tier: _enum(["native", "ram-fullframe"]).optional()
21168
21203
  });
21204
+ /**
21205
+ * What a PARKED frame is for — the `kind` half of the `(deviceId, trackId,
21206
+ * kind)` key ([D422](../../../docs/decisions/adr-0422-a-relevant-frame-is-parked-when-it-becomes-relevant.md)).
21207
+ *
21208
+ * - `keyFrame` — the track's clean best FULL frame at native width (capped by
21209
+ * the caller's `maxWidth`). What `keyFrame` + `keyFrameSmall` are written
21210
+ * from, and what the detail plane's full-frame rung cuts from.
21211
+ * - `thumbnail` — the best-shot subject slab: the caller's 16:9 central-square
21212
+ * window, cut by the runner at uncapped native resolution.
21213
+ *
21214
+ * The runner never interprets `trackId`; it is an opaque key the hub owns.
21215
+ */
21216
+ var ParkedFrameKindSchema = _enum(["keyFrame", "thumbnail"]);
21217
+ /**
21218
+ * Why a park did NOT happen. Every one is logged on the runner with
21219
+ * `tags: { deviceId }` and the handle's measured age, because a handle already
21220
+ * gone at park time IS the defect the 150–500 ms window exists to prevent —
21221
+ * never a reason to retry in a loop.
21222
+ *
21223
+ * - `handle-gone` — the native surface no longer resolves this handle (never
21224
+ * registered, past the lease bound, or the worker released it). `detail`
21225
+ * carries the runner's own miss reason.
21226
+ * - `encode-failed` — pixels resolved and the JPEG encode threw.
21227
+ */
21228
+ var ParkRefusalSchema = _enum(["handle-gone", "encode-failed"]);
21229
+ /** Answer to `parkTrackFrame` — a discriminated union, so "not parked" always names why. */
21230
+ var ParkTrackFrameResultSchema = discriminatedUnion("parked", [object({
21231
+ parked: literal(true),
21232
+ /** The node holding the parcel — what a later retrieval must be pinned to. */
21233
+ nodeId: string(),
21234
+ width: number().int().positive(),
21235
+ height: number().int().positive(),
21236
+ /** JPEG bytes the parcel costs on the node. */
21237
+ bytes: number().int().nonnegative(),
21238
+ /** Whether this park replaced a parcel already held for the key. */
21239
+ replaced: boolean()
21240
+ }), object({
21241
+ parked: literal(false),
21242
+ nodeId: string(),
21243
+ reason: ParkRefusalSchema,
21244
+ /** Age of the handle at the park, on the runner's clock; null if unknown. */
21245
+ handleAgeMs: number().nullable(),
21246
+ detail: string().optional()
21247
+ })]);
21248
+ /** A retrieved parcel — the runner's own JPEG, base64 for the wire. */
21249
+ var ParkedTrackFrameSchema = object({
21250
+ jpeg: string(),
21251
+ width: number().int().positive(),
21252
+ height: number().int().positive(),
21253
+ /** The frame instant the parcel shows (the caller's clock, echoed back). */
21254
+ timestamp: number(),
21255
+ /** When the node stored it (the node's clock). */
21256
+ parkedAt: number()
21257
+ });
21169
21258
  /** Parent detection context passed to `runDetailSubtree` — the crop's
21170
21259
  * originating detection, in FRAME-space coordinates. Reuses
21171
21260
  * `NativeCropBboxSchema`'s `{x,y,w,h}` shape (same numeric fields; here
@@ -21555,7 +21644,7 @@ var RunnerCameraConfigSchema = object({
21555
21644
  * - `analyzer`: ML/frame-diff motion detection (motion-wasm or other
21556
21645
  * analyzer providers) running on decoded frames in this runner.
21557
21646
  * - `onboard`: camera-native motion pushes (Reolink Baichuan, ONVIF
21558
- * analytics, Frigate motion). The provider emits ProviderMotion
21647
+ * analytics). The provider emits ProviderMotion
21559
21648
  * events; the orchestrator forwards to `reportMotion`.
21560
21649
  */
21561
21650
  motionSources: MotionSourcesSchema.default(["analyzer"]),
@@ -21797,7 +21886,26 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
21797
21886
  className: string(),
21798
21887
  /** Camera the pixels came from. Diagnostics + log tags ONLY — never routing. */
21799
21888
  sourceDeviceId: number()
21800
- }), RunStatelessStepResultSchema, { kind: "mutation" });
21889
+ }), RunStatelessStepResultSchema, { kind: "mutation" }), method(object({
21890
+ deviceId: number(),
21891
+ /** Opaque to the runner — the hub's track id. */
21892
+ trackId: string(),
21893
+ kind: ParkedFrameKindSchema,
21894
+ handle: FrameHandleSchema,
21895
+ /** Normalized [0,1] window to cut. `{0,0,1,1}` for a full frame. */
21896
+ bbox: NativeCropBboxSchema,
21897
+ /** Width cap for a full-frame park (the `keyFrame` native cap). */
21898
+ maxWidth: number().int().positive().optional(),
21899
+ /** The frame instant (caller's clock) — orders parks for one key. */
21900
+ timestamp: number()
21901
+ }), ParkTrackFrameResultSchema, { kind: "mutation" }), method(object({
21902
+ deviceId: number(),
21903
+ trackId: string(),
21904
+ kind: ParkedFrameKindSchema
21905
+ }), ParkedTrackFrameSchema.nullable()), method(object({
21906
+ deviceId: number(),
21907
+ trackId: string()
21908
+ }), object({ released: number().int().nonnegative() }), { kind: "mutation" });
21801
21909
  var CameraPipelineConfigSchema = object({
21802
21910
  engine: PipelineEngineChoiceSchema.optional(),
21803
21911
  steps: array(PipelineStepInputSchema).readonly(),
@@ -24971,8 +25079,8 @@ onStatusChanged: { data: object({
24971
25079
  /**
24972
25080
  * Runtime-state slice — every provider that registers this cap
24973
25081
  * stores the same shape under `device.runtimeState[battery]`.
24974
- * Cross-provider uniformity: a Reolink Argus, a Frigate sensor
24975
- * proxy, an ONVIF battery cam all read/write the same keys.
25082
+ * Cross-provider uniformity: a Reolink Argus, a sensor proxy,
25083
+ * an ONVIF battery cam all read/write the same keys.
24976
25084
  * Consumers (BatteryBadge, snapshot wrapper sleep gate) read once
24977
25085
  * via `device.runtimeState.getCapState('battery')` regardless of
24978
25086
  * the underlying driver.
@@ -25105,7 +25213,7 @@ DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _v
25105
25213
  *
25106
25214
  * Implementations:
25107
25215
  * - Each camera-providing addon registers this cap natively per
25108
- * device (Reolink, Hikvision, RTSP, Frigate, ONVIF self).
25216
+ * device (Reolink, Hikvision, RTSP, ONVIF self).
25109
25217
  * - Returns a snapshot from the persisted device config blob — no
25110
25218
  * extra round-trip to the camera, no probing.
25111
25219
  * - The cap is admin-only (`auth: 'admin'`) — credentials must not
@@ -27197,7 +27305,7 @@ var AvailableIntegrationTypeSchema = object({
27197
27305
  * Which integration-marker cap the addon declared, so the wizard can
27198
27306
  * branch on CAP — never on addon name. `device-adoption` integrations
27199
27307
  * (Home Assistant, …) route through the broker step (Approach A);
27200
- * `device-provider` integrations (Reolink/Frigate/ONVIF) keep the
27308
+ * `device-provider` integrations (Reolink/ONVIF) keep the
27201
27309
  * legacy config → discovery flow.
27202
27310
  */
27203
27311
  kind: _enum(["device-adoption", "device-provider"]),
@@ -30728,13 +30836,31 @@ var RecordingManifestSchema = object({
30728
30836
  */
30729
30837
  playbackEndpoints: array(string())
30730
30838
  });
30731
- /** Recording storage usage for one camera (bytes on disk across all its
30732
- * profiles/subtrees/locations on this node). */
30839
+ /**
30840
+ * Recording storage usage for one camera — what the ARCHIVE holds for it,
30841
+ * across every profile and every resolvable location on this node.
30842
+ *
30843
+ * ## Archive vs residency, which is the whole point of this shape (D418)
30844
+ *
30845
+ * `usedBytes` / `oldestMs` / `segments` are the durable hour ledger's numbers:
30846
+ * the same quantity `RecordingLocationUsage.usedBytes` reports for the volume
30847
+ * this camera writes to, so the two can be added up and compared.
30848
+ *
30849
+ * They were not always. Until D418 this row came from the recorder's in-RAM
30850
+ * `RecordingIndex`, which since D287 holds only the live tail plus the hours
30851
+ * somebody hydrated — so a hub with 1.9 TB and 3.3 days of footage reported
30852
+ * 60.5 GB and "oldest: three hours ago" on the same page as the volume row
30853
+ * that said otherwise, and two people read it as retention eating recent
30854
+ * footage. Residency is still reported, under `residentBytes` /
30855
+ * `residentOldestMs`, which answer a different question and say so.
30856
+ */
30733
30857
  var RecordingDeviceUsageSchema = object({
30734
30858
  deviceId: number(),
30859
+ /** Bytes the ARCHIVE holds for this camera on this node's resolvable
30860
+ * recordings locations. */
30735
30861
  usedBytes: number(),
30736
30862
  /**
30737
- * Start of this camera's OLDEST indexed segment, across every profile and
30863
+ * Start of this camera's OLDEST ARCHIVED segment, across every profile and
30738
30864
  * location — the "Oldest footage" column in Recordings → Storage, and the
30739
30865
  * only honest answer to "is retention actually holding?" per camera.
30740
30866
  *
@@ -30744,7 +30870,24 @@ var RecordingDeviceUsageSchema = object({
30744
30870
  * (types) and the addon ship on different trains, and the addon is usually
30745
30871
  * the later of the two.
30746
30872
  */
30747
- oldestMs: number().nullable().optional()
30873
+ oldestMs: number().nullable().optional(),
30874
+ /** Segments the archive holds for this camera — the "M" beside `usedBytes`.
30875
+ * OPTIONAL for the same train-skew reason as `oldestMs`. */
30876
+ segments: number().optional(),
30877
+ /**
30878
+ * RESIDENCY, not the archive: bytes the recorder process currently holds in
30879
+ * its RAM index — the live tail plus the hours a reader hydrated.
30880
+ *
30881
+ * It answers "what can be served without touching the disk", never "how much
30882
+ * footage this camera has". A UI that renders it must label it as such, and
30883
+ * must never total it as storage. OPTIONAL: a recorder predating D418 omits
30884
+ * it, and so does a node whose ledger could not answer.
30885
+ */
30886
+ residentBytes: number().optional(),
30887
+ /** The residency counterpart of `oldestMs` — the oldest segment currently in
30888
+ * RAM, which on a healthy recorder is minutes ago and means nothing about
30889
+ * retention. `null` = nothing resident. */
30890
+ residentOldestMs: number().nullable().optional()
30748
30891
  });
30749
30892
  /** Recording storage usage + capacity for one storage location. */
30750
30893
  var RecordingLocationUsageSchema = object({
@@ -30782,6 +30925,17 @@ var RecordingStorageUsageSchema = object({
30782
30925
  totalUsedBytes: number(),
30783
30926
  devices: array(RecordingDeviceUsageSchema),
30784
30927
  /**
30928
+ * WHERE `devices[]` came from, so an empty table is never mistaken for empty
30929
+ * disks (D418).
30930
+ *
30931
+ * `archive` = the durable hour ledger answered. `unavailable` = this node
30932
+ * declared no ledger and the recorder REFUSED rather than answering the
30933
+ * question with the resident index (D295 rule 3) — the rows are UNKNOWN, not
30934
+ * zero, and a UI must say so. OPTIONAL: a recorder predating D418 omits it,
30935
+ * and its rows are residency.
30936
+ */
30937
+ devicesRead: _enum(["archive", "unavailable"]).optional(),
30938
+ /**
30785
30939
  * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
30786
30940
  * of the payload without it: the hub serves this method through a BAKED
30787
30941
  * router, so a hub whose framework train predates a change to this array
@@ -34874,7 +35028,7 @@ function toDeviceSummary(device, addonId) {
34874
35028
  };
34875
35029
  }
34876
35030
  /**
34877
- * Base class for device-provider addons (rtsp, onvif, frigate).
35031
+ * Base class for device-provider addons (rtsp, onvif).
34878
35032
  *
34879
35033
  * Provides default implementations for the common device-provider cap
34880
35034
  * methods (`start`, `stop`, `getStatus`, `getDevices`, `supportsDiscovery`,
@@ -39693,6 +39847,24 @@ Object.freeze({
39693
39847
  addonId: null,
39694
39848
  access: "view"
39695
39849
  },
39850
+ "pipelineRunner.getParkedTrackFrame": {
39851
+ capName: "pipeline-runner",
39852
+ capScope: "system",
39853
+ addonId: null,
39854
+ access: "view"
39855
+ },
39856
+ "pipelineRunner.parkTrackFrame": {
39857
+ capName: "pipeline-runner",
39858
+ capScope: "system",
39859
+ addonId: null,
39860
+ access: "create"
39861
+ },
39862
+ "pipelineRunner.releaseParkedTrackFrames": {
39863
+ capName: "pipeline-runner",
39864
+ capScope: "system",
39865
+ addonId: null,
39866
+ access: "create"
39867
+ },
39696
39868
  "pipelineRunner.reportMotion": {
39697
39869
  capName: "pipeline-runner",
39698
39870
  capScope: "system",
@@ -42970,6 +43142,21 @@ Object.freeze({
42970
43142
  form: "single",
42971
43143
  optional: false
42972
43144
  }],
43145
+ "pipelineRunner.getParkedTrackFrame": [{
43146
+ name: "deviceId",
43147
+ form: "single",
43148
+ optional: false
43149
+ }],
43150
+ "pipelineRunner.parkTrackFrame": [{
43151
+ name: "deviceId",
43152
+ form: "single",
43153
+ optional: false
43154
+ }],
43155
+ "pipelineRunner.releaseParkedTrackFrames": [{
43156
+ name: "deviceId",
43157
+ form: "single",
43158
+ optional: false
43159
+ }],
42973
43160
  "pipelineRunner.reportMotion": [{
42974
43161
  name: "deviceId",
42975
43162
  form: "single",
@@ -43661,7 +43848,7 @@ var PreviewTextField = string().max(200);
43661
43848
  * Whether the notification's preview is a STILL or a short animation.
43662
43849
  *
43663
43850
  * The operator's ask, verbatim: *"inviato come gif o video (come per le altre
43664
- * rule)"* — his Scrypted advanced-notifier has a `gifRule`, and a ten-hour
43851
+ * rule)"* — the other rules already offer an animated preview, and a ten-hour
43665
43852
  * night reads better as three seconds of motion than as one frame of it. Both
43666
43853
  * modes get the SAME treatment (blurred frame, large centred title); `'gif'`
43667
43854
  * simply applies it to a dozen frames sampled across the render and assembles
@@ -145531,12 +145718,10 @@ function mapNetworkReadout(readout, now) {
145531
145718
  //#region src/error-classifier.ts
145532
145719
  /**
145533
145720
  * Recognise transient Baichuan/socket failures that are expected to
145534
- * recover on the next reconnect cycle. Mirrors
145535
- * `scrypted-reolink-native/src/camera.ts isRecoverableBaichuanError`
145536
- * keeping parity so the same wire-level conditions are treated the
145537
- * same way across plugins.
145721
+ * recover on the next reconnect cycle — the same wire-level conditions
145722
+ * are always treated the same way.
145538
145723
  *
145539
- * Ported categories:
145724
+ * Categories:
145540
145725
  * - Baichuan-specific transport closes (`Baichuan socket closed`,
145541
145726
  * `Baichuan UDP stream closed`, `Baichuan TCP socket is not
145542
145727
  * connected`)
@@ -145606,9 +145791,7 @@ var resolveZoomStep = (travel, zoom) => {
145606
145791
  /**
145607
145792
  * IMA ADPCM (DVI4) encoder — Reolink Baichuan talk-back wire format.
145608
145793
  *
145609
- * Ported from `scrypted-reolink-native/src/intercom.ts` (BSD-2 like the
145610
- * rest of the cross-cam Scrypted infra). Reolink cameras expect ADPCM
145611
- * blocks of (4 + N) bytes:
145794
+ * Reolink cameras expect ADPCM blocks of (4 + N) bytes:
145612
145795
  * - 2 bytes: little-endian Int16 predictor (first PCM sample of the
145613
145796
  * block, used to seed the decoder state on the camera side)
145614
145797
  * - 1 byte: index into the IMA step table (always 0 — we re-seed
@@ -145992,14 +146175,12 @@ var ReolinkIntercomSession = class {
145992
146175
  }
145993
146176
  }
145994
146177
  };
145995
- /** Clamp `blocksPerPayload` into the lib's accepted range. Mirrors
145996
- * scrypted-reolink-native's `intercom-mixin.ts:96-101` clamp. */
146178
+ /** Clamp `blocksPerPayload` into the lib's accepted range. */
145997
146179
  function clampBlocks(value) {
145998
146180
  if (value === void 0 || !Number.isFinite(value)) return DEFAULT_BLOCKS_PER_PAYLOAD;
145999
146181
  return Math.max(1, Math.min(8, Math.floor(value)));
146000
146182
  }
146001
- /** Clamp `outputGain` into the operator-safe range. Same `[0.1, 10]`
146002
- * band as Scrypted (`intercom-mixin.ts:104-107`). */
146183
+ /** Clamp `outputGain` into the operator-safe `[0.1, 10]` band. */
146003
146184
  function clampGain(value) {
146004
146185
  if (value === void 0 || !Number.isFinite(value)) return DEFAULT_GAIN;
146005
146186
  return Math.max(MIN_GAIN, Math.min(MAX_GAIN, value));
@@ -146598,9 +146779,8 @@ var ReolinkUdpDiscoveryMethodSchema = _enum([
146598
146779
  "map"
146599
146780
  ]);
146600
146781
  /**
146601
- * Lib-side socket debug categories. Mirrors scrypted-reolink-native's
146602
- * `DebugLogOption` (debug-options.ts) same names, same semantics, so
146603
- * users coming from Scrypted recognize them. Each flag is forwarded
146782
+ * Lib-side socket debug categories the names and semantics the lib
146783
+ * itself uses, so they are recognizable. Each flag is forwarded
146604
146784
  * verbatim into the `DebugOptions` blob `ReolinkBaichuanApi` accepts.
146605
146785
  */
146606
146786
  var ReolinkSocketDebugFlagSchema = _enum([
@@ -147002,14 +147182,12 @@ var reolinkCameraSchema = object({
147002
147182
  }).loose().optional(),
147003
147183
  /**
147004
147184
  * Generic Baichuan debug logs. Forwarded as `DebugOptions.general`
147005
- * on the lib's `ReolinkBaichuanApi` constructor. Mirrors Scrypted's
147006
- * `debugLogs` boolean.
147185
+ * on the lib's `ReolinkBaichuanApi` constructor.
147007
147186
  */
147008
147187
  debugGeneral: boolean().default(false),
147009
147188
  /**
147010
147189
  * Lib-side socket trace categories. Each flag flips the matching
147011
- * `DebugOptions.<flag>` on the api. Mirrors Scrypted's
147012
- * `socketApiDebugLogs` multi-select.
147190
+ * `DebugOptions.<flag>` on the api.
147013
147191
  */
147014
147192
  debugSocketLogs: array(ReolinkSocketDebugFlagSchema).default([]),
147015
147193
  /**
@@ -147264,21 +147442,20 @@ var reolinkCameraSchema = object({
147264
147442
  /**
147265
147443
  * Intercom (browser→cam talk channel) tuning. Only consumed
147266
147444
  * when the camera advertises two-way audio (`hasIntercom` in the
147267
- * feature-probe slice). Mirrors scrypted-reolink-native's
147268
- * `intercom-mixin.ts` storage settings same names, same ranges,
147269
- * same defaults so the user-facing latency matches Scrypted's.
147445
+ * feature-probe slice). The names, ranges and defaults are the ones
147446
+ * the lib itself documents, so the user-facing latency is predictable.
147270
147447
  *
147271
147448
  * `intercomBlocksPerPayload` — number of IMA-ADPCM blocks bundled
147272
147449
  * into each `sendAudio` call against the lib's dedicated talk
147273
147450
  * session. Lower = smaller chunks, more sends/sec, lower latency.
147274
147451
  * Higher = fewer sends, less per-call socket overhead. Default 1
147275
- * = lowest latency, matching Scrypted.
147452
+ * = lowest latency.
147276
147453
  */
147277
147454
  intercomBlocksPerPayload: number().int().min(1).max(8).optional(),
147278
147455
  /**
147279
147456
  * `intercomMaxBacklogMs` — bound on the PCM backlog before the
147280
- * pump drops oldest samples to cap latency. Default 120ms matches
147281
- * Scrypted; raise via the Settings UI on systems where the cross-
147457
+ * pump drops oldest samples to cap latency. Default 120ms; raise
147458
+ * via the Settings UI on systems where the cross-
147282
147459
  * process Opus→PCM path is bursty enough to trigger frequent
147283
147460
  * `intercom backlog clamped` warnings.
147284
147461
  */
@@ -147298,8 +147475,7 @@ var reolinkCameraSchema = object({
147298
147475
  * instances; this schema only carries connection + debug + the cached
147299
147476
  * channels summary so we can re-render the Discovery panel offline.
147300
147477
  *
147301
- * Mirrors `scrypted-reolink-native`'s `ReolinkNativeNvrDevice`
147302
- * `storageSettings` (`nvr.ts:49-178`): same scope, same fields, just
147478
+ * The NVR device's own settings scope and fields, just
147303
147479
  * adapted to the Zod conventions used elsewhere in this package.
147304
147480
  */
147305
147481
  var reolinkHubSchema = object({
@@ -148080,8 +148256,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
148080
148256
  objectDetectionsSubscribed = false;
148081
148257
  /**
148082
148258
  * Plugin-level event-health interval — fallback layer over the
148083
- * lib's internal 5-minute watchdog. Mirrors Scrypted's
148084
- * `startEventCheck` (`baichuan-base.ts:719-783`): every 60 s,
148259
+ * lib's internal 5-minute watchdog. The rule: every 60 s,
148085
148260
  * if `eventSubscriptionActive` is false on a live socket OR no
148086
148261
  * event has arrived in 10 min, force a full unsub/resub cycle.
148087
148262
  */
@@ -148196,9 +148371,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
148196
148371
  /** Aux accessory references registered by spawned children
148197
148372
  * (siren / floodlight / pir). The parent's centralised align
148198
148373
  * method iterates these to refresh their slices on a single
148199
- * cadence — replaces the old per-accessory setInterval. Mirrors
148200
- * scrypted-reolink-native's `motionSiren` / `floodlight` / `pir`
148201
- * field refs + their `alignAuxDevicesState` method. */
148374
+ * cadence — replaces the old per-accessory setInterval. */
148202
148375
  auxAccessoryRefs = /* @__PURE__ */ new Map();
148203
148376
  /**
148204
148377
  * Reliability watchdogs (Slice 10). Both timers are armed after a
@@ -149003,8 +149176,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
149003
149176
  * Single-flight guard for snapshot fetches (Slice 10). When two
149004
149177
  * consumers hit `getSnapshot` concurrently we issue ONE Baichuan
149005
149178
  * request and let both await its result — saves a doubled wake on
149006
- * battery cams and halves load for regular ones. Pattern mirrors
149007
- * `takePictureInFlight` in scrypted-reolink-native.
149179
+ * battery cams and halves load for regular ones.
149008
149180
  */
149009
149181
  snapshotInFlight = null;
149010
149182
  /**
@@ -151337,9 +151509,8 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
151337
151509
  };
151338
151510
  }
151339
151511
  /**
151340
- * Battery-cam pre-wake hook for the intercom orchestrator. Mirrors
151341
- * scrypted-reolink-native's `intercom.ts:90-106`: ask the lib for
151342
- * the current sleep status (passive — no traffic), and if the cam
151512
+ * Battery-cam pre-wake hook for the intercom orchestrator: ask the
151513
+ * lib for the current sleep status (passive — no traffic), and if the cam
151343
151514
  * is asleep issue an explicit `wakeUp(channel, {waitAfterWakeMs:2000})`
151344
151515
  * + a 1s settle delay before letting the talk-session handshake
151345
151516
  * fire. Without this, the handshake against a sleeping UDP/battery
@@ -151879,7 +152050,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
151879
152050
  /**
151880
152051
  * Synthesize the HTTP-FLV pull URL for a (channel, profile) pair. FLV is
151881
152052
  * served off the Reolink Baichuan media port (default 1935, app `bcs`) as
151882
- * `channel<channel>_<profile>.bcs` — the same shape Frigate/go2rtc use. The
152053
+ * `channel<channel>_<profile>.bcs` — the same shape go2rtc uses. The
151883
152054
  * lib does NOT surface this URL, so we build it from the SAME connection
151884
152055
  * creds the RTSP/native paths use (`host`/`username`/`password` on the
151885
152056
  * device config), URL-encoding the credentials.
@@ -152730,9 +152901,8 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
152730
152901
  /**
152731
152902
  * Shared wake-transition handler invoked by both the simpleEvent
152732
152903
  * `awake` push (canonical fast path) and the sleep poll's
152733
- * `sleeping → awake` flip (backstop). Mirrors Scrypted's
152734
- * `updateSleepingState`'s wake branch (camera.ts:3543-3560) fan
152735
- * out the refreshes that depend on the camera being responsive:
152904
+ * `sleeping → awake` flip (backstop). Fan out the refreshes that
152905
+ * depend on the camera being responsive:
152736
152906
  * - aux accessories (siren/floodlight/PIR) re-read their
152737
152907
  * firmware state via `alignAuxDevicesState('wake')`
152738
152908
  * - parent settings snapshot (image / motion / AI / enc / mask /
@@ -152881,8 +153051,6 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
152881
153051
  * 3. Register via `onSimpleEvent(handleSimpleEventBound)`.
152882
153052
  * Lib starts/refreshes its own subscribe + 5min watchdog.
152883
153053
  *
152884
- * Mirrors `subscribeToEventsInternal` in Scrypted's
152885
- * `baichuan-base.ts:837-888` — same pattern, same rationale.
152886
153054
  */
152887
153055
  async resubscribeSimpleEvents(api, reason) {
152888
153056
  try {
@@ -153759,7 +153927,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
153759
153927
  type: "slider",
153760
153928
  key: "intercomBlocksPerPayload",
153761
153929
  label: "Blocks per payload",
153762
- 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).",
153930
+ description: "Lower = lower latency (more sendAudio calls/sec). Higher = fewer calls, less per-call socket overhead. Range 1..8. Default 1 (lowest latency).",
153763
153931
  min: 1,
153764
153932
  max: 8,
153765
153933
  step: 1,
@@ -153771,7 +153939,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
153771
153939
  type: "slider",
153772
153940
  key: "intercomMaxBacklogMs",
153773
153941
  label: "Max backlog (ms)",
153774
- 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).",
153942
+ 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.",
153775
153943
  min: 20,
153776
153944
  max: 5e3,
153777
153945
  step: 20,
@@ -153906,7 +154074,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
153906
154074
  id: "debug",
153907
154075
  tab: "advanced",
153908
154076
  title: "Debug",
153909
- 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.",
154077
+ 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.",
153910
154078
  columns: 1,
153911
154079
  fields: [{
153912
154080
  type: "boolean",
@@ -154600,9 +154768,8 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
154600
154768
  return this.loginPromise;
154601
154769
  }
154602
154770
  /**
154603
- * Bridge Reolink hardware events to the camstack event bus. Mirrors
154604
- * the dispatch shape of scrypted-reolink-native's `onSimpleEvent`
154605
- * (`camera.ts:1979-2033`): online and sleeping are TWO ORTHOGONAL
154771
+ * Bridge Reolink hardware events to the camstack event bus:
154772
+ * online and sleeping are TWO ORTHOGONAL
154606
154773
  * axes, each driven exclusively by their own firmware push events.
154607
154774
  *
154608
154775
  * - 'motion' + AI types (people, vehicle, animal, face, package):
@@ -154631,8 +154798,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
154631
154798
  }
154632
154799
  /**
154633
154800
  * Build the `DebugOptions` blob forwarded to `ReolinkBaichuanApi`'s
154634
- * constructor. Mirrors Scrypted's `getBaichuanDebugOptions` at
154635
- * `camera.ts:1778-1786`. Returns `undefined` when no flags are set
154801
+ * constructor. Returns `undefined` when no flags are set
154636
154802
  * so the lib falls back to its own defaults instead of an empty
154637
154803
  * object override.
154638
154804
  */
@@ -154891,8 +155057,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
154891
155057
  * idle UDP socket on its own, the camera is asleep, and there is
154892
155058
  * nothing to reconnect to. Aggressively reconnecting in that state
154893
155059
  * is what was waking up the doorbell every ~60 s in production
154894
- * logs. Mirror Scrypted's reolink-native handler: drop the api
154895
- * reference, but DO NOT schedule a reconnect timer. The next
155060
+ * logs. Drop the api reference, but DO NOT schedule a reconnect timer. The next
154896
155061
  * `awake` push (or a snapshot demand from the operator) will
154897
155062
  * lazily call `ensureApi()` and re-establish the client.
154898
155063
  */
@@ -155005,8 +155170,8 @@ function isHubLike(d) {
155005
155170
  * starts at 60s) OR the subscription is genuinely broken / nothing is
155006
155171
  * happening in front of the cam (counter rises → backoff lengthens).
155007
155172
  *
155008
- * Schedule: 60s → 2min → 5min → 15min capped. Mirrors the long-poll
155009
- * backoff curves in scrypted-reolink-native + reolink_aio.
155173
+ * Schedule: 60s → 2min → 5min → 15min capped the long-poll backoff
155174
+ * curve this hardware is known to tolerate.
155010
155175
  */
155011
155176
  function computeHealthCheckBackoffMs(consecutive) {
155012
155177
  if (consecutive <= 1) return 6e4;
@@ -155150,7 +155315,7 @@ var HUB_RECONNECT_MAX_MS = 3e4;
155150
155315
  /**
155151
155316
  * Reolink Hub / NVR — single Baichuan socket fronting N channels.
155152
155317
  *
155153
- * Mirrors the Scrypted `ReolinkNativeNvrDevice` pattern:
155318
+ * The NVR pattern:
155154
155319
  * - One persistent control + event socket on the parent (no
155155
155320
  * per-channel login).
155156
155321
  * - `device-discovery` cap exposes the channel list with status,
@@ -155303,8 +155468,7 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
155303
155468
  }
155304
155469
  }
155305
155470
  /**
155306
- * Hub-specific settings form. Mirrors the Scrypted
155307
- * `ReolinkNativeNvrDevice.storageSettings` block (`nvr.ts:49-178`):
155471
+ * Hub-specific settings form:
155308
155472
  * connection (host / port / credentials) on the General tab,
155309
155473
  * Baichuan debug flags on Advanced. Per-channel settings (image,
155310
155474
  * motion, AI sensitivity, …) live on the adopted child cameras —
@@ -155396,7 +155560,7 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
155396
155560
  id: "debug",
155397
155561
  tab: "advanced",
155398
155562
  title: "Debug",
155399
- 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.",
155563
+ 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.",
155400
155564
  columns: 1,
155401
155565
  fields: [{
155402
155566
  type: "boolean",
@@ -155559,8 +155723,8 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
155559
155723
  }
155560
155724
  /**
155561
155725
  * Pull the channel list from the Hub via the lib's
155562
- * `getNvrChannelsSummary({ source: 'cgi' })`. CGI source mirrors
155563
- * Scrypted's choice: `GetChannelstatus` returns the channel list
155726
+ * `getNvrChannelsSummary({ source: 'cgi' })`. CGI is chosen because
155727
+ * `GetChannelstatus` returns the channel list
155564
155728
  * synchronously (no race against the cmd_id 145 push cache that
155565
155729
  * can be empty right after login).
155566
155730
  *
@@ -155952,8 +156116,7 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
155952
156116
  }
155953
156117
  /**
155954
156118
  * Dispatches a Hub-received simpleEvent to the matching child's
155955
- * `handleSimpleEvent`. Mirrors Scrypted's
155956
- * `ReolinkNativeNvrDevice.onSimpleEvent` (`nvr.ts:288-319`).
156119
+ * `handleSimpleEvent`.
155957
156120
  */
155958
156121
  routeSimpleEvent(ev) {
155959
156122
  const channel = ev?.channel;
@@ -156111,7 +156274,7 @@ function computeChildNativeId(parentStableId, channel, uid) {
156111
156274
  }
156112
156275
  /**
156113
156276
  * Map the lib's per-channel summary booleans to our `DiscoveredChildStatus`.
156114
- * Order of precedence mirrors the Scrypted handling:
156277
+ * Order of precedence:
156115
156278
  * 1. `online === false` → offline
156116
156279
  * 2. `sleeping === true` → sleeping
156117
156280
  * 3. anything else with non-empty state → online