@camstack/addon-export-hap 1.1.23 → 1.1.25

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.
@@ -35,7 +35,7 @@ let node_fs_promises = require("node:fs/promises");
35
35
  node_fs_promises = __toESM(node_fs_promises);
36
36
  let node_dgram = require("node:dgram");
37
37
  let node_os = require("node:os");
38
- //#region ../types/dist/event-category-CFZs3jI4.mjs
38
+ //#region ../types/dist/event-category-H4AVePnn.mjs
39
39
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
40
40
  EventCategory["SystemBoot"] = "system.boot";
41
41
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -185,6 +185,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
185
185
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
186
186
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
187
187
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
188
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
189
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
190
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
188
191
  EventCategory["DetectionEvent"] = "detection.event";
189
192
  EventCategory["SessionTrackNew"] = "session.track.new";
190
193
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -7254,6 +7257,24 @@ var RecordingRetentionSchema = object({
7254
7257
  maxSizeGb: number().min(0).optional()
7255
7258
  });
7256
7259
  /**
7260
+ * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7261
+ * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7262
+ * previews at. Five graduated steps; absent on a config = `standard` (the
7263
+ * shipped default, matching `sheet-geometry`/`sheet-composer`).
7264
+ *
7265
+ * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7266
+ * Each window's index sidecar carries its own tile dims, so a camera whose
7267
+ * preset changed over time renders every historical window at the dims it was
7268
+ * written with.
7269
+ */
7270
+ var ScrubThumbnailPresetSchema = _enum([
7271
+ "minimal",
7272
+ "low",
7273
+ "standard",
7274
+ "high",
7275
+ "max"
7276
+ ]);
7277
+ /**
7257
7278
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7258
7279
  *
7259
7280
  * `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
@@ -7290,7 +7311,14 @@ var RecordingConfigSchema = object({
7290
7311
  * derived into bands once via `migrateConfigToBands`.
7291
7312
  */
7292
7313
  bands: array(RecordingBandSchema).optional(),
7293
- retention: RecordingRetentionSchema.optional()
7314
+ retention: RecordingRetentionSchema.optional(),
7315
+ /**
7316
+ * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7317
+ * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7318
+ * windows only — existing sheets are immutable, and each window's index
7319
+ * carries its own tile dims so mixed-preset history renders correctly.
7320
+ */
7321
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7294
7322
  });
