@camstack/addon-provider-rtsp 1.1.23 → 1.1.24

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
@@ -23,7 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  //#endregion
24
24
  let node_net = require("node:net");
25
25
  node_net = __toESM(node_net);
26
- //#region ../types/dist/event-category-CFZs3jI4.mjs
26
+ //#region ../types/dist/event-category-H4AVePnn.mjs
27
27
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
28
28
  EventCategory["SystemBoot"] = "system.boot";
29
29
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -173,6 +173,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
173
173
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
174
174
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
175
175
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
176
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
177
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
178
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
176
179
  EventCategory["DetectionEvent"] = "detection.event";
177
180
  EventCategory["SessionTrackNew"] = "session.track.new";
178
181
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -10906,10 +10909,23 @@ var deviceDiscoveryCapability = {
10906
10909
  }
10907
10910
  };
10908
10911
  /**
10909
- * Doorbell button cap. Installed on a `DeviceType.Button` accessory
10910
- * with `role: DeviceRole.Doorbell`. Emits an `onPressed` event every
10911
- * time the firmware pushes a ring; status tracks the last press and
10912
- * a pressCount since start (diagnostic).
10912
+ * Doorbell button cap. Two kinds of providers coexist behind this cap
10913
+ * name (same pattern as `snapshot`):
10914
+ *
10915
+ * - **Native** providers: registered per-device by device-driver
10916
+ * addons via `ctx.registerNativeCap` — either on a
10917
+ * `DeviceType.Button` accessory with `role: DeviceRole.Doorbell`,
10918
+ * or directly on the camera (Reolink registers at camera level).
10919
+ * Emits an `onPressed` event every time the firmware pushes a
10920
+ * ring; status tracks the last press and a pressCount since start
10921
+ * (diagnostic).
10922
+ *
10923
+ * - **Wrapper** provider: the `virtual-doorbell` system builtin
10924
+ * (`@camstack/system/builtins/doorbell`). Turns ANY binary-ish
10925
+ * device (contact / switch / event-emitter …) into a doorbell for
10926
+ * a camera. `defaultActive: false` — the operator explicitly binds
10927
+ * it per camera in the device-bindings UI, then picks the source
10928
+ * device + trigger in the per-device settings.
10913
10929
  *
10914
10930
  * The DeviceEventPropagator re-emits `onPressed` on the camera parent
10915
10931
  * — subscribers listening at the camera level receive ring events
@@ -10930,7 +10946,10 @@ var doorbellCapability = {
10930
10946
  scope: "device",
10931
10947
  deviceNative: true,
10932
10948
  mode: "singleton",
10933
- deviceTypes: [DeviceType.Button],
10949
+ kind: "wrapper",
10950
+ defaultActive: false,
10951
+ deviceTypes: [DeviceType.Button, DeviceType.Camera],
10952
+ exposesDeviceSettings: true,
10934
10953
  methods: {},
10935
10954
  events: {
10936
10955
  /**
@@ -17282,6 +17301,14 @@ var ConfigEntrySchema = object({
17282
17301
  value: unknown(),
17283
17302
  description: string().optional()
17284
17303
  });
17304
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
17305
+ /** One resolved linked device — the compact projection consumers need. */
17306
+ var LinkedDeviceSchema = object({
17307
+ deviceId: number(),
17308
+ name: string(),
17309
+ location: string().nullable(),
17310
+ features: array(string())
17311
+ });
17285
17312
  var SavedDeviceRowSchema = object({
17286
17313
  /** Numeric id reserved at allocateDeviceId time. */
17287
17314
  id: number(),
@@ -17503,7 +17530,10 @@ method(object({
17503
17530
  }), _void(), {
17504
17531
  kind: "mutation",
17505
17532
  auth: "admin"
17506
- }), 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({
17533
+ }), 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({
17534
+ mode: DeviceLinkModeSchema,
17535
+ devices: array(LinkedDeviceSchema)
17536
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
17507
17537
  deviceId: number(),
17508
17538
  values: record(string(), unknown())
17509
17539
  }), object({ success: literal(true) }), {
@@ -18772,6 +18802,61 @@ var EventKindSchema = _enum([
18772
18802
  "object",
18773
18803
  "audio"
18774
18804
  ]);
18805
+ /**
18806
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
18807
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
18808
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
18809
+ */
18810
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
18811
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
18812
+ var EventKindIconSchema = _enum([
18813
+ "motion",
18814
+ "audio",
18815
+ "person",
18816
+ "vehicle",
18817
+ "animal",
18818
+ "door",
18819
+ "pir",
18820
+ "smoke",
18821
+ "water",
18822
+ "button",
18823
+ "generic"
18824
+ ]);
18825
+ var EventKindCategorySchema = _enum([
18826
+ "motion",
18827
+ "audio",
18828
+ "detection",
18829
+ "sensor",
18830
+ "custom"
18831
+ ]);
18832
+ var EventKindDescriptorSchema = object({
18833
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
18834
+ kind: string(),
18835
+ label: string(),
18836
+ /** Hex color for timeline/legend rendering. */
18837
+ color: string(),
18838
+ icon: EventKindIconSchema,
18839
+ category: EventKindCategorySchema,
18840
+ /** Which cap + device contributes this kind. For built-ins the camera
18841
+ * itself; for sensor kinds the LINKED source device. */
18842
+ source: object({
18843
+ capName: string(),
18844
+ deviceId: number()
18845
+ })
18846
+ });
18847
+ var SensorEventSchema = object({
18848
+ id: string(),
18849
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
18850
+ * yields N rows, one per camera). */
18851
+ deviceId: number(),
18852
+ /** The linked sensor device whose state changed. */
18853
+ sourceDeviceId: number(),
18854
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
18855
+ kind: string(),
18856
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
18857
+ value: record(string(), unknown()).nullable(),
18858
+ timestamp: number()
18859
+ });
18775
18860
  var TrackPositionSchema = object({
18776
18861
  x: number(),
18777
18862
  y: number(),
@@ -18785,6 +18870,30 @@ var TrackSnapshotSchema = object({
18785
18870
  mediaKey: string()
18786
18871
  });
18787
18872
  /**
18873
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
18874
+ * divided by the track's detection-frame dims), computed at persist time.
18875
+ * Absent when the frame dims were unknown when the track was persisted
18876
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
18877
+ */
18878
+ var TrackEnvelopeSchema = object({
18879
+ minX: number(),
18880
+ minY: number(),
18881
+ maxX: number(),
18882
+ maxY: number()
18883
+ });
18884
+ /**
18885
+ * Row projection for track list queries. `full` (default) returns the
18886
+ * complete Track including the frame-rate `positions[]` history and the
18887
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
18888
+ * keeps every scalar the list surfaces actually render (ids, class(es),
18889
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
18890
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
18891
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
18892
+ * `getTrack`. Mirrors the event-store `projection` convention
18893
+ * (`getObjectEvents` et al.).
18894
+ */
18895
+ var TrackProjectionSchema = _enum(["full", "slim"]);
18896
+ /**
18788
18897
  * One audio-classification label heard on the track's camera while the
18789
18898
  * track was alive, aggregated per label. An "episode" is one persisted
18790
18899
  * audio event (the confident-classification path: score ≥ the device's
@@ -18835,7 +18944,11 @@ var TrackSchema = object({
18835
18944
  /** Audio-classification labels heard on the camera during the track's
18836
18945
  * life (score ≥ device `classificationMinScore`), aggregated per label.
18837
18946
  * Absent on legacy rows / tracks with no confident audio. */
18838
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
18947
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
18948
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
18949
+ * Populated from the persisted envelope columns on historical reads;
18950
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
18951
+ envelope: TrackEnvelopeSchema.optional()
18839
18952
  });
18840
18953
  var BaseEventFields = {
18841
18954
  id: string(),
@@ -18945,11 +19058,13 @@ var MediaFileSchema = object({
18945
19058
  sizeBytes: number(),
18946
19059
  timestamp: number()
18947
19060
  });
19061
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
19062
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
18948
19063
  var DeviceEventQueryInput = object({
18949
19064
  deviceId: number(),
18950
19065
  since: number().optional(),
18951
19066
  until: number().optional(),
18952
- limit: number().int().min(1).max(5e3).default(1e3),
19067
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
18953
19068
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
18954
19069
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
18955
19070
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -18957,6 +19072,27 @@ var DeviceEventQueryInput = object({
18957
19072
  projection: _enum(["full", "slim"]).optional()
18958
19073
  });
18959
19074
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
19075
+ var RecentTracksQueryInput = object({
19076
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
19077
+ deviceIds: array(number()),
19078
+ /** Window lower bound on `lastSeen` (inclusive). */
19079
+ since: number().optional(),
19080
+ /** Window upper bound on `lastSeen` (inclusive). */
19081
+ until: number().optional(),
19082
+ /** Page size. Default 200, max 1000. */
19083
+ limit: number().int().min(1).max(1e3).default(200),
19084
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
19085
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
19086
+ cursor: string().optional(),
19087
+ /** See {@link TrackProjectionSchema}. Default `full`. */
19088
+ projection: TrackProjectionSchema.optional()
19089
+ });
19090
+ var RecentTracksPageSchema = object({
19091
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
19092
+ tracks: array(TrackSchema).readonly(),
19093
+ /** Cursor for the next page, or null when this page is the last. */
19094
+ nextCursor: string().nullable()
19095
+ });
18960
19096
  var KeyEventQueryInput = object({
18961
19097
  deviceId: number(),
18962
19098
  /** Window lower bound (track firstSeen ≥ since). */
@@ -19039,11 +19175,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19039
19175
  deviceId: number(),
19040
19176
  since: number().optional(),
19041
19177
  until: number().optional(),
19042
- limit: number().optional()
19043
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
19178
+ limit: number().optional(),
19179
+ /** Spatial filter only tracks whose trajectory intersects the zone
19180
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
19181
+ * envelope columns, then precisely tested per position. Tracks with
19182
+ * an unknown envelope (no frame dims at persist time) always match. */
19183
+ zone: TrackZoneFilterSchema.optional(),
19184
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
19185
+ * compatible — omitting the field keeps today's exact behaviour). */
19186
+ projection: TrackProjectionSchema.optional()
19187
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
19044
19188
  kind: "mutation",
19045
19189
  auth: "admin"
19046
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19190
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
19191
+ deviceId: number(),
19192
+ since: number().optional(),
19193
+ until: number().optional(),
19194
+ kinds: array(string()).optional(),
19195
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19196
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19047
19197
  deviceId: number(),
19048
19198
  since: number(),
19049
19199
  until: number(),
@@ -22295,7 +22445,12 @@ var RecordingStorageUsageSchema = object({
22295
22445
  /**
22296
22446
  * Result of locating footage at a wall-clock instant for one device/profile.
22297
22447
  * `segment` carries the covering segment's window; `gap` reports the forward
22298
- * nearest covered edge (`null` past the end of footage / when none exists).
22448
+ * nearest covered edge (`null` past the end of footage / when none exists)
22449
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
22450
+ * footage behind the epoch; `null` when none — optional so older providers
22451
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
22452
+ * small inter-segment cracks (durMs under-covers the span to the next
22453
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
22299
22454
  */
22300
22455
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
22301
22456
  kind: literal("segment"),
@@ -22304,7 +22459,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
22304
22459
  bytes: number()
22305
22460
  }), object({
22306
22461
  kind: literal("gap"),
22307
- nearestEdgeMs: number().nullable()
22462
+ nearestEdgeMs: number().nullable(),
22463
+ prevEndMs: number().nullable().optional()
22308
22464
  })]);
22309
22465
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
22310
22466
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -23886,6 +24042,12 @@ Object.freeze({
23886
24042
  addonId: null,
23887
24043
  access: "view"
23888
24044
  },
24045
+ "deviceManager.getLinkedDevices": {
24046
+ capName: "device-manager",
24047
+ capScope: "system",
24048
+ addonId: null,
24049
+ access: "view"
24050
+ },
23889
24051
  "deviceManager.getRoleDisplayDefaults": {
23890
24052
  capName: "device-manager",
23891
24053
  capScope: "system",
@@ -25440,6 +25602,12 @@ Object.freeze({
25440
25602
  addonId: null,
25441
25603
  access: "view"
25442
25604
  },
25605
+ "pipelineAnalytics.getSensorEvents": {
25606
+ capName: "pipeline-analytics",
25607
+ capScope: "device",
25608
+ addonId: null,
25609
+ access: "view"
25610
+ },
25443
25611
  "pipelineAnalytics.getTrack": {
25444
25612
  capName: "pipeline-analytics",
25445
25613
  capScope: "device",
@@ -25452,6 +25620,18 @@ Object.freeze({
25452
25620
  addonId: null,
25453
25621
  access: "view"
25454
25622
  },
25623
+ "pipelineAnalytics.listEventKinds": {
25624
+ capName: "pipeline-analytics",
25625
+ capScope: "device",
25626
+ addonId: null,
25627
+ access: "view"
25628
+ },
25629
+ "pipelineAnalytics.listRecentTracks": {
25630
+ capName: "pipeline-analytics",
25631
+ capScope: "device",
25632
+ addonId: null,
25633
+ access: "view"
25634
+ },
25455
25635
  "pipelineAnalytics.listTracks": {
25456
25636
  capName: "pipeline-analytics",
25457
25637
  capScope: "device",
package/dist/addon.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import net from "node:net";
2
- //#region ../types/dist/event-category-CFZs3jI4.mjs
2
+ //#region ../types/dist/event-category-H4AVePnn.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -149,6 +149,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
149
149
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
150
150
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
151
151
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
152
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
153
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
154
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
152
155
  EventCategory["DetectionEvent"] = "detection.event";
153
156
  EventCategory["SessionTrackNew"] = "session.track.new";
154
157
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -10882,10 +10885,23 @@ var deviceDiscoveryCapability = {
10882
10885
  }
10883
10886
  };
10884
10887
  /**
10885
- * Doorbell button cap. Installed on a `DeviceType.Button` accessory
10886
- * with `role: DeviceRole.Doorbell`. Emits an `onPressed` event every
10887
- * time the firmware pushes a ring; status tracks the last press and
10888
- * a pressCount since start (diagnostic).
10888
+ * Doorbell button cap. Two kinds of providers coexist behind this cap
10889
+ * name (same pattern as `snapshot`):
10890
+ *
10891
+ * - **Native** providers: registered per-device by device-driver
10892
+ * addons via `ctx.registerNativeCap` — either on a
10893
+ * `DeviceType.Button` accessory with `role: DeviceRole.Doorbell`,
10894
+ * or directly on the camera (Reolink registers at camera level).
10895
+ * Emits an `onPressed` event every time the firmware pushes a
10896
+ * ring; status tracks the last press and a pressCount since start
10897
+ * (diagnostic).
10898
+ *
10899
+ * - **Wrapper** provider: the `virtual-doorbell` system builtin
10900
+ * (`@camstack/system/builtins/doorbell`). Turns ANY binary-ish
10901
+ * device (contact / switch / event-emitter …) into a doorbell for
10902
+ * a camera. `defaultActive: false` — the operator explicitly binds
10903
+ * it per camera in the device-bindings UI, then picks the source
10904
+ * device + trigger in the per-device settings.
10889
10905
  *
10890
10906
  * The DeviceEventPropagator re-emits `onPressed` on the camera parent
10891
10907
  * — subscribers listening at the camera level receive ring events
@@ -10906,7 +10922,10 @@ var doorbellCapability = {
10906
10922
  scope: "device",
10907
10923
  deviceNative: true,
10908
10924
  mode: "singleton",
10909
- deviceTypes: [DeviceType.Button],
10925
+ kind: "wrapper",
10926
+ defaultActive: false,
10927
+ deviceTypes: [DeviceType.Button, DeviceType.Camera],
10928
+ exposesDeviceSettings: true,
10910
10929
  methods: {},
10911
10930
  events: {
10912
10931
  /**
@@ -17258,6 +17277,14 @@ var ConfigEntrySchema = object({
17258
17277
  value: unknown(),
17259
17278
  description: string().optional()
17260
17279
  });
17280
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
17281
+ /** One resolved linked device — the compact projection consumers need. */
17282
+ var LinkedDeviceSchema = object({
17283
+ deviceId: number(),
17284
+ name: string(),
17285
+ location: string().nullable(),
17286
+ features: array(string())
17287
+ });
17261
17288
  var SavedDeviceRowSchema = object({
17262
17289
  /** Numeric id reserved at allocateDeviceId time. */
17263
17290
  id: number(),
@@ -17479,7 +17506,10 @@ method(object({
17479
17506
  }), _void(), {
17480
17507
  kind: "mutation",
17481
17508
  auth: "admin"
17482
- }), 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({
17509
+ }), 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({
17510
+ mode: DeviceLinkModeSchema,
17511
+ devices: array(LinkedDeviceSchema)
17512
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
17483
17513
  deviceId: number(),
17484
17514
  values: record(string(), unknown())
17485
17515
  }), object({ success: literal(true) }), {
@@ -18748,6 +18778,61 @@ var EventKindSchema = _enum([
18748
18778
  "object",
18749
18779
  "audio"
18750
18780
  ]);
18781
+ /**
18782
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
18783
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
18784
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
18785
+ */
18786
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
18787
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
18788
+ var EventKindIconSchema = _enum([
18789
+ "motion",
18790
+ "audio",
18791
+ "person",
18792
+ "vehicle",
18793
+ "animal",
18794
+ "door",
18795
+ "pir",
18796
+ "smoke",
18797
+ "water",
18798
+ "button",
18799
+ "generic"
18800
+ ]);
18801
+ var EventKindCategorySchema = _enum([
18802
+ "motion",
18803
+ "audio",
18804
+ "detection",
18805
+ "sensor",
18806
+ "custom"
18807
+ ]);
18808
+ var EventKindDescriptorSchema = object({
18809
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
18810
+ kind: string(),
18811
+ label: string(),
18812
+ /** Hex color for timeline/legend rendering. */
18813
+ color: string(),
18814
+ icon: EventKindIconSchema,
18815
+ category: EventKindCategorySchema,
18816
+ /** Which cap + device contributes this kind. For built-ins the camera
18817
+ * itself; for sensor kinds the LINKED source device. */
18818
+ source: object({
18819
+ capName: string(),
18820
+ deviceId: number()
18821
+ })
18822
+ });
18823
+ var SensorEventSchema = object({
18824
+ id: string(),
18825
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
18826
+ * yields N rows, one per camera). */
18827
+ deviceId: number(),
18828
+ /** The linked sensor device whose state changed. */
18829
+ sourceDeviceId: number(),
18830
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
18831
+ kind: string(),
18832
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
18833
+ value: record(string(), unknown()).nullable(),
18834
+ timestamp: number()
18835
+ });
18751
18836
  var TrackPositionSchema = object({
18752
18837
  x: number(),
18753
18838
  y: number(),
@@ -18761,6 +18846,30 @@ var TrackSnapshotSchema = object({
18761
18846
  mediaKey: string()
18762
18847
  });
18763
18848
  /**
18849
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
18850
+ * divided by the track's detection-frame dims), computed at persist time.
18851
+ * Absent when the frame dims were unknown when the track was persisted
18852
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
18853
+ */
18854
+ var TrackEnvelopeSchema = object({
18855
+ minX: number(),
18856
+ minY: number(),
18857
+ maxX: number(),
18858
+ maxY: number()
18859
+ });
18860
+ /**
18861
+ * Row projection for track list queries. `full` (default) returns the
18862
+ * complete Track including the frame-rate `positions[]` history and the
18863
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
18864
+ * keeps every scalar the list surfaces actually render (ids, class(es),
18865
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
18866
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
18867
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
18868
+ * `getTrack`. Mirrors the event-store `projection` convention
18869
+ * (`getObjectEvents` et al.).
18870
+ */
18871
+ var TrackProjectionSchema = _enum(["full", "slim"]);
18872
+ /**
18764
18873
  * One audio-classification label heard on the track's camera while the
18765
18874
  * track was alive, aggregated per label. An "episode" is one persisted
18766
18875
  * audio event (the confident-classification path: score ≥ the device's
@@ -18811,7 +18920,11 @@ var TrackSchema = object({
18811
18920
  /** Audio-classification labels heard on the camera during the track's
18812
18921
  * life (score ≥ device `classificationMinScore`), aggregated per label.
18813
18922
  * Absent on legacy rows / tracks with no confident audio. */
18814
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
18923
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
18924
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
18925
+ * Populated from the persisted envelope columns on historical reads;
18926
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
18927
+ envelope: TrackEnvelopeSchema.optional()
18815
18928
  });
18816
18929
  var BaseEventFields = {
18817
18930
  id: string(),
@@ -18921,11 +19034,13 @@ var MediaFileSchema = object({
18921
19034
  sizeBytes: number(),
18922
19035
  timestamp: number()
18923
19036
  });
19037
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
19038
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
18924
19039
  var DeviceEventQueryInput = object({
18925
19040
  deviceId: number(),
18926
19041
  since: number().optional(),
18927
19042
  until: number().optional(),
18928
- limit: number().int().min(1).max(5e3).default(1e3),
19043
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
18929
19044
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
18930
19045
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
18931
19046
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -18933,6 +19048,27 @@ var DeviceEventQueryInput = object({
18933
19048
  projection: _enum(["full", "slim"]).optional()
18934
19049
  });
18935
19050
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
19051
+ var RecentTracksQueryInput = object({
19052
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
19053
+ deviceIds: array(number()),
19054
+ /** Window lower bound on `lastSeen` (inclusive). */
19055
+ since: number().optional(),
19056
+ /** Window upper bound on `lastSeen` (inclusive). */
19057
+ until: number().optional(),
19058
+ /** Page size. Default 200, max 1000. */
19059
+ limit: number().int().min(1).max(1e3).default(200),
19060
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
19061
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
19062
+ cursor: string().optional(),
19063
+ /** See {@link TrackProjectionSchema}. Default `full`. */
19064
+ projection: TrackProjectionSchema.optional()
19065
+ });
19066
+ var RecentTracksPageSchema = object({
19067
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
19068
+ tracks: array(TrackSchema).readonly(),
19069
+ /** Cursor for the next page, or null when this page is the last. */
19070
+ nextCursor: string().nullable()
19071
+ });
18936
19072
  var KeyEventQueryInput = object({
18937
19073
  deviceId: number(),
18938
19074
  /** Window lower bound (track firstSeen ≥ since). */
@@ -19015,11 +19151,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19015
19151
  deviceId: number(),
19016
19152
  since: number().optional(),
19017
19153
  until: number().optional(),
19018
- limit: number().optional()
19019
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
19154
+ limit: number().optional(),
19155
+ /** Spatial filter only tracks whose trajectory intersects the zone
19156
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
19157
+ * envelope columns, then precisely tested per position. Tracks with
19158
+ * an unknown envelope (no frame dims at persist time) always match. */
19159
+ zone: TrackZoneFilterSchema.optional(),
19160
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
19161
+ * compatible — omitting the field keeps today's exact behaviour). */
19162
+ projection: TrackProjectionSchema.optional()
19163
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
19020
19164
  kind: "mutation",
19021
19165
  auth: "admin"
19022
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19166
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
19167
+ deviceId: number(),
19168
+ since: number().optional(),
19169
+ until: number().optional(),
19170
+ kinds: array(string()).optional(),
19171
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19172
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19023
19173
  deviceId: number(),
19024
19174
  since: number(),
19025
19175
  until: number(),
@@ -22271,7 +22421,12 @@ var RecordingStorageUsageSchema = object({
22271
22421
  /**
22272
22422
  * Result of locating footage at a wall-clock instant for one device/profile.
22273
22423
  * `segment` carries the covering segment's window; `gap` reports the forward
22274
- * nearest covered edge (`null` past the end of footage / when none exists).
22424
+ * nearest covered edge (`null` past the end of footage / when none exists)
22425
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
22426
+ * footage behind the epoch; `null` when none — optional so older providers
22427
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
22428
+ * small inter-segment cracks (durMs under-covers the span to the next
22429
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
22275
22430
  */
22276
22431
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
22277
22432
  kind: literal("segment"),
@@ -22280,7 +22435,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
22280
22435
  bytes: number()
22281
22436
  }), object({
22282
22437
  kind: literal("gap"),
22283
- nearestEdgeMs: number().nullable()
22438
+ nearestEdgeMs: number().nullable(),
22439
+ prevEndMs: number().nullable().optional()
22284
22440
  })]);
22285
22441
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
22286
22442
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -23862,6 +24018,12 @@ Object.freeze({
23862
24018
  addonId: null,
23863
24019
  access: "view"
23864
24020
  },
24021
+ "deviceManager.getLinkedDevices": {
24022
+ capName: "device-manager",
24023
+ capScope: "system",
24024
+ addonId: null,
24025
+ access: "view"
24026
+ },
23865
24027
  "deviceManager.getRoleDisplayDefaults": {
23866
24028
  capName: "device-manager",
23867
24029
  capScope: "system",
@@ -25416,6 +25578,12 @@ Object.freeze({
25416
25578
  addonId: null,
25417
25579
  access: "view"
25418
25580
  },
25581
+ "pipelineAnalytics.getSensorEvents": {
25582
+ capName: "pipeline-analytics",
25583
+ capScope: "device",
25584
+ addonId: null,
25585
+ access: "view"
25586
+ },
25419
25587
  "pipelineAnalytics.getTrack": {
25420
25588
  capName: "pipeline-analytics",
25421
25589
  capScope: "device",
@@ -25428,6 +25596,18 @@ Object.freeze({
25428
25596
  addonId: null,
25429
25597
  access: "view"
25430
25598
  },
25599
+ "pipelineAnalytics.listEventKinds": {
25600
+ capName: "pipeline-analytics",
25601
+ capScope: "device",
25602
+ addonId: null,
25603
+ access: "view"
25604
+ },
25605
+ "pipelineAnalytics.listRecentTracks": {
25606
+ capName: "pipeline-analytics",
25607
+ capScope: "device",
25608
+ addonId: null,
25609
+ access: "view"
25610
+ },
25431
25611
  "pipelineAnalytics.listTracks": {
25432
25612
  capName: "pipeline-analytics",
25433
25613
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rtsp",
3
- "version": "1.1.23",
3
+ "version": "1.1.24",
4
4
  "description": "Generic RTSP camera device provider addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",