@camstack/addon-provider-reolink 1.1.25 → 1.1.26

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 +194 -14
  2. package/dist/addon.mjs +194 -14
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -26,7 +26,7 @@ let fs_promises = require("fs/promises");
26
26
  fs_promises = require_chunk.__toESM(fs_promises, 1);
27
27
  let node_os = require("node:os");
28
28
  node_os = require_chunk.__toESM(node_os);
29
- //#region ../types/dist/event-category-CFZs3jI4.mjs
29
+ //#region ../types/dist/event-category-H4AVePnn.mjs
30
30
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
31
31
  EventCategory["SystemBoot"] = "system.boot";
32
32
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -176,6 +176,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
176
176
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
177
177
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
178
178
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
179
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
180
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
181
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
179
182
  EventCategory["DetectionEvent"] = "detection.event";
180
183
  EventCategory["SessionTrackNew"] = "session.track.new";
181
184
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -11035,10 +11038,23 @@ var deviceDiscoveryCapability = {
11035
11038
  }
11036
11039
  };
11037
11040
  /**
11038
- * Doorbell button cap. Installed on a `DeviceType.Button` accessory
11039
- * with `role: DeviceRole.Doorbell`. Emits an `onPressed` event every
11040
- * time the firmware pushes a ring; status tracks the last press and
11041
- * a pressCount since start (diagnostic).
11041
+ * Doorbell button cap. Two kinds of providers coexist behind this cap
11042
+ * name (same pattern as `snapshot`):
11043
+ *
11044
+ * - **Native** providers: registered per-device by device-driver
11045
+ * addons via `ctx.registerNativeCap` — either on a
11046
+ * `DeviceType.Button` accessory with `role: DeviceRole.Doorbell`,
11047
+ * or directly on the camera (Reolink registers at camera level).
11048
+ * Emits an `onPressed` event every time the firmware pushes a
11049
+ * ring; status tracks the last press and a pressCount since start
11050
+ * (diagnostic).
11051
+ *
11052
+ * - **Wrapper** provider: the `virtual-doorbell` system builtin
11053
+ * (`@camstack/system/builtins/doorbell`). Turns ANY binary-ish
11054
+ * device (contact / switch / event-emitter …) into a doorbell for
11055
+ * a camera. `defaultActive: false` — the operator explicitly binds
11056
+ * it per camera in the device-bindings UI, then picks the source
11057
+ * device + trigger in the per-device settings.
11042
11058
  *
11043
11059
  * The DeviceEventPropagator re-emits `onPressed` on the camera parent
11044
11060
  * — subscribers listening at the camera level receive ring events
@@ -11059,7 +11075,10 @@ var doorbellCapability = {
11059
11075
  scope: "device",
11060
11076
  deviceNative: true,
11061
11077
  mode: "singleton",
11062
- deviceTypes: [DeviceType.Button],
11078
+ kind: "wrapper",
11079
+ defaultActive: false,
11080
+ deviceTypes: [DeviceType.Button, DeviceType.Camera],
11081
+ exposesDeviceSettings: true,
11063
11082
  methods: {},
11064
11083
  events: {
11065
11084
  /**
@@ -17423,6 +17442,14 @@ var ConfigEntrySchema = object({
17423
17442
  value: unknown(),
17424
17443
  description: string().optional()
17425
17444
  });
17445
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
17446
+ /** One resolved linked device — the compact projection consumers need. */
17447
+ var LinkedDeviceSchema = object({
17448
+ deviceId: number(),
17449
+ name: string(),
17450
+ location: string().nullable(),
17451
+ features: array(string())
17452
+ });
17426
17453
  var SavedDeviceRowSchema = object({
17427
17454
  /** Numeric id reserved at allocateDeviceId time. */
17428
17455
  id: number(),
@@ -17644,7 +17671,10 @@ method(object({
17644
17671
  }), _void(), {
17645
17672
  kind: "mutation",
17646
17673
  auth: "admin"
17647
- }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
17674
+ }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
17675
+ mode: DeviceLinkModeSchema,
17676
+ devices: array(LinkedDeviceSchema)
17677
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
17648
17678
  deviceId: number(),
17649
17679
  values: record(string(), unknown())
17650
17680
  }), object({ success: literal(true) }), {
@@ -18913,6 +18943,61 @@ var EventKindSchema = _enum([
18913
18943
  "object",
18914
18944
  "audio"
18915
18945
  ]);
18946
+ /**
18947
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
18948
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
18949
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
18950
+ */
18951
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
18952
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
18953
+ var EventKindIconSchema = _enum([
18954
+ "motion",
18955
+ "audio",
18956
+ "person",
18957
+ "vehicle",
18958
+ "animal",
18959
+ "door",
18960
+ "pir",
18961
+ "smoke",
18962
+ "water",
18963
+ "button",
18964
+ "generic"
18965
+ ]);
18966
+ var EventKindCategorySchema = _enum([
18967
+ "motion",
18968
+ "audio",
18969
+ "detection",
18970
+ "sensor",
18971
+ "custom"
18972
+ ]);
18973
+ var EventKindDescriptorSchema = object({
18974
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
18975
+ kind: string(),
18976
+ label: string(),
18977
+ /** Hex color for timeline/legend rendering. */
18978
+ color: string(),
18979
+ icon: EventKindIconSchema,
18980
+ category: EventKindCategorySchema,
18981
+ /** Which cap + device contributes this kind. For built-ins the camera
18982
+ * itself; for sensor kinds the LINKED source device. */
18983
+ source: object({
18984
+ capName: string(),
18985
+ deviceId: number()
18986
+ })
18987
+ });
18988
+ var SensorEventSchema = object({
18989
+ id: string(),
18990
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
18991
+ * yields N rows, one per camera). */
18992
+ deviceId: number(),
18993
+ /** The linked sensor device whose state changed. */
18994
+ sourceDeviceId: number(),
18995
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
18996
+ kind: string(),
18997
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
18998
+ value: record(string(), unknown()).nullable(),
18999
+ timestamp: number()
19000
+ });
18916
19001
  var TrackPositionSchema = object({
18917
19002
  x: number(),
18918
19003
  y: number(),
@@ -18926,6 +19011,30 @@ var TrackSnapshotSchema = object({
18926
19011
  mediaKey: string()
18927
19012
  });
18928
19013
  /**
19014
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
19015
+ * divided by the track's detection-frame dims), computed at persist time.
19016
+ * Absent when the frame dims were unknown when the track was persisted
19017
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
19018
+ */
19019
+ var TrackEnvelopeSchema = object({
19020
+ minX: number(),
19021
+ minY: number(),
19022
+ maxX: number(),
19023
+ maxY: number()
19024
+ });
19025
+ /**
19026
+ * Row projection for track list queries. `full` (default) returns the
19027
+ * complete Track including the frame-rate `positions[]` history and the
19028
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
19029
+ * keeps every scalar the list surfaces actually render (ids, class(es),
19030
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
19031
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
19032
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
19033
+ * `getTrack`. Mirrors the event-store `projection` convention
19034
+ * (`getObjectEvents` et al.).
19035
+ */
19036
+ var TrackProjectionSchema = _enum(["full", "slim"]);
19037
+ /**
18929
19038
  * One audio-classification label heard on the track's camera while the
18930
19039
  * track was alive, aggregated per label. An "episode" is one persisted
18931
19040
  * audio event (the confident-classification path: score ≥ the device's
@@ -18976,7 +19085,11 @@ var TrackSchema = object({
18976
19085
  /** Audio-classification labels heard on the camera during the track's
18977
19086
  * life (score ≥ device `classificationMinScore`), aggregated per label.
18978
19087
  * Absent on legacy rows / tracks with no confident audio. */
18979
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
19088
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
19089
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
19090
+ * Populated from the persisted envelope columns on historical reads;
19091
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
19092
+ envelope: TrackEnvelopeSchema.optional()
18980
19093
  });
