@camstack/addon-provider-rademacher 0.1.9 → 0.1.10

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
@@ -984,7 +984,7 @@ var Rademacher = class {
984
984
  }
985
985
  };
986
986
  //#endregion
987
- //#region ../types/dist/event-category-CFZs3jI4.mjs
987
+ //#region ../types/dist/event-category-H4AVePnn.mjs
988
988
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
989
989
  EventCategory["SystemBoot"] = "system.boot";
990
990
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -1134,6 +1134,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
1134
1134
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
1135
1135
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
1136
1136
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
1137
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
1138
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
1139
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
1137
1140
  EventCategory["DetectionEvent"] = "detection.event";
1138
1141
  EventCategory["SessionTrackNew"] = "session.track.new";
1139
1142
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -11781,10 +11784,23 @@ var deviceDiscoveryCapability = {
11781
11784
  }
11782
11785
  };
11783
11786
  /**
11784
- * Doorbell button cap. Installed on a `DeviceType.Button` accessory
11785
- * with `role: DeviceRole.Doorbell`. Emits an `onPressed` event every
11786
- * time the firmware pushes a ring; status tracks the last press and
11787
- * a pressCount since start (diagnostic).
11787
+ * Doorbell button cap. Two kinds of providers coexist behind this cap
11788
+ * name (same pattern as `snapshot`):
11789
+ *
11790
+ * - **Native** providers: registered per-device by device-driver
11791
+ * addons via `ctx.registerNativeCap` — either on a
11792
+ * `DeviceType.Button` accessory with `role: DeviceRole.Doorbell`,
11793
+ * or directly on the camera (Reolink registers at camera level).
11794
+ * Emits an `onPressed` event every time the firmware pushes a
11795
+ * ring; status tracks the last press and a pressCount since start
11796
+ * (diagnostic).
11797
+ *
11798
+ * - **Wrapper** provider: the `virtual-doorbell` system builtin
11799
+ * (`@camstack/system/builtins/doorbell`). Turns ANY binary-ish
11800
+ * device (contact / switch / event-emitter …) into a doorbell for
11801
+ * a camera. `defaultActive: false` — the operator explicitly binds
11802
+ * it per camera in the device-bindings UI, then picks the source
11803
+ * device + trigger in the per-device settings.
11788
11804
  *
11789
11805
  * The DeviceEventPropagator re-emits `onPressed` on the camera parent
11790
11806
  * — subscribers listening at the camera level receive ring events
@@ -11805,7 +11821,10 @@ var doorbellCapability = {
11805
11821
  scope: "device",
11806
11822
  deviceNative: true,
11807
11823
  mode: "singleton",
11808
- deviceTypes: [DeviceType.Button],
11824
+ kind: "wrapper",
11825
+ defaultActive: false,
11826
+ deviceTypes: [DeviceType.Button, DeviceType.Camera],
11827
+ exposesDeviceSettings: true,
11809
11828
  methods: {},
11810
11829
  events: {
11811
11830
  /**
@@ -18157,6 +18176,14 @@ var ConfigEntrySchema = object({
18157
18176
  value: unknown(),
18158
18177
  description: string().optional()
18159
18178
  });
18179
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
18180
+ /** One resolved linked device — the compact projection consumers need. */
18181
+ var LinkedDeviceSchema = object({
18182
+ deviceId: number(),
18183
+ name: string(),
18184
+ location: string().nullable(),
18185
+ features: array(string())
18186
+ });
18160
18187
  var SavedDeviceRowSchema = object({
18161
18188
  /** Numeric id reserved at allocateDeviceId time. */
18162
18189
  id: number(),
@@ -18378,7 +18405,10 @@ method(object({
18378
18405
  }), _void(), {
18379
18406
  kind: "mutation",
18380
18407
  auth: "admin"
18381
- }), 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({
18408
+ }), 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({
18409
+ mode: DeviceLinkModeSchema,
18410
+ devices: array(LinkedDeviceSchema)
18411
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
18382
18412
  deviceId: number(),
18383
18413
  values: record(string(), unknown())
18384
18414
  }), object({ success: literal(true) }), {
@@ -19647,6 +19677,61 @@ var EventKindSchema = _enum([
19647
19677
  "object",
19648
19678
  "audio"
19649
19679
  ]);
19680
+ /**
19681
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
19682
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
19683
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
19684
+ */
19685
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
19686
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
19687
+ var EventKindIconSchema = _enum([
19688
+ "motion",
19689
+ "audio",
19690
+ "person",
19691
+ "vehicle",
19692
+ "animal",
19693
+ "door",
19694
+ "pir",
19695
+ "smoke",
19696
+ "water",
19697
+ "button",
19698
+ "generic"
19699
+ ]);
19700
+ var EventKindCategorySchema = _enum([
19701
+ "motion",
19702
+ "audio",
19703
+ "detection",
19704
+ "sensor",
19705
+ "custom"
19706
+ ]);
19707
+ var EventKindDescriptorSchema = object({
19708
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
19709
+ kind: string(),
19710
+ label: string(),
19711
+ /** Hex color for timeline/legend rendering. */
19712
+ color: string(),
19713
+ icon: EventKindIconSchema,
19714
+ category: EventKindCategorySchema,
19715
+ /** Which cap + device contributes this kind. For built-ins the camera
19716
+ * itself; for sensor kinds the LINKED source device. */
19717
+ source: object({
19718
+ capName: string(),
19719
+ deviceId: number()
19720
+ })
19721
+ });
19722
+ var SensorEventSchema = object({
19723
+ id: string(),
19724
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
19725
+ * yields N rows, one per camera). */
19726
+ deviceId: number(),
19727
+ /** The linked sensor device whose state changed. */
19728
+ sourceDeviceId: number(),
19729
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
19730
+ kind: string(),
19731
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
19732
+ value: record(string(), unknown()).nullable(),
19733
+ timestamp: number()
19734
+ });
19650
19735
  var TrackPositionSchema = object({
19651
19736
  x: number(),
19652
19737
  y: number(),
@@ -19660,6 +19745,30 @@ var TrackSnapshotSchema = object({
19660
19745
  mediaKey: string()
19661
19746
  });
19662
19747
  /**
19748
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
19749
+ * divided by the track's detection-frame dims), computed at persist time.
19750
+ * Absent when the frame dims were unknown when the track was persisted
19751
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
19752
+ */
19753
+ var TrackEnvelopeSchema = object({
19754
+ minX: number(),
19755
+ minY: number(),
19756
+ maxX: number(),
19757
+ maxY: number()
19758
+ });
19759
+ /**
19760
+ * Row projection for track list queries. `full` (default) returns the
19761
+ * complete Track including the frame-rate `positions[]` history and the
19762
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
19763
+ * keeps every scalar the list surfaces actually render (ids, class(es),
19764
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
19765
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
19766
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
19767
+ * `getTrack`. Mirrors the event-store `projection` convention
19768
+ * (`getObjectEvents` et al.).
19769
+ */
19770
+ var TrackProjectionSchema = _enum(["full", "slim"]);
19771
+ /**
19663
19772
  * One audio-classification label heard on the track's camera while the
19664
19773
  * track was alive, aggregated per label. An "episode" is one persisted
19665
19774
  * audio event (the confident-classification path: score ≥ the device's
@@ -19710,7 +19819,11 @@ var TrackSchema = object({
19710
19819
  /** Audio-classification labels heard on the camera during the track's
19711
19820
  * life (score ≥ device `classificationMinScore`), aggregated per label.
19712
19821
  * Absent on legacy rows / tracks with no confident audio. */
19713
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
19822
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
19823
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
19824
+ * Populated from the persisted envelope columns on historical reads;
19825
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
19826
+ envelope: TrackEnvelopeSchema.optional()
19714
19827
  });
19715
19828
  var BaseEventFields = {
19716
19829
  id: string(),
@@ -19820,11 +19933,13 @@ var MediaFileSchema = object({
19820
19933
  sizeBytes: number(),
19821
19934
  timestamp: number()
19822
19935
  });
19936
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
19937
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
19823
19938
  var DeviceEventQueryInput = object({
19824
19939
  deviceId: number(),
19825
19940
  since: number().optional(),
19826
19941
  until: number().optional(),
19827
- limit: number().int().min(1).max(5e3).default(1e3),
19942
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
19828
19943
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
19829
19944
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
19830
19945
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -19832,6 +19947,27 @@ var DeviceEventQueryInput = object({
19832
19947
  projection: _enum(["full", "slim"]).optional()
19833
19948
  });
19834
19949
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
19950
+ var RecentTracksQueryInput = object({
19951
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
19952
+ deviceIds: array(number()),
19953
+ /** Window lower bound on `lastSeen` (inclusive). */
19954
+ since: number().optional(),
19955
+ /** Window upper bound on `lastSeen` (inclusive). */
19956
+ until: number().optional(),
19957
+ /** Page size. Default 200, max 1000. */
19958
+ limit: number().int().min(1).max(1e3).default(200),
19959
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
19960
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
19961
+ cursor: string().optional(),
19962
+ /** See {@link TrackProjectionSchema}. Default `full`. */
19963
+ projection: TrackProjectionSchema.optional()
19964
+ });
19965
+ var RecentTracksPageSchema = object({
19966
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
19967
+ tracks: array(TrackSchema).readonly(),
19968
+ /** Cursor for the next page, or null when this page is the last. */
19969
+ nextCursor: string().nullable()
19970
+ });
19835
19971
  var KeyEventQueryInput = object({
19836
19972
  deviceId: number(),
19837
19973
  /** Window lower bound (track firstSeen ≥ since). */
@@ -19914,11 +20050,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19914
20050
  deviceId: number(),
19915
20051
  since: number().optional(),
19916
20052
  until: number().optional(),
19917
- limit: number().optional()
19918
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
20053
+ limit: number().optional(),
20054
+ /** Spatial filter only tracks whose trajectory intersects the zone
20055
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
20056
+ * envelope columns, then precisely tested per position. Tracks with
20057
+ * an unknown envelope (no frame dims at persist time) always match. */
20058
+ zone: TrackZoneFilterSchema.optional(),
20059
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
20060
+ * compatible — omitting the field keeps today's exact behaviour). */
20061
+ projection: TrackProjectionSchema.optional()
20062
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
19919
20063
  kind: "mutation",
19920
20064
  auth: "admin"
19921
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
20065
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
20066
+ deviceId: number(),
20067
+ since: number().optional(),
20068
+ until: number().optional(),
20069
+ kinds: array(string()).optional(),
20070
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
20071
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19922
20072
  deviceId: number(),
19923
20073
  since: number(),
19924
20074
  until: number(),
@@ -23119,7 +23269,12 @@ var RecordingStorageUsageSchema = object({
23119
23269
  /**
23120
23270
  * Result of locating footage at a wall-clock instant for one device/profile.
23121
23271
  * `segment` carries the covering segment's window; `gap` reports the forward
23122
- * nearest covered edge (`null` past the end of footage / when none exists).
23272
+ * nearest covered edge (`null` past the end of footage / when none exists)
23273
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
23274
+ * footage behind the epoch; `null` when none — optional so older providers
23275
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
23276
+ * small inter-segment cracks (durMs under-covers the span to the next
23277
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
23123
23278
  */
23124
23279
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
23125
23280
  kind: literal("segment"),
@@ -23128,7 +23283,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
23128
23283
  bytes: number()
23129
23284
  }), object({
23130
23285
  kind: literal("gap"),
23131
- nearestEdgeMs: number().nullable()
23286
+ nearestEdgeMs: number().nullable(),
23287
+ prevEndMs: number().nullable().optional()
23132
23288
  })]);
23133
23289
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
23134
23290
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -24697,6 +24853,12 @@ Object.freeze({
24697
24853
  addonId: null,
24698
24854
  access: "view"
24699
24855
  },
24856
+ "deviceManager.getLinkedDevices": {
24857
+ capName: "device-manager",
24858
+ capScope: "system",
24859
+ addonId: null,
24860
+ access: "view"
24861
+ },
24700
24862
  "deviceManager.getRoleDisplayDefaults": {
24701
24863
  capName: "device-manager",
24702
24864
  capScope: "system",
@@ -26251,6 +26413,12 @@ Object.freeze({
26251
26413
  addonId: null,
26252
26414
  access: "view"
26253
26415
  },
26416
+ "pipelineAnalytics.getSensorEvents": {
26417
+ capName: "pipeline-analytics",
26418
+ capScope: "device",
26419
+ addonId: null,
26420
+ access: "view"
26421
+ },
26254
26422
  "pipelineAnalytics.getTrack": {
26255
26423
  capName: "pipeline-analytics",
26256
26424
  capScope: "device",
@@ -26263,6 +26431,18 @@ Object.freeze({
26263
26431
  addonId: null,
26264
26432
  access: "view"
26265
26433
  },
26434
+ "pipelineAnalytics.listEventKinds": {
26435
+ capName: "pipeline-analytics",
26436
+ capScope: "device",
26437
+ addonId: null,
26438
+ access: "view"
26439
+ },
26440
+ "pipelineAnalytics.listRecentTracks": {
26441
+ capName: "pipeline-analytics",
26442
+ capScope: "device",
26443
+ addonId: null,
26444
+ access: "view"
26445
+ },
26266
26446
  "pipelineAnalytics.listTracks": {
26267
26447
  capName: "pipeline-analytics",
26268
26448
  capScope: "device",
package/dist/addon.mjs CHANGED
@@ -983,7 +983,7 @@ var Rademacher = class {
983
983
  }
984
984
  };
985
985
  //#endregion
986
- //#region ../types/dist/event-category-CFZs3jI4.mjs
986
+ //#region ../types/dist/event-category-H4AVePnn.mjs
987
987
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
988
988
  EventCategory["SystemBoot"] = "system.boot";
989
989
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -1133,6 +1133,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
1133
1133
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
1134
1134
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
1135
1135
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
1136
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
1137
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
1138
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
1136
1139
  EventCategory["DetectionEvent"] = "detection.event";
1137
1140
  EventCategory["SessionTrackNew"] = "session.track.new";
1138
1141
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -11780,10 +11783,23 @@ var deviceDiscoveryCapability = {
11780
11783
  }
11781
11784
  };
11782
11785
  /**
11783
- * Doorbell button cap. Installed on a `DeviceType.Button` accessory
11784
- * with `role: DeviceRole.Doorbell`. Emits an `onPressed` event every
11785
- * time the firmware pushes a ring; status tracks the last press and
11786
- * a pressCount since start (diagnostic).
11786
+ * Doorbell button cap. Two kinds of providers coexist behind this cap
11787
+ * name (same pattern as `snapshot`):
11788
+ *
11789
+ * - **Native** providers: registered per-device by device-driver
11790
+ * addons via `ctx.registerNativeCap` — either on a
11791
+ * `DeviceType.Button` accessory with `role: DeviceRole.Doorbell`,
11792
+ * or directly on the camera (Reolink registers at camera level).
11793
+ * Emits an `onPressed` event every time the firmware pushes a
11794
+ * ring; status tracks the last press and a pressCount since start
11795
+ * (diagnostic).
11796
+ *
11797
+ * - **Wrapper** provider: the `virtual-doorbell` system builtin
11798
+ * (`@camstack/system/builtins/doorbell`). Turns ANY binary-ish
11799
+ * device (contact / switch / event-emitter …) into a doorbell for
11800
+ * a camera. `defaultActive: false` — the operator explicitly binds
11801
+ * it per camera in the device-bindings UI, then picks the source
11802
+ * device + trigger in the per-device settings.
11787
11803
  *
11788
11804
  * The DeviceEventPropagator re-emits `onPressed` on the camera parent
11789
11805
  * — subscribers listening at the camera level receive ring events
@@ -11804,7 +11820,10 @@ var doorbellCapability = {
11804
11820
  scope: "device",
11805
11821
  deviceNative: true,
11806
11822
  mode: "singleton",
11807
- deviceTypes: [DeviceType.Button],
11823
+ kind: "wrapper",
11824
+ defaultActive: false,
11825
+ deviceTypes: [DeviceType.Button, DeviceType.Camera],
11826
+ exposesDeviceSettings: true,
11808
11827
  methods: {},
11809
11828
  events: {
11810
11829
  /**
@@ -18156,6 +18175,14 @@ var ConfigEntrySchema = object({
18156
18175
  value: unknown(),
18157
18176
  description: string().optional()
18158
18177
  });
18178
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
18179
+ /** One resolved linked device — the compact projection consumers need. */
18180
+ var LinkedDeviceSchema = object({
18181
+ deviceId: number(),
18182
+ name: string(),
18183
+ location: string().nullable(),
18184
+ features: array(string())
18185
+ });
18159
18186
  var SavedDeviceRowSchema = object({
18160
18187
  /** Numeric id reserved at allocateDeviceId time. */
18161
18188
  id: number(),
@@ -18377,7 +18404,10 @@ method(object({
18377
18404
  }), _void(), {
18378
18405
  kind: "mutation",
18379
18406
  auth: "admin"
18380
- }), 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({
18407
+ }), 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({
18408
+ mode: DeviceLinkModeSchema,
18409
+ devices: array(LinkedDeviceSchema)
18410
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
18381
18411
  deviceId: number(),
18382
18412
  values: record(string(), unknown())
18383
18413
  }), object({ success: literal(true) }), {
@@ -19646,6 +19676,61 @@ var EventKindSchema = _enum([
19646
19676
  "object",
19647
19677
  "audio"
19648
19678
  ]);
19679
+ /**
19680
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
19681
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
19682
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
19683
+ */
19684
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
19685
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
19686
+ var EventKindIconSchema = _enum([
19687
+ "motion",
19688
+ "audio",
19689
+ "person",
19690
+ "vehicle",
19691
+ "animal",
19692
+ "door",
19693
+ "pir",
19694
+ "smoke",
19695
+ "water",
19696
+ "button",
19697
+ "generic"
19698
+ ]);
19699
+ var EventKindCategorySchema = _enum([
19700
+ "motion",
19701
+ "audio",
19702
+ "detection",
19703
+ "sensor",
19704
+ "custom"
19705
+ ]);
19706
+ var EventKindDescriptorSchema = object({
19707
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
19708
+ kind: string(),
19709
+ label: string(),
19710
+ /** Hex color for timeline/legend rendering. */
19711
+ color: string(),
19712
+ icon: EventKindIconSchema,
19713
+ category: EventKindCategorySchema,
19714
+ /** Which cap + device contributes this kind. For built-ins the camera
19715
+ * itself; for sensor kinds the LINKED source device. */
19716
+ source: object({
19717
+ capName: string(),
19718
+ deviceId: number()
19719
+ })
19720
+ });
19721
+ var SensorEventSchema = object({
19722
+ id: string(),
19723
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
19724
+ * yields N rows, one per camera). */
19725
+ deviceId: number(),
19726
+ /** The linked sensor device whose state changed. */
19727
+ sourceDeviceId: number(),
19728
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
19729
+ kind: string(),
19730
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
19731
+ value: record(string(), unknown()).nullable(),
19732
+ timestamp: number()
19733
+ });
19649
19734
  var TrackPositionSchema = object({
19650
19735
  x: number(),
19651
19736
  y: number(),
@@ -19659,6 +19744,30 @@ var TrackSnapshotSchema = object({
19659
19744
  mediaKey: string()
19660
19745
  });
19661
19746
  /**
19747
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
19748
+ * divided by the track's detection-frame dims), computed at persist time.
19749
+ * Absent when the frame dims were unknown when the track was persisted
19750
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
19751
+ */
19752
+ var TrackEnvelopeSchema = object({
19753
+ minX: number(),
19754
+ minY: number(),
19755
+ maxX: number(),
19756
+ maxY: number()
19757
+ });
19758
+ /**
19759
+ * Row projection for track list queries. `full` (default) returns the
19760
+ * complete Track including the frame-rate `positions[]` history and the
19761
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
19762
+ * keeps every scalar the list surfaces actually render (ids, class(es),
19763
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
19764
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
19765
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
19766
+ * `getTrack`. Mirrors the event-store `projection` convention
19767
+ * (`getObjectEvents` et al.).
19768
+ */
19769
+ var TrackProjectionSchema = _enum(["full", "slim"]);
19770
+ /**
19662
19771
  * One audio-classification label heard on the track's camera while the
19663
19772
  * track was alive, aggregated per label. An "episode" is one persisted
19664
19773
  * audio event (the confident-classification path: score ≥ the device's
@@ -19709,7 +19818,11 @@ var TrackSchema = object({
19709
19818
  /** Audio-classification labels heard on the camera during the track's
19710
19819
  * life (score ≥ device `classificationMinScore`), aggregated per label.
19711
19820
  * Absent on legacy rows / tracks with no confident audio. */
19712
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
19821
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
19822
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
19823
+ * Populated from the persisted envelope columns on historical reads;
19824
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
19825
+ envelope: TrackEnvelopeSchema.optional()
19713
19826
  });
19714
19827
  var BaseEventFields = {
19715
19828
  id: string(),
@@ -19819,11 +19932,13 @@ var MediaFileSchema = object({
19819
19932
  sizeBytes: number(),
19820
19933
  timestamp: number()
19821
19934
  });
19935
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
19936
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
19822
19937
  var DeviceEventQueryInput = object({
19823
19938
  deviceId: number(),
19824
19939
  since: number().optional(),
19825
19940
  until: number().optional(),
19826
- limit: number().int().min(1).max(5e3).default(1e3),
19941
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
19827
19942
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
19828
19943
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
19829
19944
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -19831,6 +19946,27 @@ var DeviceEventQueryInput = object({
19831
19946
  projection: _enum(["full", "slim"]).optional()
19832
19947
  });
19833
19948
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
19949
+ var RecentTracksQueryInput = object({
19950
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
19951
+ deviceIds: array(number()),
19952
+ /** Window lower bound on `lastSeen` (inclusive). */
19953
+ since: number().optional(),
19954
+ /** Window upper bound on `lastSeen` (inclusive). */
19955
+ until: number().optional(),
19956
+ /** Page size. Default 200, max 1000. */
19957
+ limit: number().int().min(1).max(1e3).default(200),
19958
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
19959
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
19960
+ cursor: string().optional(),
19961
+ /** See {@link TrackProjectionSchema}. Default `full`. */
19962
+ projection: TrackProjectionSchema.optional()
19963
+ });
19964
+ var RecentTracksPageSchema = object({
19965
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
19966
+ tracks: array(TrackSchema).readonly(),
19967
+ /** Cursor for the next page, or null when this page is the last. */
19968
+ nextCursor: string().nullable()
19969
+ });
19834
19970
  var KeyEventQueryInput = object({
19835
19971
  deviceId: number(),
19836
19972
  /** Window lower bound (track firstSeen ≥ since). */
@@ -19913,11 +20049,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19913
20049
  deviceId: number(),
19914
20050
  since: number().optional(),
19915
20051
  until: number().optional(),
19916
- limit: number().optional()
19917
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
20052
+ limit: number().optional(),
20053
+ /** Spatial filter only tracks whose trajectory intersects the zone
20054
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
20055
+ * envelope columns, then precisely tested per position. Tracks with
20056
+ * an unknown envelope (no frame dims at persist time) always match. */
20057
+ zone: TrackZoneFilterSchema.optional(),
20058
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
20059
+ * compatible — omitting the field keeps today's exact behaviour). */
20060
+ projection: TrackProjectionSchema.optional()
20061
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
19918
20062
  kind: "mutation",
19919
20063
  auth: "admin"
19920
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
20064
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
20065
+ deviceId: number(),
20066
+ since: number().optional(),
20067
+ until: number().optional(),
20068
+ kinds: array(string()).optional(),
20069
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
20070
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19921
20071
  deviceId: number(),
19922
20072
  since: number(),
19923
20073
  until: number(),
@@ -23118,7 +23268,12 @@ var RecordingStorageUsageSchema = object({
23118
23268
  /**
23119
23269
  * Result of locating footage at a wall-clock instant for one device/profile.
23120
23270
  * `segment` carries the covering segment's window; `gap` reports the forward
23121
- * nearest covered edge (`null` past the end of footage / when none exists).
23271
+ * nearest covered edge (`null` past the end of footage / when none exists)
23272
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
23273
+ * footage behind the epoch; `null` when none — optional so older providers
23274
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
23275
+ * small inter-segment cracks (durMs under-covers the span to the next
23276
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
23122
23277
  */
23123
23278
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
23124
23279
  kind: literal("segment"),
@@ -23127,7 +23282,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
23127
23282
  bytes: number()
23128
23283
  }), object({
23129
23284
  kind: literal("gap"),
23130
- nearestEdgeMs: number().nullable()
23285
+ nearestEdgeMs: number().nullable(),
23286
+ prevEndMs: number().nullable().optional()
23131
23287
  })]);
23132
23288
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
23133
23289
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -24696,6 +24852,12 @@ Object.freeze({
24696
24852
  addonId: null,
24697
24853
  access: "view"
24698
24854
  },
24855
+ "deviceManager.getLinkedDevices": {
24856
+ capName: "device-manager",
24857
+ capScope: "system",
24858
+ addonId: null,
24859
+ access: "view"
24860
+ },
24699
24861
  "deviceManager.getRoleDisplayDefaults": {
24700
24862
  capName: "device-manager",
24701
24863
  capScope: "system",
@@ -26250,6 +26412,12 @@ Object.freeze({
26250
26412
  addonId: null,
26251
26413
  access: "view"
26252
26414
  },
26415
+ "pipelineAnalytics.getSensorEvents": {
26416
+ capName: "pipeline-analytics",
26417
+ capScope: "device",
26418
+ addonId: null,
26419
+ access: "view"
26420
+ },
26253
26421
  "pipelineAnalytics.getTrack": {
26254
26422
  capName: "pipeline-analytics",
26255
26423
  capScope: "device",
@@ -26262,6 +26430,18 @@ Object.freeze({
26262
26430
  addonId: null,
26263
26431
  access: "view"
26264
26432
  },
26433
+ "pipelineAnalytics.listEventKinds": {
26434
+ capName: "pipeline-analytics",
26435
+ capScope: "device",
26436
+ addonId: null,
26437
+ access: "view"
26438
+ },
26439
+ "pipelineAnalytics.listRecentTracks": {
26440
+ capName: "pipeline-analytics",
26441
+ capScope: "device",
26442
+ addonId: null,
26443
+ access: "view"
26444
+ },
26265
26445
  "pipelineAnalytics.listTracks": {
26266
26446
  capName: "pipeline-analytics",
26267
26447
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rademacher",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Rademacher HomePilot device-provider addon for CamStack — wraps the @apocaliss92/noderademacher local-hub client (roller shutters over the cover cap)",
5
5
  "keywords": [
6
6
  "camstack",