@camstack/addon-provider-wyze 0.1.20 → 0.1.21

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
@@ -30,7 +30,7 @@ let events = require("events");
30
30
  let net = require("net");
31
31
  net = __toESM(net, 1);
32
32
  let node_child_process = require("node:child_process");
33
- //#region ../types/dist/event-category-CFZs3jI4.mjs
33
+ //#region ../types/dist/event-category-H4AVePnn.mjs
34
34
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
35
35
  EventCategory["SystemBoot"] = "system.boot";
36
36
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -180,6 +180,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
180
180
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
181
181
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
182
182
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
183
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
184
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
185
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
183
186
  EventCategory["DetectionEvent"] = "detection.event";
184
187
  EventCategory["SessionTrackNew"] = "session.track.new";
185
188
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -10813,10 +10816,23 @@ var deviceDiscoveryCapability = {
10813
10816
  }
10814
10817
  };
10815
10818
  /**
10816
- * Doorbell button cap. Installed on a `DeviceType.Button` accessory
10817
- * with `role: DeviceRole.Doorbell`. Emits an `onPressed` event every
10818
- * time the firmware pushes a ring; status tracks the last press and
10819
- * a pressCount since start (diagnostic).
10819
+ * Doorbell button cap. Two kinds of providers coexist behind this cap
10820
+ * name (same pattern as `snapshot`):
10821
+ *
10822
+ * - **Native** providers: registered per-device by device-driver
10823
+ * addons via `ctx.registerNativeCap` — either on a
10824
+ * `DeviceType.Button` accessory with `role: DeviceRole.Doorbell`,
10825
+ * or directly on the camera (Reolink registers at camera level).
10826
+ * Emits an `onPressed` event every time the firmware pushes a
10827
+ * ring; status tracks the last press and a pressCount since start
10828
+ * (diagnostic).
10829
+ *
10830
+ * - **Wrapper** provider: the `virtual-doorbell` system builtin
10831
+ * (`@camstack/system/builtins/doorbell`). Turns ANY binary-ish
10832
+ * device (contact / switch / event-emitter …) into a doorbell for
10833
+ * a camera. `defaultActive: false` — the operator explicitly binds
10834
+ * it per camera in the device-bindings UI, then picks the source
10835
+ * device + trigger in the per-device settings.
10820
10836
  *
10821
10837
  * The DeviceEventPropagator re-emits `onPressed` on the camera parent
10822
10838
  * — subscribers listening at the camera level receive ring events
@@ -10837,7 +10853,10 @@ var doorbellCapability = {
10837
10853
  scope: "device",
10838
10854
  deviceNative: true,
10839
10855
  mode: "singleton",
10840
- deviceTypes: [DeviceType.Button],
10856
+ kind: "wrapper",
10857
+ defaultActive: false,
10858
+ deviceTypes: [DeviceType.Button, DeviceType.Camera],
10859
+ exposesDeviceSettings: true,
10841
10860
  methods: {},
10842
10861
  events: {
10843
10862
  /**
@@ -17206,6 +17225,14 @@ var ConfigEntrySchema = object({
17206
17225
  value: unknown(),
17207
17226
  description: string().optional()
17208
17227
  });
17228
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
17229
+ /** One resolved linked device — the compact projection consumers need. */
17230
+ var LinkedDeviceSchema = object({
17231
+ deviceId: number(),
17232
+ name: string(),
17233
+ location: string().nullable(),
17234
+ features: array(string())
17235
+ });
17209
17236
  var SavedDeviceRowSchema = object({
17210
17237
  /** Numeric id reserved at allocateDeviceId time. */
17211
17238
  id: number(),
@@ -17427,7 +17454,10 @@ method(object({
17427
17454
  }), _void(), {
17428
17455
  kind: "mutation",
17429
17456
  auth: "admin"
17430
- }), 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({
17457
+ }), 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({
17458
+ mode: DeviceLinkModeSchema,
17459
+ devices: array(LinkedDeviceSchema)
17460
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
17431
17461
  deviceId: number(),
17432
17462
  values: record(string(), unknown())
17433
17463
  }), object({ success: literal(true) }), {
@@ -18696,6 +18726,61 @@ var EventKindSchema = _enum([
18696
18726
  "object",
18697
18727
  "audio"
18698
18728
  ]);
18729
+ /**
18730
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
18731
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
18732
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
18733
+ */
18734
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
18735
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
18736
+ var EventKindIconSchema = _enum([
18737
+ "motion",
18738
+ "audio",
18739
+ "person",
18740
+ "vehicle",
18741
+ "animal",
18742
+ "door",
18743
+ "pir",
18744
+ "smoke",
18745
+ "water",
18746
+ "button",
18747
+ "generic"
18748
+ ]);
18749
+ var EventKindCategorySchema = _enum([
18750
+ "motion",
18751
+ "audio",
18752
+ "detection",
18753
+ "sensor",
18754
+ "custom"
18755
+ ]);
18756
+ var EventKindDescriptorSchema = object({
18757
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
18758
+ kind: string(),
18759
+ label: string(),
18760
+ /** Hex color for timeline/legend rendering. */
18761
+ color: string(),
18762
+ icon: EventKindIconSchema,
18763
+ category: EventKindCategorySchema,
18764
+ /** Which cap + device contributes this kind. For built-ins the camera
18765
+ * itself; for sensor kinds the LINKED source device. */
18766
+ source: object({
18767
+ capName: string(),
18768
+ deviceId: number()
18769
+ })
18770
+ });
18771
+ var SensorEventSchema = object({
18772
+ id: string(),
18773
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
18774
+ * yields N rows, one per camera). */
18775
+ deviceId: number(),
18776
+ /** The linked sensor device whose state changed. */
18777
+ sourceDeviceId: number(),
18778
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
18779
+ kind: string(),
18780
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
18781
+ value: record(string(), unknown()).nullable(),
18782
+ timestamp: number()
18783
+ });
18699
18784
  var TrackPositionSchema = object({
18700
18785
  x: number(),
18701
18786
  y: number(),
@@ -18709,6 +18794,30 @@ var TrackSnapshotSchema = object({
18709
18794
  mediaKey: string()
18710
18795
  });
18711
18796
  /**
18797
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
18798
+ * divided by the track's detection-frame dims), computed at persist time.
18799
+ * Absent when the frame dims were unknown when the track was persisted
18800
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
18801
+ */
18802
+ var TrackEnvelopeSchema = object({
18803
+ minX: number(),
18804
+ minY: number(),
18805
+ maxX: number(),
18806
+ maxY: number()
18807
+ });
18808
+ /**
18809
+ * Row projection for track list queries. `full` (default) returns the
18810
+ * complete Track including the frame-rate `positions[]` history and the
18811
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
18812
+ * keeps every scalar the list surfaces actually render (ids, class(es),
18813
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
18814
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
18815
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
18816
+ * `getTrack`. Mirrors the event-store `projection` convention
18817
+ * (`getObjectEvents` et al.).
18818
+ */
18819
+ var TrackProjectionSchema = _enum(["full", "slim"]);
18820
+ /**
18712
18821
  * One audio-classification label heard on the track's camera while the
18713
18822
  * track was alive, aggregated per label. An "episode" is one persisted
18714
18823
  * audio event (the confident-classification path: score ≥ the device's
@@ -18759,7 +18868,11 @@ var TrackSchema = object({
18759
18868
  /** Audio-classification labels heard on the camera during the track's
18760
18869
  * life (score ≥ device `classificationMinScore`), aggregated per label.
18761
18870
  * Absent on legacy rows / tracks with no confident audio. */
18762
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
18871
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
18872
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
18873
+ * Populated from the persisted envelope columns on historical reads;
18874
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
18875
+ envelope: TrackEnvelopeSchema.optional()
18763
18876
  });
18764
18877
  var BaseEventFields = {
18765
18878
  id: string(),
@@ -18869,11 +18982,13 @@ var MediaFileSchema = object({
18869
18982
  sizeBytes: number(),
18870
18983
  timestamp: number()
18871
18984
  });
18985
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
18986
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
18872
18987
  var DeviceEventQueryInput = object({
18873
18988
  deviceId: number(),
18874
18989
  since: number().optional(),
18875
18990
  until: number().optional(),
18876
- limit: number().int().min(1).max(5e3).default(1e3),
18991
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
18877
18992
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
18878
18993
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
18879
18994
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -18881,6 +18996,27 @@ var DeviceEventQueryInput = object({
18881
18996
  projection: _enum(["full", "slim"]).optional()
18882
18997
  });
18883
18998
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
18999
+ var RecentTracksQueryInput = object({
19000
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
19001
+ deviceIds: array(number()),
19002
+ /** Window lower bound on `lastSeen` (inclusive). */
19003
+ since: number().optional(),
19004
+ /** Window upper bound on `lastSeen` (inclusive). */
19005
+ until: number().optional(),
19006
+ /** Page size. Default 200, max 1000. */
19007
+ limit: number().int().min(1).max(1e3).default(200),
19008
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
19009
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
19010
+ cursor: string().optional(),
19011
+ /** See {@link TrackProjectionSchema}. Default `full`. */
19012
+ projection: TrackProjectionSchema.optional()
19013
+ });
19014
+ var RecentTracksPageSchema = object({
19015
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
19016
+ tracks: array(TrackSchema).readonly(),
19017
+ /** Cursor for the next page, or null when this page is the last. */
19018
+ nextCursor: string().nullable()
19019
+ });
18884
19020
  var KeyEventQueryInput = object({
18885
19021
  deviceId: number(),
18886
19022
  /** Window lower bound (track firstSeen ≥ since). */
@@ -18963,11 +19099,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18963
19099
  deviceId: number(),
18964
19100
  since: number().optional(),
18965
19101
  until: number().optional(),
18966
- limit: number().optional()
18967
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
19102
+ limit: number().optional(),
19103
+ /** Spatial filter only tracks whose trajectory intersects the zone
19104
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
19105
+ * envelope columns, then precisely tested per position. Tracks with
19106
+ * an unknown envelope (no frame dims at persist time) always match. */
19107
+ zone: TrackZoneFilterSchema.optional(),
19108
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
19109
+ * compatible — omitting the field keeps today's exact behaviour). */
19110
+ projection: TrackProjectionSchema.optional()
19111
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
18968
19112
  kind: "mutation",
18969
19113
  auth: "admin"
18970
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19114
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
19115
+ deviceId: number(),
19116
+ since: number().optional(),
19117
+ until: number().optional(),
19118
+ kinds: array(string()).optional(),
19119
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19120
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
18971
19121
  deviceId: number(),
18972
19122
  since: number(),
18973
19123
  until: number(),
@@ -22270,7 +22420,12 @@ var RecordingStorageUsageSchema = object({
22270
22420
  /**
22271
22421
  * Result of locating footage at a wall-clock instant for one device/profile.
22272
22422
  * `segment` carries the covering segment's window; `gap` reports the forward
22273
- * nearest covered edge (`null` past the end of footage / when none exists).
22423
+ * nearest covered edge (`null` past the end of footage / when none exists)
22424
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
22425
+ * footage behind the epoch; `null` when none — optional so older providers
22426
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
22427
+ * small inter-segment cracks (durMs under-covers the span to the next
22428
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
22274
22429
  */
22275
22430
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
22276
22431
  kind: literal("segment"),
@@ -22279,7 +22434,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
22279
22434
  bytes: number()
22280
22435
  }), object({
22281
22436
  kind: literal("gap"),
22282
- nearestEdgeMs: number().nullable()
22437
+ nearestEdgeMs: number().nullable(),
22438
+ prevEndMs: number().nullable().optional()
22283
22439
  })]);
22284
22440
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
22285
22441
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -23861,6 +24017,12 @@ Object.freeze({
23861
24017
  addonId: null,
23862
24018
  access: "view"
23863
24019
  },
24020
+ "deviceManager.getLinkedDevices": {
24021
+ capName: "device-manager",
24022
+ capScope: "system",
24023
+ addonId: null,
24024
+ access: "view"
24025
+ },
23864
24026
  "deviceManager.getRoleDisplayDefaults": {
23865
24027
  capName: "device-manager",
23866
24028
  capScope: "system",
@@ -25415,6 +25577,12 @@ Object.freeze({
25415
25577
  addonId: null,
25416
25578
  access: "view"
25417
25579
  },
25580
+ "pipelineAnalytics.getSensorEvents": {
25581
+ capName: "pipeline-analytics",
25582
+ capScope: "device",
25583
+ addonId: null,
25584
+ access: "view"
25585
+ },
25418
25586
  "pipelineAnalytics.getTrack": {
25419
25587
  capName: "pipeline-analytics",
25420
25588
  capScope: "device",
@@ -25427,6 +25595,18 @@ Object.freeze({
25427
25595
  addonId: null,
25428
25596
  access: "view"
25429
25597
  },
25598
+ "pipelineAnalytics.listEventKinds": {
25599
+ capName: "pipeline-analytics",
25600
+ capScope: "device",
25601
+ addonId: null,
25602
+ access: "view"
25603
+ },
25604
+ "pipelineAnalytics.listRecentTracks": {
25605
+ capName: "pipeline-analytics",
25606
+ capScope: "device",
25607
+ addonId: null,
25608
+ access: "view"
25609
+ },
25430
25610
  "pipelineAnalytics.listTracks": {
25431
25611
  capName: "pipeline-analytics",
25432
25612
  capScope: "device",
package/dist/addon.mjs CHANGED
@@ -9,7 +9,7 @@ import { spawn } from "node:child_process";
9
9
  //#region \0rolldown/runtime.js
10
10
  var __require$1 = /* @__PURE__ */ createRequire(import.meta.url);
11
11
  //#endregion
12
- //#region ../types/dist/event-category-CFZs3jI4.mjs
12
+ //#region ../types/dist/event-category-H4AVePnn.mjs
13
13
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
14
14
  EventCategory["SystemBoot"] = "system.boot";
15
15
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -159,6 +159,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
159
159
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
160
160
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
161
161
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
162
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
163
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
164
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
162
165
  EventCategory["DetectionEvent"] = "detection.event";
163
166
  EventCategory["SessionTrackNew"] = "session.track.new";
164
167
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -10792,10 +10795,23 @@ var deviceDiscoveryCapability = {
10792
10795
  }
10793
10796
  };
10794
10797
  /**
10795
- * Doorbell button cap. Installed on a `DeviceType.Button` accessory
10796
- * with `role: DeviceRole.Doorbell`. Emits an `onPressed` event every
10797
- * time the firmware pushes a ring; status tracks the last press and
10798
- * a pressCount since start (diagnostic).
10798
+ * Doorbell button cap. Two kinds of providers coexist behind this cap
10799
+ * name (same pattern as `snapshot`):
10800
+ *
10801
+ * - **Native** providers: registered per-device by device-driver
10802
+ * addons via `ctx.registerNativeCap` — either on a
10803
+ * `DeviceType.Button` accessory with `role: DeviceRole.Doorbell`,
10804
+ * or directly on the camera (Reolink registers at camera level).
10805
+ * Emits an `onPressed` event every time the firmware pushes a
10806
+ * ring; status tracks the last press and a pressCount since start
10807
+ * (diagnostic).
10808
+ *
10809
+ * - **Wrapper** provider: the `virtual-doorbell` system builtin
10810
+ * (`@camstack/system/builtins/doorbell`). Turns ANY binary-ish
10811
+ * device (contact / switch / event-emitter …) into a doorbell for
10812
+ * a camera. `defaultActive: false` — the operator explicitly binds
10813
+ * it per camera in the device-bindings UI, then picks the source
10814
+ * device + trigger in the per-device settings.
10799
10815
  *
10800
10816
  * The DeviceEventPropagator re-emits `onPressed` on the camera parent
10801
10817
  * — subscribers listening at the camera level receive ring events
@@ -10816,7 +10832,10 @@ var doorbellCapability = {
10816
10832
  scope: "device",
10817
10833
  deviceNative: true,
10818
10834
  mode: "singleton",
10819
- deviceTypes: [DeviceType.Button],
10835
+ kind: "wrapper",
10836
+ defaultActive: false,
10837
+ deviceTypes: [DeviceType.Button, DeviceType.Camera],
10838
+ exposesDeviceSettings: true,
10820
10839
  methods: {},
10821
10840
  events: {
10822
10841
  /**
@@ -17185,6 +17204,14 @@ var ConfigEntrySchema = object({
17185
17204
  value: unknown(),
17186
17205
  description: string().optional()
17187
17206
  });
17207
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
17208
+ /** One resolved linked device — the compact projection consumers need. */
17209
+ var LinkedDeviceSchema = object({
17210
+ deviceId: number(),
17211
+ name: string(),
17212
+ location: string().nullable(),
17213
+ features: array(string())
17214
+ });
17188
17215
  var SavedDeviceRowSchema = object({
17189
17216
  /** Numeric id reserved at allocateDeviceId time. */
17190
17217
  id: number(),
@@ -17406,7 +17433,10 @@ method(object({
17406
17433
  }), _void(), {
17407
17434
  kind: "mutation",
17408
17435
  auth: "admin"
17409
- }), 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({
17436
+ }), 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({
17437
+ mode: DeviceLinkModeSchema,
17438
+ devices: array(LinkedDeviceSchema)
17439
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
17410
17440
  deviceId: number(),
17411
17441
  values: record(string(), unknown())
17412
17442
  }), object({ success: literal(true) }), {
@@ -18675,6 +18705,61 @@ var EventKindSchema = _enum([
18675
18705
  "object",
18676
18706
  "audio"
18677
18707
  ]);
18708
+ /**
18709
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
18710
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
18711
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
18712
+ */
18713
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
18714
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
18715
+ var EventKindIconSchema = _enum([
18716
+ "motion",
18717
+ "audio",
18718
+ "person",
18719
+ "vehicle",
18720
+ "animal",
18721
+ "door",
18722
+ "pir",
18723
+ "smoke",
18724
+ "water",
18725
+ "button",
18726
+ "generic"
18727
+ ]);
18728
+ var EventKindCategorySchema = _enum([
18729
+ "motion",
18730
+ "audio",
18731
+ "detection",
18732
+ "sensor",
18733
+ "custom"
18734
+ ]);
18735
+ var EventKindDescriptorSchema = object({
18736
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
18737
+ kind: string(),
18738
+ label: string(),
18739
+ /** Hex color for timeline/legend rendering. */
18740
+ color: string(),
18741
+ icon: EventKindIconSchema,
18742
+ category: EventKindCategorySchema,
18743
+ /** Which cap + device contributes this kind. For built-ins the camera
18744
+ * itself; for sensor kinds the LINKED source device. */
18745
+ source: object({
18746
+ capName: string(),
18747
+ deviceId: number()
18748
+ })
18749
+ });
18750
+ var SensorEventSchema = object({
18751
+ id: string(),
18752
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
18753
+ * yields N rows, one per camera). */
18754
+ deviceId: number(),
18755
+ /** The linked sensor device whose state changed. */
18756
+ sourceDeviceId: number(),
18757
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
18758
+ kind: string(),
18759
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
18760
+ value: record(string(), unknown()).nullable(),
18761
+ timestamp: number()
18762
+ });
18678
18763
  var TrackPositionSchema = object({
18679
18764
  x: number(),
18680
18765
  y: number(),
@@ -18688,6 +18773,30 @@ var TrackSnapshotSchema = object({
18688
18773
  mediaKey: string()
18689
18774
  });
18690
18775
  /**
18776
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
18777
+ * divided by the track's detection-frame dims), computed at persist time.
18778
+ * Absent when the frame dims were unknown when the track was persisted
18779
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
18780
+ */
18781
+ var TrackEnvelopeSchema = object({
18782
+ minX: number(),
18783
+ minY: number(),
18784
+ maxX: number(),
18785
+ maxY: number()
18786
+ });
18787
+ /**
18788
+ * Row projection for track list queries. `full` (default) returns the
18789
+ * complete Track including the frame-rate `positions[]` history and the
18790
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
18791
+ * keeps every scalar the list surfaces actually render (ids, class(es),
18792
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
18793
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
18794
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
18795
+ * `getTrack`. Mirrors the event-store `projection` convention
18796
+ * (`getObjectEvents` et al.).
18797
+ */
18798
+ var TrackProjectionSchema = _enum(["full", "slim"]);
18799
+ /**
18691
18800
  * One audio-classification label heard on the track's camera while the
18692
18801
  * track was alive, aggregated per label. An "episode" is one persisted
18693
18802
  * audio event (the confident-classification path: score ≥ the device's
@@ -18738,7 +18847,11 @@ var TrackSchema = object({
18738
18847
  /** Audio-classification labels heard on the camera during the track's
18739
18848
  * life (score ≥ device `classificationMinScore`), aggregated per label.
18740
18849
  * Absent on legacy rows / tracks with no confident audio. */
18741
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
18850
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
18851
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
18852
+ * Populated from the persisted envelope columns on historical reads;
18853
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
18854
+ envelope: TrackEnvelopeSchema.optional()
18742
18855
  });
18743
18856
  var BaseEventFields = {
18744
18857
  id: string(),
@@ -18848,11 +18961,13 @@ var MediaFileSchema = object({
18848
18961
  sizeBytes: number(),
18849
18962
  timestamp: number()
18850
18963
  });
18964
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
18965
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
18851
18966
  var DeviceEventQueryInput = object({
18852
18967
  deviceId: number(),
18853
18968
  since: number().optional(),
18854
18969
  until: number().optional(),
18855
- limit: number().int().min(1).max(5e3).default(1e3),
18970
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
18856
18971
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
18857
18972
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
18858
18973
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -18860,6 +18975,27 @@ var DeviceEventQueryInput = object({
18860
18975
  projection: _enum(["full", "slim"]).optional()
18861
18976
  });
18862
18977
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
18978
+ var RecentTracksQueryInput = object({
18979
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
18980
+ deviceIds: array(number()),
18981
+ /** Window lower bound on `lastSeen` (inclusive). */
18982
+ since: number().optional(),
18983
+ /** Window upper bound on `lastSeen` (inclusive). */
18984
+ until: number().optional(),
18985
+ /** Page size. Default 200, max 1000. */
18986
+ limit: number().int().min(1).max(1e3).default(200),
18987
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
18988
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
18989
+ cursor: string().optional(),
18990
+ /** See {@link TrackProjectionSchema}. Default `full`. */
18991
+ projection: TrackProjectionSchema.optional()
18992
+ });
18993
+ var RecentTracksPageSchema = object({
18994
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
18995
+ tracks: array(TrackSchema).readonly(),
18996
+ /** Cursor for the next page, or null when this page is the last. */
18997
+ nextCursor: string().nullable()
18998
+ });
18863
18999
  var KeyEventQueryInput = object({
18864
19000
  deviceId: number(),
18865
19001
  /** Window lower bound (track firstSeen ≥ since). */
@@ -18942,11 +19078,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18942
19078
  deviceId: number(),
18943
19079
  since: number().optional(),
18944
19080
  until: number().optional(),
18945
- limit: number().optional()
18946
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
19081
+ limit: number().optional(),
19082
+ /** Spatial filter only tracks whose trajectory intersects the zone
19083
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
19084
+ * envelope columns, then precisely tested per position. Tracks with
19085
+ * an unknown envelope (no frame dims at persist time) always match. */
19086
+ zone: TrackZoneFilterSchema.optional(),
19087
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
19088
+ * compatible — omitting the field keeps today's exact behaviour). */
19089
+ projection: TrackProjectionSchema.optional()
19090
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
18947
19091
  kind: "mutation",
18948
19092
  auth: "admin"
18949
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19093
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
19094
+ deviceId: number(),
19095
+ since: number().optional(),
19096
+ until: number().optional(),
19097
+ kinds: array(string()).optional(),
19098
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19099
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
18950
19100
  deviceId: number(),
18951
19101
  since: number(),
18952
19102
  until: number(),
@@ -22249,7 +22399,12 @@ var RecordingStorageUsageSchema = object({
22249
22399
  /**
22250
22400
  * Result of locating footage at a wall-clock instant for one device/profile.
22251
22401
  * `segment` carries the covering segment's window; `gap` reports the forward
22252
- * nearest covered edge (`null` past the end of footage / when none exists).
22402
+ * nearest covered edge (`null` past the end of footage / when none exists)
22403
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
22404
+ * footage behind the epoch; `null` when none — optional so older providers
22405
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
22406
+ * small inter-segment cracks (durMs under-covers the span to the next
22407
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
22253
22408
  */
22254
22409
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
22255
22410
  kind: literal("segment"),
@@ -22258,7 +22413,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
22258
22413
  bytes: number()
22259
22414
  }), object({
22260
22415
  kind: literal("gap"),
22261
- nearestEdgeMs: number().nullable()
22416
+ nearestEdgeMs: number().nullable(),
22417
+ prevEndMs: number().nullable().optional()
22262
22418
  })]);
22263
22419
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
22264
22420
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -23840,6 +23996,12 @@ Object.freeze({
23840
23996
  addonId: null,
23841
23997
  access: "view"
23842
23998
  },
23999
+ "deviceManager.getLinkedDevices": {
24000
+ capName: "device-manager",
24001
+ capScope: "system",
24002
+ addonId: null,
24003
+ access: "view"
24004
+ },
23843
24005
  "deviceManager.getRoleDisplayDefaults": {
23844
24006
  capName: "device-manager",
23845
24007
  capScope: "system",
@@ -25394,6 +25556,12 @@ Object.freeze({
25394
25556
  addonId: null,
25395
25557
  access: "view"
25396
25558
  },
25559
+ "pipelineAnalytics.getSensorEvents": {
25560
+ capName: "pipeline-analytics",
25561
+ capScope: "device",
25562
+ addonId: null,
25563
+ access: "view"
25564
+ },
25397
25565
  "pipelineAnalytics.getTrack": {
25398
25566
  capName: "pipeline-analytics",
25399
25567
  capScope: "device",
@@ -25406,6 +25574,18 @@ Object.freeze({
25406
25574
  addonId: null,
25407
25575
  access: "view"
25408
25576
  },
25577
+ "pipelineAnalytics.listEventKinds": {
25578
+ capName: "pipeline-analytics",
25579
+ capScope: "device",
25580
+ addonId: null,
25581
+ access: "view"
25582
+ },
25583
+ "pipelineAnalytics.listRecentTracks": {
25584
+ capName: "pipeline-analytics",
25585
+ capScope: "device",
25586
+ addonId: null,
25587
+ access: "view"
25588
+ },
25409
25589
  "pipelineAnalytics.listTracks": {
25410
25590
  capName: "pipeline-analytics",
25411
25591
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-wyze",
3
- "version": "0.1.20",
3
+ "version": "0.1.21",
4
4
  "description": "Wyze camera device-provider addon for CamStack — wraps the @apocaliss92/wyze-bridge-js P2P/DTLS client, feeding the stream-broker via the pull-rfc4571 lazy-publish path (a structural twin of addon-provider-reolink)",
5
5
  "keywords": [
6
6
  "camstack",