18981
19094
  var BaseEventFields = {
18982
19095
  id: string(),
@@ -19086,11 +19199,13 @@ var MediaFileSchema = object({
19086
19199
  sizeBytes: number(),
19087
19200
  timestamp: number()
19088
19201
  });
19202
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
19203
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
19089
19204
  var DeviceEventQueryInput = object({
19090
19205
  deviceId: number(),
19091
19206
  since: number().optional(),
19092
19207
  until: number().optional(),
19093
- limit: number().int().min(1).max(5e3).default(1e3),
19208
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
19094
19209
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
19095
19210
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
19096
19211
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -19098,6 +19213,27 @@ var DeviceEventQueryInput = object({
19098
19213
  projection: _enum(["full", "slim"]).optional()
19099
19214
  });
19100
19215
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
19216
+ var RecentTracksQueryInput = object({
19217
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
19218
+ deviceIds: array(number()),
19219
+ /** Window lower bound on `lastSeen` (inclusive). */
19220
+ since: number().optional(),
19221
+ /** Window upper bound on `lastSeen` (inclusive). */
19222
+ until: number().optional(),
19223
+ /** Page size. Default 200, max 1000. */
19224
+ limit: number().int().min(1).max(1e3).default(200),
19225
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
19226
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
19227
+ cursor: string().optional(),
19228
+ /** See {@link TrackProjectionSchema}. Default `full`. */
19229
+ projection: TrackProjectionSchema.optional()
19230
+ });
19231
+ var RecentTracksPageSchema = object({
19232
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
19233
+ tracks: array(TrackSchema).readonly(),
19234
+ /** Cursor for the next page, or null when this page is the last. */
19235
+ nextCursor: string().nullable()
19236
+ });
19101
19237
  var KeyEventQueryInput = object({
19102
19238
  deviceId: number(),
19103
19239
  /** Window lower bound (track firstSeen ≥ since). */
@@ -19180,11 +19316,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19180
19316
  deviceId: number(),
19181
19317
  since: number().optional(),
19182
19318
  until: number().optional(),
19183
- limit: number().optional()
19184
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
19319
+ limit: number().optional(),
19320
+ /** Spatial filter only tracks whose trajectory intersects the zone
19321
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
19322
+ * envelope columns, then precisely tested per position. Tracks with
19323
+ * an unknown envelope (no frame dims at persist time) always match. */
19324
+ zone: TrackZoneFilterSchema.optional(),
19325
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
19326
+ * compatible — omitting the field keeps today's exact behaviour). */
19327
+ projection: TrackProjectionSchema.optional()
19328
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
19185
19329
  kind: "mutation",
19186
19330
  auth: "admin"
19187
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19331
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
19332
+ deviceId: number(),
19333
+ since: number().optional(),
19334
+ until: number().optional(),
19335
+ kinds: array(string()).optional(),
19336
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19337
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19188
19338
  deviceId: number(),
19189
19339
  since: number(),
19190
19340
  until: number(),
@@ -22554,7 +22704,12 @@ var RecordingStorageUsageSchema = object({
22554
22704
  /**
22555
22705
  * Result of locating footage at a wall-clock instant for one device/profile.
22556
22706
  * `segment` carries the covering segment's window; `gap` reports the forward
22557
- * nearest covered edge (`null` past the end of footage / when none exists).
22707
+ * nearest covered edge (`null` past the end of footage / when none exists)
22708
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
22709
+ * footage behind the epoch; `null` when none — optional so older providers
22710
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
22711
+ * small inter-segment cracks (durMs under-covers the span to the next
22712
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
22558
22713
  */
22559
22714
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
22560
22715
  kind: literal("segment"),
@@ -22563,7 +22718,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
22563
22718
  bytes: number()
22564
22719
  }), object({
22565
22720
  kind: literal("gap"),
22566
- nearestEdgeMs: number().nullable()
22721
+ nearestEdgeMs: number().nullable(),
22722
+ prevEndMs: number().nullable().optional()
22567
22723
  })]);
22568
22724
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
22569
22725
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -24343,6 +24499,12 @@ Object.freeze({
24343
24499
  addonId: null,
24344
24500
  access: "view"
24345
24501
  },
24502
+ "deviceManager.getLinkedDevices": {
24503
+ capName: "device-manager",
24504
+ capScope: "system",
24505
+ addonId: null,
24506
+ access: "view"
24507
+ },
24346
24508
  "deviceManager.getRoleDisplayDefaults": {
24347
24509
  capName: "device-manager",
24348
24510
  capScope: "system",
@@ -25897,6 +26059,12 @@ Object.freeze({
25897
26059
  addonId: null,
25898
26060
  access: "view"
25899
26061
  },
26062
+ "pipelineAnalytics.getSensorEvents": {
26063
+ capName: "pipeline-analytics",
26064
+ capScope: "device",
26065
+ addonId: null,
26066
+ access: "view"
26067
+ },
25900
26068
  "pipelineAnalytics.getTrack": {
25901
26069
  capName: "pipeline-analytics",
25902
26070
  capScope: "device",
@@ -25909,6 +26077,18 @@ Object.freeze({
25909
26077
  addonId: null,
25910
26078
  access: "view"
25911
26079
  },
26080
+ "pipelineAnalytics.listEventKinds": {
26081
+ capName: "pipeline-analytics",
26082
+ capScope: "device",
26083
+ addonId: null,
26084
+ access: "view"
26085
+ },
26086
+ "pipelineAnalytics.listRecentTracks": {
26087
+ capName: "pipeline-analytics",
26088
+ capScope: "device",
26089
+ addonId: null,
26090
+ access: "view"
26091
+ },
25912
26092
  "pipelineAnalytics.listTracks": {
25913
26093
  capName: "pipeline-analytics",
25914
26094
  capScope: "device",
package/dist/addon.mjs CHANGED
@@ -21,7 +21,7 @@ import netImpl from "net";
21
21
  import { fileURLToPath } from "url";
22
22
  import { mkdir } from "fs/promises";
23
23
  import os from "node:os";
24
- //#region ../types/dist/event-category-CFZs3jI4.mjs
24
+ //#region ../types/dist/event-category-H4AVePnn.mjs
25
25
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
26
26
  EventCategory["SystemBoot"] = "system.boot";
27
27
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -171,6 +171,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
171
171
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
172
172
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
173
173
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
174
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
175
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
176
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
174
177
  EventCategory["DetectionEvent"] = "detection.event";
175
178
  EventCategory["SessionTrackNew"] = "session.track.new";
176
179
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -11030,10 +11033,23 @@ var deviceDiscoveryCapability = {
11030
11033
  }
11031
11034
  };
11032
11035
  /**
11033
- * Doorbell button cap. Installed on a `DeviceType.Button` accessory
11034
- * with `role: DeviceRole.Doorbell`. Emits an `onPressed` event every
11035
- * time the firmware pushes a ring; status tracks the last press and
11036
- * a pressCount since start (diagnostic).
11036
+ * Doorbell button cap. Two kinds of providers coexist behind this cap
11037
+ * name (same pattern as `snapshot`):
11038
+ *
11039
+ * - **Native** providers: registered per-device by device-driver
11040
+ * addons via `ctx.registerNativeCap` — either on a
11041
+ * `DeviceType.Button` accessory with `role: DeviceRole.Doorbell`,
11042
+ * or directly on the camera (Reolink registers at camera level).
11043
+ * Emits an `onPressed` event every time the firmware pushes a
11044
+ * ring; status tracks the last press and a pressCount since start
11045
+ * (diagnostic).
11046
+ *
11047
+ * - **Wrapper** provider: the `virtual-doorbell` system builtin
11048
+ * (`@camstack/system/builtins/doorbell`). Turns ANY binary-ish
11049
+ * device (contact / switch / event-emitter …) into a doorbell for
11050
+ * a camera. `defaultActive: false` — the operator explicitly binds
11051
+ * it per camera in the device-bindings UI, then picks the source
11052
+ * device + trigger in the per-device settings.
11037
11053
  *
11038
11054
  * The DeviceEventPropagator re-emits `onPressed` on the camera parent
11039
11055
  * — subscribers listening at the camera level receive ring events
@@ -11054,7 +11070,10 @@ var doorbellCapability = {
11054
11070
  scope: "device",
11055
11071
  deviceNative: true,
11056
11072
  mode: "singleton",
11057
- deviceTypes: [DeviceType.Button],
11073
+ kind: "wrapper",
11074
+ defaultActive: false,
11075
+ deviceTypes: [DeviceType.Button, DeviceType.Camera],
11076
+ exposesDeviceSettings: true,
11058
11077
  methods: {},
11059
11078
  events: {
11060
11079
  /**
@@ -17418,6 +17437,14 @@ var ConfigEntrySchema = object({
17418
17437
  value: unknown(),
17419
17438
  description: string().optional()
17420
17439
  });
17440
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
17441
+ /** One resolved linked device — the compact projection consumers need. */
17442
+ var LinkedDeviceSchema = object({
17443
+ deviceId: number(),
17444
+ name: string(),
17445
+ location: string().nullable(),
17446
+ features: array(string())
17447
+ });
17421
17448
  var SavedDeviceRowSchema = object({
17422
17449
  /** Numeric id reserved at allocateDeviceId time. */
17423
17450
  id: number(),
@@ -17639,7 +17666,10 @@ method(object({
17639
17666
  }), _void(), {
17640
17667
  kind: "mutation",
17641
17668
  auth: "admin"
17642
- }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
17669
+ }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
17670
+ mode: DeviceLinkModeSchema,
17671
+ devices: array(LinkedDeviceSchema)
17672
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
17643
17673
  deviceId: number(),
17644
17674
  values: record(string(), unknown())
17645
17675
  }), object({ success: literal(true) }), {
@@ -18908,6 +18938,61 @@ var EventKindSchema = _enum([
18908
18938
  "object",
18909
18939
  "audio"
18910
18940
  ]);
18941
+ /**
18942
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
18943
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
18944
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
18945
+ */
18946
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
18947
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
18948
+ var EventKindIconSchema = _enum([
18949
+ "motion",
18950
+ "audio",
18951
+ "person",
18952
+ "vehicle",
18953
+ "animal",
18954
+ "door",
18955
+ "pir",
18956
+ "smoke",
18957
+ "water",
18958
+ "button",
18959
+ "generic"
18960
+ ]);
18961
+ var EventKindCategorySchema = _enum([
18962
+ "motion",
18963
+ "audio",
18964
+ "detection",
18965
+ "sensor",
18966
+ "custom"
18967
+ ]);
18968
+ var EventKindDescriptorSchema = object({
18969
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
18970
+ kind: string(),
18971
+ label: string(),
18972
+ /** Hex color for timeline/legend rendering. */
18973
+ color: string(),
18974
+ icon: EventKindIconSchema,
18975
+ category: EventKindCategorySchema,
18976
+ /** Which cap + device contributes this kind. For built-ins the camera
18977
+ * itself; for sensor kinds the LINKED source device. */
18978
+ source: object({
18979
+ capName: string(),
18980
+ deviceId: number()
18981
+ })
18982
+ });
18983
+ var SensorEventSchema = object({
18984
+ id: string(),
18985
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
18986
+ * yields N rows, one per camera). */
18987
+ deviceId: number(),
18988
+ /** The linked sensor device whose state changed. */
18989
+ sourceDeviceId: number(),
18990
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
18991
+ kind: string(),
18992
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
18993
+ value: record(string(), unknown()).nullable(),
18994
+ timestamp: number()
18995
+ });
18911
18996
  var TrackPositionSchema = object({
18912
18997
  x: number(),
18913
18998
  y: number(),
@@ -18921,6 +19006,30 @@ var TrackSnapshotSchema = object({
18921
19006
  mediaKey: string()
18922
19007
  });
18923
19008
  /**
19009
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
19010
+ * divided by the track's detection-frame dims), computed at persist time.
19011
+ * Absent when the frame dims were unknown when the track was persisted
19012
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
19013
+ */
19014
+ var TrackEnvelopeSchema = object({
19015
+ minX: number(),
19016
+ minY: number(),
19017
+ maxX: number(),
19018
+ maxY: number()
19019
+ });
19020
+ /**
19021
+ * Row projection for track list queries. `full` (default) returns the
19022
+ * complete Track including the frame-rate `positions[]` history and the
19023
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
19024
+ * keeps every scalar the list surfaces actually render (ids, class(es),
19025
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
19026
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
19027
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
19028
+ * `getTrack`. Mirrors the event-store `projection` convention
19029
+ * (`getObjectEvents` et al.).
19030
+ */
19031
+ var TrackProjectionSchema = _enum(["full", "slim"]);
19032
+ /**
18924
19033
  * One audio-classification label heard on the track's camera while the
18925
19034
  * track was alive, aggregated per label. An "episode" is one persisted
18926
19035
  * audio event (the confident-classification path: score ≥ the device's
@@ -18971,7 +19080,11 @@ var TrackSchema = object({
18971
19080
  /** Audio-classification labels heard on the camera during the track's
18972
19081
  * life (score ≥ device `classificationMinScore`), aggregated per label.
18973
19082
  * Absent on legacy rows / tracks with no confident audio. */
18974
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
19083
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
19084
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
19085
+ * Populated from the persisted envelope columns on historical reads;
19086
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
19087
+ envelope: TrackEnvelopeSchema.optional()
18975
19088
  });
18976
19089
  var BaseEventFields = {
18977
19090
  id: string(),
@@ -19081,11 +19194,13 @@ var MediaFileSchema = object({
19081
19194
  sizeBytes: number(),
19082
19195
  timestamp: number()
19083
19196
  });
19197
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
19198
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
19084
19199
  var DeviceEventQueryInput = object({
19085
19200
  deviceId: number(),
19086
19201
  since: number().optional(),
19087
19202
  until: number().optional(),
19088
- limit: number().int().min(1).max(5e3).default(1e3),
19203
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
19089
19204
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
19090
19205
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
19091
19206
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -19093,6 +19208,27 @@ var DeviceEventQueryInput = object({
19093
19208
  projection: _enum(["full", "slim"]).optional()
19094
19209
  });
19095
19210
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
19211
+ var RecentTracksQueryInput = object({
19212
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
19213
+ deviceIds: array(number()),
19214
+ /** Window lower bound on `lastSeen` (inclusive). */
19215
+ since: number().optional(),
19216
+ /** Window upper bound on `lastSeen` (inclusive). */
19217
+ until: number().optional(),
19218
+ /** Page size. Default 200, max 1000. */
19219
+ limit: number().int().min(1).max(1e3).default(200),
19220
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
19221
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
19222
+ cursor: string().optional(),
19223
+ /** See {@link TrackProjectionSchema}. Default `full`. */
19224
+ projection: TrackProjectionSchema.optional()
19225
+ });
19226
+ var RecentTracksPageSchema = object({
19227
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
19228
+ tracks: array(TrackSchema).readonly(),
19229
+ /** Cursor for the next page, or null when this page is the last. */
19230
+ nextCursor: string().nullable()
19231
+ });
19096
19232
  var KeyEventQueryInput = object({
19097
19233
  deviceId: number(),
19098
19234
  /** Window lower bound (track firstSeen ≥ since). */
@@ -19175,11 +19311,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19175
19311
  deviceId: number(),
19176
19312
  since: number().optional(),
19177
19313
  until: number().optional(),
19178
- limit: number().optional()
19179
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
19314
+ limit: number().optional(),
19315
+ /** Spatial filter only tracks whose trajectory intersects the zone
19316
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
19317
+ * envelope columns, then precisely tested per position. Tracks with
19318
+ * an unknown envelope (no frame dims at persist time) always match. */
19319
+ zone: TrackZoneFilterSchema.optional(),
19320
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
19321
+ * compatible — omitting the field keeps today's exact behaviour). */
19322
+ projection: TrackProjectionSchema.optional()
19323
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
19180
19324
  kind: "mutation",
19181
19325
  auth: "admin"
19182
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19326
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
19327
+ deviceId: number(),
19328
+ since: number().optional(),
19329
+ until: number().optional(),
19330
+ kinds: array(string()).optional(),
19331
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19332
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19183
19333
  deviceId: number(),
19184
19334
  since: number(),
19185
19335
  until: number(),
@@ -22549,7 +22699,12 @@ var RecordingStorageUsageSchema = object({
22549
22699
  /**
22550
22700
  * Result of locating footage at a wall-clock instant for one device/profile.
22551
22701
  * `segment` carries the covering segment's window; `gap` reports the forward
22552
- * nearest covered edge (`null` past the end of footage / when none exists).
22702
+ * nearest covered edge (`null` past the end of footage / when none exists)
22703
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
22704
+ * footage behind the epoch; `null` when none — optional so older providers
22705
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
22706
+ * small inter-segment cracks (durMs under-covers the span to the next
22707
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
22553
22708
  */
22554
22709
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
22555
22710
  kind: literal("segment"),
@@ -22558,7 +22713,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
22558
22713
  bytes: number()
22559
22714
  }), object({
22560
22715
  kind: literal("gap"),
22561
- nearestEdgeMs: number().nullable()
22716
+ nearestEdgeMs: number().nullable(),
22717
+ prevEndMs: number().nullable().optional()
22562
22718
  })]);
22563
22719
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
22564
22720
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -24338,6 +24494,12 @@ Object.freeze({
24338
24494
  addonId: null,
24339
24495
  access: "view"
24340
24496
  },
24497
+ "deviceManager.getLinkedDevices": {
24498
+ capName: "device-manager",
24499
+ capScope: "system",
24500
+ addonId: null,
24501
+ access: "view"
24502
+ },
24341
24503
  "deviceManager.getRoleDisplayDefaults": {
24342
24504
  capName: "device-manager",
24343
24505
  capScope: "system",
@@ -25892,6 +26054,12 @@ Object.freeze({
25892
26054
  addonId: null,
25893
26055
  access: "view"
25894
26056
  },
26057
+ "pipelineAnalytics.getSensorEvents": {
26058
+ capName: "pipeline-analytics",
26059
+ capScope: "device",
26060
+ addonId: null,
26061
+ access: "view"
26062
+ },
25895
26063
  "pipelineAnalytics.getTrack": {
25896
26064
  capName: "pipeline-analytics",
25897
26065
  capScope: "device",
@@ -25904,6 +26072,18 @@ Object.freeze({
25904
26072
  addonId: null,
25905
26073
  access: "view"
25906
26074
  },
26075
+ "pipelineAnalytics.listEventKinds": {
26076
+ capName: "pipeline-analytics",
26077
+ capScope: "device",
26078
+ addonId: null,
26079
+ access: "view"
26080
+ },
26081
+ "pipelineAnalytics.listRecentTracks": {
26082
+ capName: "pipeline-analytics",
26083
+ capScope: "device",
26084
+ addonId: null,
26085
+ access: "view"
26086
+ },
25907
26087
  "pipelineAnalytics.listTracks": {
25908
26088
  capName: "pipeline-analytics",
25909
26089
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-reolink",
3
- "version": "1.1.25",
3
+ "version": "1.1.26",
4
4
  "description": "Reolink camera device provider addon for CamStack — native Baichuan protocol",
5
5
  "keywords": [
6
6
  "camstack",