7295
7323
  /**
7296
7324
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -9903,7 +9931,7 @@ object({
9903
9931
  deviceId: number(),
9904
9932
  timestamp: number()
9905
9933
  });
9906
- DeviceType.Button;
9934
+ DeviceType.Button, DeviceType.Camera;
9907
9935
  /**
9908
9936
  * Enum-state sensor — a string value picked from a finite option set.
9909
9937
  * Drives HA `sensor` entries with `state_class: enum` (HVAC action
@@ -14298,6 +14326,14 @@ var ConfigEntrySchema = object({
14298
14326
  value: unknown(),
14299
14327
  description: string().optional()
14300
14328
  });
14329
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
14330
+ /** One resolved linked device — the compact projection consumers need. */
14331
+ var LinkedDeviceSchema = object({
14332
+ deviceId: number(),
14333
+ name: string(),
14334
+ location: string().nullable(),
14335
+ features: array(string())
14336
+ });
14301
14337
  var SavedDeviceRowSchema = object({
14302
14338
  /** Numeric id reserved at allocateDeviceId time. */
14303
14339
  id: number(),
@@ -14519,7 +14555,10 @@ method(object({
14519
14555
  }), _void(), {
14520
14556
  kind: "mutation",
14521
14557
  auth: "admin"
14522
- }), 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({
14558
+ }), 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({
14559
+ mode: DeviceLinkModeSchema,
14560
+ devices: array(LinkedDeviceSchema)
14561
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
14523
14562
  deviceId: number(),
14524
14563
  values: record(string(), unknown())
14525
14564
  }), object({ success: literal(true) }), {
@@ -15788,6 +15827,61 @@ var EventKindSchema = _enum([
15788
15827
  "object",
15789
15828
  "audio"
15790
15829
  ]);
15830
+ /**
15831
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
15832
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
15833
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
15834
+ */
15835
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
15836
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
15837
+ var EventKindIconSchema = _enum([
15838
+ "motion",
15839
+ "audio",
15840
+ "person",
15841
+ "vehicle",
15842
+ "animal",
15843
+ "door",
15844
+ "pir",
15845
+ "smoke",
15846
+ "water",
15847
+ "button",
15848
+ "generic"
15849
+ ]);
15850
+ var EventKindCategorySchema = _enum([
15851
+ "motion",
15852
+ "audio",
15853
+ "detection",
15854
+ "sensor",
15855
+ "custom"
15856
+ ]);
15857
+ var EventKindDescriptorSchema = object({
15858
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
15859
+ kind: string(),
15860
+ label: string(),
15861
+ /** Hex color for timeline/legend rendering. */
15862
+ color: string(),
15863
+ icon: EventKindIconSchema,
15864
+ category: EventKindCategorySchema,
15865
+ /** Which cap + device contributes this kind. For built-ins the camera
15866
+ * itself; for sensor kinds the LINKED source device. */
15867
+ source: object({
15868
+ capName: string(),
15869
+ deviceId: number()
15870
+ })
15871
+ });
15872
+ var SensorEventSchema = object({
15873
+ id: string(),
15874
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
15875
+ * yields N rows, one per camera). */
15876
+ deviceId: number(),
15877
+ /** The linked sensor device whose state changed. */
15878
+ sourceDeviceId: number(),
15879
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
15880
+ kind: string(),
15881
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
15882
+ value: record(string(), unknown()).nullable(),
15883
+ timestamp: number()
15884
+ });
15791
15885
  var TrackPositionSchema = object({
15792
15886
  x: number(),
15793
15887
  y: number(),
@@ -15801,6 +15895,30 @@ var TrackSnapshotSchema = object({
15801
15895
  mediaKey: string()
15802
15896
  });
15803
15897
  /**
15898
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
15899
+ * divided by the track's detection-frame dims), computed at persist time.
15900
+ * Absent when the frame dims were unknown when the track was persisted
15901
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
15902
+ */
15903
+ var TrackEnvelopeSchema = object({
15904
+ minX: number(),
15905
+ minY: number(),
15906
+ maxX: number(),
15907
+ maxY: number()
15908
+ });
15909
+ /**
15910
+ * Row projection for track list queries. `full` (default) returns the
15911
+ * complete Track including the frame-rate `positions[]` history and the
15912
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
15913
+ * keeps every scalar the list surfaces actually render (ids, class(es),
15914
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
15915
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
15916
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
15917
+ * `getTrack`. Mirrors the event-store `projection` convention
15918
+ * (`getObjectEvents` et al.).
15919
+ */
15920
+ var TrackProjectionSchema = _enum(["full", "slim"]);
15921
+ /**
15804
15922
  * One audio-classification label heard on the track's camera while the
15805
15923
  * track was alive, aggregated per label. An "episode" is one persisted
15806
15924
  * audio event (the confident-classification path: score ≥ the device's
@@ -15851,7 +15969,11 @@ var TrackSchema = object({
15851
15969
  /** Audio-classification labels heard on the camera during the track's
15852
15970
  * life (score ≥ device `classificationMinScore`), aggregated per label.
15853
15971
  * Absent on legacy rows / tracks with no confident audio. */
15854
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
15972
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
15973
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
15974
+ * Populated from the persisted envelope columns on historical reads;
15975
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
15976
+ envelope: TrackEnvelopeSchema.optional()
15855
15977
  });
15856
15978
  var BaseEventFields = {
15857
15979
  id: string(),
@@ -15961,11 +16083,13 @@ var MediaFileSchema = object({
15961
16083
  sizeBytes: number(),
15962
16084
  timestamp: number()
15963
16085
  });
16086
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
16087
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
15964
16088
  var DeviceEventQueryInput = object({
15965
16089
  deviceId: number(),
15966
16090
  since: number().optional(),
15967
16091
  until: number().optional(),
15968
- limit: number().int().min(1).max(5e3).default(1e3),
16092
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
15969
16093
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
15970
16094
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
15971
16095
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -15973,6 +16097,27 @@ var DeviceEventQueryInput = object({
15973
16097
  projection: _enum(["full", "slim"]).optional()
15974
16098
  });
15975
16099
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
16100
+ var RecentTracksQueryInput = object({
16101
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
16102
+ deviceIds: array(number()),
16103
+ /** Window lower bound on `lastSeen` (inclusive). */
16104
+ since: number().optional(),
16105
+ /** Window upper bound on `lastSeen` (inclusive). */
16106
+ until: number().optional(),
16107
+ /** Page size. Default 200, max 1000. */
16108
+ limit: number().int().min(1).max(1e3).default(200),
16109
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
16110
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
16111
+ cursor: string().optional(),
16112
+ /** See {@link TrackProjectionSchema}. Default `full`. */
16113
+ projection: TrackProjectionSchema.optional()
16114
+ });
16115
+ var RecentTracksPageSchema = object({
16116
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
16117
+ tracks: array(TrackSchema).readonly(),
16118
+ /** Cursor for the next page, or null when this page is the last. */
16119
+ nextCursor: string().nullable()
16120
+ });
15976
16121
  var KeyEventQueryInput = object({
15977
16122
  deviceId: number(),
15978
16123
  /** Window lower bound (track firstSeen ≥ since). */
@@ -16055,11 +16200,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16055
16200
  deviceId: number(),
16056
16201
  since: number().optional(),
16057
16202
  until: number().optional(),
16058
- limit: number().optional()
16059
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
16060
- kind: "mutation",
16061
- auth: "admin"
16062
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16203
+ limit: number().optional(),
16204
+ /** Spatial filter only tracks whose trajectory intersects the zone
16205
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
16206
+ * envelope columns, then precisely tested per position. Tracks with
16207
+ * an unknown envelope (no frame dims at persist time) always match. */
16208
+ zone: TrackZoneFilterSchema.optional(),
16209
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
16210
+ * compatible — omitting the field keeps today's exact behaviour). */
16211
+ projection: TrackProjectionSchema.optional()
16212
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
16213
+ kind: "mutation",
16214
+ auth: "admin"
16215
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
16216
+ deviceId: number(),
16217
+ since: number().optional(),
16218
+ until: number().optional(),
16219
+ kinds: array(string()).optional(),
16220
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
16221
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16063
16222
  deviceId: number(),
16064
16223
  since: number(),
16065
16224
  until: number(),
@@ -19260,7 +19419,12 @@ var RecordingStorageUsageSchema = object({
19260
19419
  /**
19261
19420
  * Result of locating footage at a wall-clock instant for one device/profile.
19262
19421
  * `segment` carries the covering segment's window; `gap` reports the forward
19263
- * nearest covered edge (`null` past the end of footage / when none exists).
19422
+ * nearest covered edge (`null` past the end of footage / when none exists)
19423
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
19424
+ * footage behind the epoch; `null` when none — optional so older providers
19425
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
19426
+ * small inter-segment cracks (durMs under-covers the span to the next
19427
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
19264
19428
  */
19265
19429
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19266
19430
  kind: literal("segment"),
@@ -19269,7 +19433,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19269
19433
  bytes: number()
19270
19434
  }), object({
19271
19435
  kind: literal("gap"),
19272
- nearestEdgeMs: number().nullable()
19436
+ nearestEdgeMs: number().nullable(),
19437
+ prevEndMs: number().nullable().optional()
19273
19438
  })]);
19274
19439
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
19275
19440
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -20838,6 +21003,12 @@ Object.freeze({
20838
21003
  addonId: null,
20839
21004
  access: "view"
20840
21005
  },
21006
+ "deviceManager.getLinkedDevices": {
21007
+ capName: "device-manager",
21008
+ capScope: "system",
21009
+ addonId: null,
21010
+ access: "view"
21011
+ },
20841
21012
  "deviceManager.getRoleDisplayDefaults": {
20842
21013
  capName: "device-manager",
20843
21014
  capScope: "system",
@@ -22392,6 +22563,12 @@ Object.freeze({
22392
22563
  addonId: null,
22393
22564
  access: "view"
22394
22565
  },
22566
+ "pipelineAnalytics.getSensorEvents": {
22567
+ capName: "pipeline-analytics",
22568
+ capScope: "device",
22569
+ addonId: null,
22570
+ access: "view"
22571
+ },
22395
22572
  "pipelineAnalytics.getTrack": {
22396
22573
  capName: "pipeline-analytics",
22397
22574
  capScope: "device",
@@ -22404,6 +22581,18 @@ Object.freeze({
22404
22581
  addonId: null,
22405
22582
  access: "view"
22406
22583
  },
22584
+ "pipelineAnalytics.listEventKinds": {
22585
+ capName: "pipeline-analytics",
22586
+ capScope: "device",
22587
+ addonId: null,
22588
+ access: "view"
22589
+ },
22590
+ "pipelineAnalytics.listRecentTracks": {
22591
+ capName: "pipeline-analytics",
22592
+ capScope: "device",
22593
+ addonId: null,
22594
+ access: "view"
22595
+ },
22407
22596
  "pipelineAnalytics.listTracks": {
22408
22597
  capName: "pipeline-analytics",
22409
22598
  capScope: "device",
@@ -10,7 +10,7 @@ import { networkInterfaces } from "node:os";
10
10
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
11
11
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
12
12
  //#endregion
13
- //#region ../types/dist/event-category-CFZs3jI4.mjs
13
+ //#region ../types/dist/event-category-H4AVePnn.mjs
14
14
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
15
15
  EventCategory["SystemBoot"] = "system.boot";
16
16
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -160,6 +160,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
160
160
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
161
161
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
162
162
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
163
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
164
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
165
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
163
166
  EventCategory["DetectionEvent"] = "detection.event";
164
167
  EventCategory["SessionTrackNew"] = "session.track.new";
165
168
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -7229,6 +7232,24 @@ var RecordingRetentionSchema = object({
7229
7232
  maxSizeGb: number().min(0).optional()
7230
7233
  });
7231
7234
  /**
7235
+ * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7236
+ * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7237
+ * previews at. Five graduated steps; absent on a config = `standard` (the
7238
+ * shipped default, matching `sheet-geometry`/`sheet-composer`).
7239
+ *
7240
+ * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7241
+ * Each window's index sidecar carries its own tile dims, so a camera whose
7242
+ * preset changed over time renders every historical window at the dims it was
7243
+ * written with.
7244
+ */
7245
+ var ScrubThumbnailPresetSchema = _enum([
7246
+ "minimal",
7247
+ "low",
7248
+ "standard",
7249
+ "high",
7250
+ "max"
7251
+ ]);
7252
+ /**
7232
7253
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7233
7254
  *
7234
7255
  * `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
@@ -7265,7 +7286,14 @@ var RecordingConfigSchema = object({
7265
7286
  * derived into bands once via `migrateConfigToBands`.
7266
7287
  */
7267
7288
  bands: array(RecordingBandSchema).optional(),
7268
- retention: RecordingRetentionSchema.optional()
7289
+ retention: RecordingRetentionSchema.optional(),
7290
+ /**
7291
+ * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7292
+ * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7293
+ * windows only — existing sheets are immutable, and each window's index
7294
+ * carries its own tile dims so mixed-preset history renders correctly.
7295
+ */
7296
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7269
7297
  });
7270
7298
  /**
7271
7299
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -9878,7 +9906,7 @@ object({
9878
9906
  deviceId: number(),
9879
9907
  timestamp: number()
9880
9908
  });
9881
- DeviceType.Button;
9909
+ DeviceType.Button, DeviceType.Camera;
9882
9910
  /**
9883
9911
  * Enum-state sensor — a string value picked from a finite option set.
9884
9912
  * Drives HA `sensor` entries with `state_class: enum` (HVAC action
@@ -14273,6 +14301,14 @@ var ConfigEntrySchema = object({
14273
14301
  value: unknown(),
14274
14302
  description: string().optional()
14275
14303
  });
14304
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
14305
+ /** One resolved linked device — the compact projection consumers need. */
14306
+ var LinkedDeviceSchema = object({
14307
+ deviceId: number(),
14308
+ name: string(),
14309
+ location: string().nullable(),
14310
+ features: array(string())
14311
+ });
14276
14312
  var SavedDeviceRowSchema = object({
14277
14313
  /** Numeric id reserved at allocateDeviceId time. */
14278
14314
  id: number(),
@@ -14494,7 +14530,10 @@ method(object({
14494
14530
  }), _void(), {
14495
14531
  kind: "mutation",
14496
14532
  auth: "admin"
14497
- }), 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({
14533
+ }), 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({
14534
+ mode: DeviceLinkModeSchema,
14535
+ devices: array(LinkedDeviceSchema)
14536
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
14498
14537
  deviceId: number(),
14499
14538
  values: record(string(), unknown())
14500
14539
  }), object({ success: literal(true) }), {
@@ -15763,6 +15802,61 @@ var EventKindSchema = _enum([
15763
15802
  "object",
15764
15803
  "audio"
15765
15804
  ]);
15805
+ /**
15806
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
15807
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
15808
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
15809
+ */
15810
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
15811
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
15812
+ var EventKindIconSchema = _enum([
15813
+ "motion",
15814
+ "audio",
15815
+ "person",
15816
+ "vehicle",
15817
+ "animal",
15818
+ "door",
15819
+ "pir",
15820
+ "smoke",
15821
+ "water",
15822
+ "button",
15823
+ "generic"
15824
+ ]);
15825
+ var EventKindCategorySchema = _enum([
15826
+ "motion",
15827
+ "audio",
15828
+ "detection",
15829
+ "sensor",
15830
+ "custom"
15831
+ ]);
15832
+ var EventKindDescriptorSchema = object({
15833
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
15834
+ kind: string(),
15835
+ label: string(),
15836
+ /** Hex color for timeline/legend rendering. */
15837
+ color: string(),
15838
+ icon: EventKindIconSchema,
15839
+ category: EventKindCategorySchema,
15840
+ /** Which cap + device contributes this kind. For built-ins the camera
15841
+ * itself; for sensor kinds the LINKED source device. */
15842
+ source: object({
15843
+ capName: string(),
15844
+ deviceId: number()
15845
+ })
15846
+ });
15847
+ var SensorEventSchema = object({
15848
+ id: string(),
15849
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
15850
+ * yields N rows, one per camera). */
15851
+ deviceId: number(),
15852
+ /** The linked sensor device whose state changed. */
15853
+ sourceDeviceId: number(),
15854
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
15855
+ kind: string(),
15856
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
15857
+ value: record(string(), unknown()).nullable(),
15858
+ timestamp: number()
15859
+ });
15766
15860
  var TrackPositionSchema = object({
15767
15861
  x: number(),
15768
15862
  y: number(),
@@ -15776,6 +15870,30 @@ var TrackSnapshotSchema = object({
15776
15870
  mediaKey: string()
15777
15871
  });
15778
15872
  /**
15873
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
15874
+ * divided by the track's detection-frame dims), computed at persist time.
15875
+ * Absent when the frame dims were unknown when the track was persisted
15876
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
15877
+ */
15878
+ var TrackEnvelopeSchema = object({
15879
+ minX: number(),
15880
+ minY: number(),
15881
+ maxX: number(),
15882
+ maxY: number()
15883
+ });
15884
+ /**
15885
+ * Row projection for track list queries. `full` (default) returns the
15886
+ * complete Track including the frame-rate `positions[]` history and the
15887
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
15888
+ * keeps every scalar the list surfaces actually render (ids, class(es),
15889
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
15890
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
15891
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
15892
+ * `getTrack`. Mirrors the event-store `projection` convention
15893
+ * (`getObjectEvents` et al.).
15894
+ */
15895
+ var TrackProjectionSchema = _enum(["full", "slim"]);
15896
+ /**
15779
15897
  * One audio-classification label heard on the track's camera while the
15780
15898
  * track was alive, aggregated per label. An "episode" is one persisted
15781
15899
  * audio event (the confident-classification path: score ≥ the device's
@@ -15826,7 +15944,11 @@ var TrackSchema = object({
15826
15944
  /** Audio-classification labels heard on the camera during the track's
15827
15945
  * life (score ≥ device `classificationMinScore`), aggregated per label.
15828
15946
  * Absent on legacy rows / tracks with no confident audio. */
15829
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
15947
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
15948
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
15949
+ * Populated from the persisted envelope columns on historical reads;
15950
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
15951
+ envelope: TrackEnvelopeSchema.optional()
15830
15952
  });
15831
15953
  var BaseEventFields = {
15832
15954
  id: string(),
@@ -15936,11 +16058,13 @@ var MediaFileSchema = object({
15936
16058
  sizeBytes: number(),
15937
16059
  timestamp: number()
15938
16060
  });
16061
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
16062
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
15939
16063
  var DeviceEventQueryInput = object({
15940
16064
  deviceId: number(),
15941
16065
  since: number().optional(),
15942
16066
  until: number().optional(),
15943
- limit: number().int().min(1).max(5e3).default(1e3),
16067
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
15944
16068
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
15945
16069
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
15946
16070
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -15948,6 +16072,27 @@ var DeviceEventQueryInput = object({
15948
16072
  projection: _enum(["full", "slim"]).optional()
15949
16073
  });
15950
16074
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
16075
+ var RecentTracksQueryInput = object({
16076
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
16077
+ deviceIds: array(number()),
16078
+ /** Window lower bound on `lastSeen` (inclusive). */
16079
+ since: number().optional(),
16080
+ /** Window upper bound on `lastSeen` (inclusive). */
16081
+ until: number().optional(),
16082
+ /** Page size. Default 200, max 1000. */
16083
+ limit: number().int().min(1).max(1e3).default(200),
16084
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
16085
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
16086
+ cursor: string().optional(),
16087
+ /** See {@link TrackProjectionSchema}. Default `full`. */
16088
+ projection: TrackProjectionSchema.optional()
16089
+ });
16090
+ var RecentTracksPageSchema = object({
16091
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
16092
+ tracks: array(TrackSchema).readonly(),
16093
+ /** Cursor for the next page, or null when this page is the last. */
16094
+ nextCursor: string().nullable()
16095
+ });
15951
16096
  var KeyEventQueryInput = object({
15952
16097
  deviceId: number(),
15953
16098
  /** Window lower bound (track firstSeen ≥ since). */
@@ -16030,11 +16175,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16030
16175
  deviceId: number(),
16031
16176
  since: number().optional(),
16032
16177
  until: number().optional(),
16033
- limit: number().optional()
16034
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
16035
- kind: "mutation",
16036
- auth: "admin"
16037
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16178
+ limit: number().optional(),
16179
+ /** Spatial filter only tracks whose trajectory intersects the zone
16180
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
16181
+ * envelope columns, then precisely tested per position. Tracks with
16182
+ * an unknown envelope (no frame dims at persist time) always match. */
16183
+ zone: TrackZoneFilterSchema.optional(),
16184
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
16185
+ * compatible — omitting the field keeps today's exact behaviour). */
16186
+ projection: TrackProjectionSchema.optional()
16187
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
16188
+ kind: "mutation",
16189
+ auth: "admin"
16190
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
16191
+ deviceId: number(),
16192
+ since: number().optional(),
16193
+ until: number().optional(),
16194
+ kinds: array(string()).optional(),
16195
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
16196
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16038
16197
  deviceId: number(),
16039
16198
  since: number(),
16040
16199
  until: number(),
@@ -19235,7 +19394,12 @@ var RecordingStorageUsageSchema = object({
19235
19394
  /**
19236
19395
  * Result of locating footage at a wall-clock instant for one device/profile.
19237
19396
  * `segment` carries the covering segment's window; `gap` reports the forward
19238
- * nearest covered edge (`null` past the end of footage / when none exists).
19397
+ * nearest covered edge (`null` past the end of footage / when none exists)
19398
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
19399
+ * footage behind the epoch; `null` when none — optional so older providers
19400
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
19401
+ * small inter-segment cracks (durMs under-covers the span to the next
19402
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
19239
19403
  */
19240
19404
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19241
19405
  kind: literal("segment"),
@@ -19244,7 +19408,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19244
19408
  bytes: number()
19245
19409
  }), object({
19246
19410
  kind: literal("gap"),
19247
- nearestEdgeMs: number().nullable()
19411
+ nearestEdgeMs: number().nullable(),
19412
+ prevEndMs: number().nullable().optional()
19248
19413
  })]);
19249
19414
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
19250
19415
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -20813,6 +20978,12 @@ Object.freeze({
20813
20978
  addonId: null,
20814
20979
  access: "view"
20815
20980
  },
20981
+ "deviceManager.getLinkedDevices": {
20982
+ capName: "device-manager",
20983
+ capScope: "system",
20984
+ addonId: null,
20985
+ access: "view"
20986
+ },
20816
20987
  "deviceManager.getRoleDisplayDefaults": {
20817
20988
  capName: "device-manager",
20818
20989
  capScope: "system",
@@ -22367,6 +22538,12 @@ Object.freeze({
22367
22538
  addonId: null,
22368
22539
  access: "view"
22369
22540
  },
22541
+ "pipelineAnalytics.getSensorEvents": {
22542
+ capName: "pipeline-analytics",
22543
+ capScope: "device",
22544
+ addonId: null,
22545
+ access: "view"
22546
+ },
22370
22547
  "pipelineAnalytics.getTrack": {
22371
22548
  capName: "pipeline-analytics",
22372
22549
  capScope: "device",
@@ -22379,6 +22556,18 @@ Object.freeze({
22379
22556
  addonId: null,
22380
22557
  access: "view"
22381
22558
  },
22559
+ "pipelineAnalytics.listEventKinds": {
22560
+ capName: "pipeline-analytics",
22561
+ capScope: "device",
22562
+ addonId: null,
22563
+ access: "view"
22564
+ },
22565
+ "pipelineAnalytics.listRecentTracks": {
22566
+ capName: "pipeline-analytics",
22567
+ capScope: "device",
22568
+ addonId: null,
22569
+ access: "view"
22570
+ },
22382
22571
  "pipelineAnalytics.listTracks": {
22383
22572
  capName: "pipeline-analytics",
22384
22573
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-export-hap",
3
- "version": "1.1.23",
3
+ "version": "1.1.25",
4
4
  "description": "HomeKit (HAP) bridge exporter for CamStack devices. Publishes a bridged accessory per exposed device — MotionSensor in this MVP; Camera/Doorbell/Switch/Light follow.",
5
5
  "keywords": [
6
6
  "camstack",