@camstack/addon-provider-homeassistant 1.2.61 → 1.2.63

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.
@@ -8221,6 +8221,20 @@ var RelocateJobSchema = object({
8221
8221
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8222
8222
  */
8223
8223
  rowsReconciled: number().int().nonnegative().optional(),
8224
+ /**
8225
+ * Rows this run FORGOT because the file they name is not on disk.
8226
+ *
8227
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8228
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8229
+ * and the durable row is dropped through the same channel eviction uses. It
8230
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8231
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8232
+ * the same failure as one that quietly skips them (D295).
8233
+ *
8234
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8235
+ * ledger claimed 5.65 GB of footage that no longer existed.
8236
+ */
8237
+ rowsForgotten: number().int().nonnegative().optional(),
8224
8238
  startedAt: number(),
8225
8239
  finishedAt: number().nullable(),
8226
8240
  error: string().nullable()
@@ -8285,6 +8299,13 @@ var MediaRelocateModeSchema = _enum([
8285
8299
  ]);
8286
8300
  var RelocateMediaInputSchema = object({
8287
8301
  toLocationId: string(),
8302
+ /**
8303
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8304
+ * every row that is not already on `toLocationId` (the historical
8305
+ * behaviour). A named source is what a from→to migration needs: without it
8306
+ * "move events off disk 2" also emptied disk 1.
8307
+ */
8308
+ fromLocationId: string().optional(),
8288
8309
  throttleMbps: number().min(1).max(1e3).optional(),
8289
8310
  /** Omitted = `move`, the pre-existing behaviour. */
8290
8311
  mode: MediaRelocateModeSchema.optional()
@@ -8352,6 +8373,19 @@ var StorageMigrationDestinationsSchema = object({
8352
8373
  galleryMedia: string().min(1).optional()
8353
8374
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8354
8375
  /**
8376
+ * Optional named source per class. Omitted = the class's current default
8377
+ * (the historical behaviour). A named source that is NOT the default is a
8378
+ * drain of that disk: bytes move, the default stays, and the source is
8379
+ * disabled when the move finishes.
8380
+ */
8381
+ var StorageMigrationSourcesSchema = object({
8382
+ recordings: string().min(1).optional(),
8383
+ recordingsLow: string().min(1).optional(),
8384
+ eventMedia: string().min(1).optional(),
8385
+ backups: string().min(1).optional(),
8386
+ galleryMedia: string().min(1).optional()
8387
+ }).optional();
8388
+ /**
8355
8389
  * How a migration sequences the cutover against the byte move.
8356
8390
  *
8357
8391
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8373,6 +8407,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8373
8407
  /** Shared input for planning and starting an orchestrated storage migration. */
8374
8408
  var StorageMigrationInputSchema = object({
8375
8409
  destinations: StorageMigrationDestinationsSchema,
8410
+ /** Omitted = each class's current default. */
8411
+ sources: StorageMigrationSourcesSchema,
8376
8412
  throttleMbps: number().min(1).max(1e3).optional(),
8377
8413
  /** Omitted = `blocking`, which stays the default. */
8378
8414
  mode: StorageMigrationModeSchema.optional()
@@ -8452,6 +8488,13 @@ var StorageMigrationMoveSchema = object({
8452
8488
  storageClass: StorageMigrationClassSchema,
8453
8489
  fromLocationId: string(),
8454
8490
  toLocationId: string(),
8491
+ /**
8492
+ * True when `from` was NOT the class default at plan time. The move still
8493
+ * copies bytes, but the default is left alone and the source is disabled
8494
+ * once the copy verifies. Absent on jobs planned before this field existed
8495
+ * — those jobs always repointed, which is `false`.
8496
+ */
8497
+ freezeSource: boolean().optional(),
8455
8498
  moverJobId: string().nullable(),
8456
8499
  state: RelocateJobStateSchema.nullable(),
8457
8500
  error: string().nullable(),
@@ -8465,6 +8508,7 @@ var StorageMigrationJobSchema = object({
8465
8508
  * can tell a seconds-long cutover from a thirty-hour one. */
8466
8509
  mode: StorageMigrationModeSchema,
8467
8510
  destinations: StorageMigrationDestinationsSchema,
8511
+ sources: StorageMigrationSourcesSchema,
8468
8512
  throttleMbps: number(),
8469
8513
  moves: array(StorageMigrationMoveSchema),
8470
8514
  pauseLeaseId: string().nullable(),
@@ -8490,6 +8534,7 @@ var StorageMigrationFindingSchema = object({
8490
8534
  });
8491
8535
  var StorageMigrationPlanSchema = object({
8492
8536
  destinations: StorageMigrationDestinationsSchema,
8537
+ sources: StorageMigrationSourcesSchema,
8493
8538
  /** The mode this plan was built for. A plan is only valid for its mode: the
8494
8539
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
8495
8540
  * it. */
@@ -8497,7 +8542,8 @@ var StorageMigrationPlanSchema = object({
8497
8542
  moves: array(object({
8498
8543
  storageClass: StorageMigrationClassSchema,
8499
8544
  fromLocationId: string(),
8500
- toLocationId: string()
8545
+ toLocationId: string(),
8546
+ freezeSource: boolean().optional()
8501
8547
  })),
8502
8548
  findings: array(StorageMigrationFindingSchema)
8503
8549
  });
@@ -8584,16 +8630,142 @@ var RelocateResidueSchema = object({
8584
8630
  segments: number().int().nonnegative(),
8585
8631
  bytes: number().int().nonnegative()
8586
8632
  }).nullable();
8633
+ /**
8634
+ * Ask one location whether its durable hour rows describe the disk — the walk
8635
+ * (D319).
8636
+ *
8637
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8638
+ * missing tool is the question, and the dry run is how they sanity-check the
8639
+ * destructive run before authorising it.
8640
+ */
8641
+ var LedgerWalkInputSchema = object({
8642
+ locationId: string().min(1),
8643
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8644
+ apply: boolean().optional(),
8645
+ /** Narrow to one camera. */
8646
+ deviceId: number().int().positive().optional(),
8647
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8648
+ profiles: array(string().min(1)).optional()
8649
+ });
8650
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8651
+ var LedgerWalkRefusalSchema = _enum([
8652
+ "location-unknown",
8653
+ "source-writable",
8654
+ "no-ledger",
8655
+ "archive-unreadable",
8656
+ "anchor-absent",
8657
+ "anchor-unreadable",
8658
+ "anchor-moved"
8659
+ ]);
8660
+ _enum([
8661
+ "live-tail",
8662
+ "listing-error",
8663
+ "path-mismatch",
8664
+ "durable-refused"
8665
+ ]);
8666
+ /** Every skip reason, always present, always a number — so a reason that never
8667
+ * fired reports as zero rather than absent and the report shape is constant
8668
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8669
+ var LedgerWalkSkipCountsSchema = object({
8670
+ "live-tail": number().int().nonnegative(),
8671
+ "listing-error": number().int().nonnegative(),
8672
+ "path-mismatch": number().int().nonnegative(),
8673
+ "durable-refused": number().int().nonnegative()
8674
+ });
8675
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8676
+ var LedgerWalkDeviceReportSchema = object({
8677
+ deviceId: number().int(),
8678
+ hoursWalked: number().int().nonnegative(),
8679
+ hoursMissing: number().int().nonnegative(),
8680
+ ghostSegments: number().int().nonnegative(),
8681
+ ghostBytes: number().int().nonnegative(),
8682
+ forgottenSegments: number().int().nonnegative(),
8683
+ orphanFiles: number().int().nonnegative()
8684
+ });
8685
+ /**
8686
+ * What one walk claimed, listed, found and (only when armed) forgot.
8687
+ *
8688
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8689
+ * walk that saw a fraction of the location is visible in its own report rather
8690
+ * than in the absence of one.
8691
+ */
8692
+ var LedgerWalkReportSchema = object({
8693
+ locationId: string(),
8694
+ applied: boolean(),
8695
+ refused: LedgerWalkRefusalSchema.nullable(),
8696
+ archiveSegments: number().int().nonnegative().nullable(),
8697
+ archiveBytes: number().int().nonnegative().nullable(),
8698
+ hoursClaimed: number().int().nonnegative(),
8699
+ hoursWalked: number().int().nonnegative(),
8700
+ hoursMissing: number().int().nonnegative(),
8701
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8702
+ listings: number().int().nonnegative(),
8703
+ segmentsClaimed: number().int().nonnegative(),
8704
+ ghostSegments: number().int().nonnegative(),
8705
+ ghostBytes: number().int().nonnegative(),
8706
+ ghostHoursWhole: number().int().nonnegative(),
8707
+ forgottenSegments: number().int().nonnegative(),
8708
+ forgottenBytes: number().int().nonnegative(),
8709
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8710
+ orphanFiles: number().int().nonnegative(),
8711
+ orphanSample: array(string()).readonly(),
8712
+ hoursSkipped: number().int().nonnegative(),
8713
+ skippedByReason: LedgerWalkSkipCountsSchema,
8714
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8715
+ bounded: boolean(),
8716
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8717
+ });
8587
8718
  /** How many rows a media pass would still act on against a given target — the
8588
8719
  * media lane's denominator AND its residue, from ONE derivation so the two can
8589
8720
  * never disagree. `null` = the count could not be taken. */
8590
8721
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
8591
8722
  var RelocatableMediaCountInputSchema = object({
8592
8723
  toLocationId: string().min(1),
8724
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
8725
+ fromLocationId: string().optional(),
8593
8726
  /** Omitted = `move`. */
8594
8727
  mode: MediaRelocateModeSchema.optional()
8595
8728
  });
8596
8729
  /**
8730
+ * Operator cleanup of leftover analytics rows, optional debug media, and
8731
+ * ghost ledger entries on frozen footage locations.
8732
+ *
8733
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
8734
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
8735
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
8736
+ * with no operator-visible status.
8737
+ */
8738
+ var StorageCleanupPhaseSchema = _enum([
8739
+ "orphans",
8740
+ "debug-media",
8741
+ "ghost-ledger",
8742
+ "done",
8743
+ "failed",
8744
+ "cancelled"
8745
+ ]);
8746
+ var StorageCleanupInputSchema = object({
8747
+ /** Also walk motion stills / track filmstrips. Off by default. */
8748
+ includeDebugMedia: boolean().optional() });
8749
+ var StorageCleanupJobSchema = object({
8750
+ jobId: string(),
8751
+ phase: StorageCleanupPhaseSchema,
8752
+ includeDebugMedia: boolean(),
8753
+ orphansReclaimed: number().int().nonnegative(),
8754
+ orphanBytesReclaimed: number().int().nonnegative(),
8755
+ debugMediaReclaimed: number().int().nonnegative(),
8756
+ debugMediaBytesReclaimed: number().int().nonnegative(),
8757
+ ghostsForgotten: number().int().nonnegative(),
8758
+ ghostBytesForgotten: number().int().nonnegative(),
8759
+ /** Short operator-facing line: current collection, pass, or location. */
8760
+ detail: string().nullable(),
8761
+ cancelRequested: boolean(),
8762
+ startedAt: number(),
8763
+ updatedAt: number(),
8764
+ finishedAt: number().nullable(),
8765
+ error: string().nullable()
8766
+ });
8767
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
8768
+ /**
8597
8769
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8598
8770
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8599
8771
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -8621,11 +8793,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8621
8793
  * The default location for a type uses `id === <type>:default` by
8622
8794
  * convention (the bare type ref like `'backups'` resolves to it).
8623
8795
  *
8624
- * `isSystem: true` marks a location as orchestrator-seeded and
8625
- * undeletable. The bootstrap-installed defaults (one per type) carry
8626
- * this flag; operator-added locations don't. Editing the config of
8627
- * a system location is allowed (path migration, provider swap) but
8628
- * deleting it is rejected at the cap level.
8796
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
8797
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
8798
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
8799
+ * / last-enabled, not on this bit.
8629
8800
  */
8630
8801
  var StorageLocationSchema = object({
8631
8802
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -13306,6 +13477,12 @@ method(object({
13306
13477
  }), _void(), {
13307
13478
  kind: "mutation",
13308
13479
  auth: "admin"
13480
+ }), method(object({
13481
+ from: string(),
13482
+ to: string()
13483
+ }), object({ moved: number() }), {
13484
+ kind: "mutation",
13485
+ auth: "admin"
13309
13486
  }), method(object({
13310
13487
  deviceId: number(),
13311
13488
  disabled: boolean()
@@ -19344,53 +19521,15 @@ var RecentTracksPageSchema = object({
19344
19521
  /** Cursor for the next page, or null when this page is the last. */
19345
19522
  nextCursor: string().nullable()
19346
19523
  });
19347
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
19348
- var LIST_GROUPS_MAX_LIMIT = 100;
19349
- var AnalyticsGroupRecordSchema = object({
19350
- id: string(),
19351
- deviceId: number().int(),
19352
- openedAt: number().int(),
19353
- closedAt: number().int(),
19354
- timestamp: number().int(),
19355
- memberCount: number().int(),
19356
- memberTrackIds: array(string()).readonly(),
19357
- className: string(),
19358
- classes: array(string()).readonly(),
19359
- /** Relative event-media path, or null when the group has no picture yet. */
19360
- mediaUrl: string().nullable(),
19361
- singleton: boolean()
19362
- });
19363
- var AnalyticsGroupMemberSchema = object({
19364
- trackId: string(),
19365
- deviceId: number().int(),
19366
- className: string(),
19367
- firstSeen: number().int(),
19368
- lastSeen: number().int(),
19369
- mediaUrl: string().nullable()
19370
- });
19371
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
19372
- var ListGroupsQueryInput = object({
19373
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
19374
- deviceIds: array(number()),
19375
- /** Window lower bound on `closedAt` (inclusive). */
19376
- since: number().optional(),
19377
- /** Window upper bound on `openedAt` (inclusive). */
19378
- until: number().optional(),
19379
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
19380
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
19381
- cursor: string().optional()
19382
- });
19383
- var ListGroupsPageSchema = object({
19384
- groups: array(AnalyticsGroupRecordSchema).readonly(),
19385
- nextCursor: string().nullable()
19386
- });
19524
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19525
+ var KEY_EVENTS_MAX_LIMIT = 200;
19387
19526
  var KeyEventQueryInput = object({
19388
19527
  deviceId: number(),
19389
19528
  /** Window lower bound (track firstSeen ≥ since). */
19390
19529
  since: number(),
19391
19530
  /** Window upper bound (track firstSeen ≤ until). */
19392
19531
  until: number(),
19393
- limit: number().int().min(1).max(200).default(50),
19532
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19394
19533
  /** Drop tracks scoring below this importance. */
19395
19534
  minImportance: number().min(0).max(1).optional(),
19396
19535
  /** Restrict to a single class (e.g. 'person'). */
@@ -19412,6 +19551,32 @@ var KeyEventSchema = object({
19412
19551
  ...TrackFlagFields,
19413
19552
  ...TrackRetrainFields
19414
19553
  });
19554
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
19555
+ var KeyEventBatchQueryInput = object({
19556
+ deviceIds: array(number()).min(1).max(200),
19557
+ since: number(),
19558
+ until: number(),
19559
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
19560
+ * across the set, which would let a busy camera starve a quiet one of its
19561
+ * rows and change what the merged feed contains. */
19562
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19563
+ minImportance: number().min(0).max(1).optional(),
19564
+ classFilter: string().optional()
19565
+ });
19566
+ /**
19567
+ * One camera's key events in a batch answer.
19568
+ *
19569
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
19570
+ * error rather than throwing, so a camera whose store read failed and one with
19571
+ * no events in the window were ALREADY indistinguishable per camera — the
19572
+ * batch does not make that worse, and the row keeps the deviceId the single
19573
+ * method's output never carried (the caller used to stamp it from the fan-out
19574
+ * key, which only worked because there was one query per camera).
19575
+ */
19576
+ var KeyEventsForDeviceSchema = object({
19577
+ deviceId: number(),
19578
+ events: array(KeyEventSchema).readonly()
19579
+ });
19415
19580
  object({
19416
19581
  trackId: string(),
19417
19582
  className: string(),
@@ -19459,9 +19624,7 @@ var TrackCascadeCountsSchema = object({
19459
19624
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
19460
19625
  plates: number().int(),
19461
19626
  /** Per-track CLIP search vectors removed (best-effort). */
19462
- embeddings: number().int(),
19463
- /** Group membership + group rows removed with their last member (best-effort). */
19464
- groups: number().int()
19627
+ embeddings: number().int()
19465
19628
  });
19466
19629
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
19467
19630
  var DiskReconcileCountsSchema = object({
@@ -19631,6 +19794,47 @@ var RebuildStatusSchema = object({
19631
19794
  /** Present when the pass ended by throwing. */
19632
19795
  error: string().nullable()
19633
19796
  });
19797
+ /**
19798
+ * Acknowledgement that a debug-media reclaim STARTED.
19799
+ *
19800
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
19801
+ * it runs detached and this returns immediately. Awaiting it is how the
19802
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
19803
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
19804
+ */
19805
+ var MediaReclaimStartResultSchema = object({
19806
+ started: boolean(),
19807
+ /** True when a pass was already running; the new request is ignored. */
19808
+ alreadyRunning: boolean()
19809
+ });
19810
+ var MediaReclaimInputSchema = object({
19811
+ mode: _enum(["report", "reclaim"]).default("report"),
19812
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
19813
+ deviceIds: array(number().int()).min(1).optional(),
19814
+ restart: boolean().optional(),
19815
+ pageSize: number().int().min(50).max(5e3).optional(),
19816
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
19817
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
19818
+ maxBytesPerRun: number().int().min(1).optional(),
19819
+ budgetMinutes: number().int().min(1).max(720).optional(),
19820
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
19821
+ graceMinutes: number().int().min(1).max(10080).optional()
19822
+ });
19823
+ var MediaReclaimStatusSchema = object({
19824
+ running: boolean(),
19825
+ mode: _enum(["report", "reclaim"]).nullable(),
19826
+ totalExamined: number(),
19827
+ totalEligible: number(),
19828
+ totalReclaimed: number(),
19829
+ totalBytesReclaimed: number(),
19830
+ totalRefused: number(),
19831
+ /** Device+scope windows finished in this pass. */
19832
+ devicesDone: number(),
19833
+ complete: boolean().nullable(),
19834
+ startedAtMs: number().nullable(),
19835
+ finishedAtMs: number().nullable(),
19836
+ error: string().nullable()
19837
+ });
19634
19838
  var ReplayFrameInputSchema = object({
19635
19839
  timestamp: number(),
19636
19840
  frame: PipelineRunResultBridge
@@ -19669,10 +19873,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19669
19873
  * stationary registry). Default false: the timeline lists passages,
19670
19874
  * not parking records (operator decision, 2026-08-15). */
19671
19875
  includeStationary: boolean().optional()
19672
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
19673
- deviceId: number(),
19674
- groupId: string().min(1)
19675
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
19876
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
19676
19877
  kind: "mutation",
19677
19878
  auth: "admin"
19678
19879
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -19681,7 +19882,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19681
19882
  until: number().optional(),
19682
19883
  kinds: array(string()).optional(),
19683
19884
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19684
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19885
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
19685
19886
  deviceId: number(),
19686
19887
  since: number(),
19687
19888
  until: number(),
@@ -19772,6 +19973,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19772
19973
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19773
19974
  kind: "query",
19774
19975
  auth: "admin"
19976
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
19977
+ kind: "mutation",
19978
+ auth: "admin"
19979
+ }), method(object({}), MediaReclaimStatusSchema, {
19980
+ kind: "query",
19981
+ auth: "admin"
19775
19982
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
19776
19983
  kind: "query",
19777
19984
  auth: "admin"
@@ -21742,7 +21949,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
21742
21949
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
21743
21950
  kind: "mutation",
21744
21951
  auth: "admin"
21745
- });
21952
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
21953
+ kind: "mutation",
21954
+ auth: "admin"
21955
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
21956
+ kind: "mutation",
21957
+ auth: "admin"
21958
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
21746
21959
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
21747
21960
  providerId: string().min(1),
21748
21961
  displayName: string().min(1),
@@ -28924,6 +29137,33 @@ var RecordingRebalanceInputSchema = object({
28924
29137
  minMoveGb: number().min(0).optional()
28925
29138
  });
28926
29139
  /**
29140
+ * Operator-facing placement of one camera onto a recordings location.
29141
+ *
29142
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
29143
+ * Auto (the planner may move this camera). `locationId` is where high/mid
29144
+ * currently write — the plan, which may disagree with the pin when Auto.
29145
+ */
29146
+ var RecordingDevicePlacementSchema = object({
29147
+ deviceId: number().int(),
29148
+ profile: string(),
29149
+ locationId: string()
29150
+ });
29151
+ var RecordingDevicePinSchema = object({
29152
+ deviceId: number().int(),
29153
+ /** Recordings-class location this camera is pinned to. */
29154
+ locationId: string()
29155
+ });
29156
+ var RecordingPlacementViewSchema = object({
29157
+ assignments: array(RecordingDevicePlacementSchema),
29158
+ pins: array(RecordingDevicePinSchema),
29159
+ defaultLocations: record(string(), string())
29160
+ });
29161
+ var RecordingSetDevicePlacementInputSchema = object({
29162
+ deviceId: number().int(),
29163
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
29164
+ locationId: string().nullable()
29165
+ });
29166
+ /**
28927
29167
  * Result of locating footage at a wall-clock instant for one device/profile.
28928
29168
  * `segment` carries the covering segment's window; `gap` reports the forward
28929
29169
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -29137,6 +29377,9 @@ method(object({
29137
29377
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
29138
29378
  kind: "query",
29139
29379
  auth: "admin"
29380
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
29381
+ kind: "mutation",
29382
+ auth: "admin"
29140
29383
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
29141
29384
  kind: "mutation",
29142
29385
  auth: "admin"
@@ -29146,6 +29389,12 @@ method(object({
29146
29389
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
29147
29390
  kind: "mutation",
29148
29391
  auth: "admin"
29392
+ }), method(object({}), RecordingPlacementViewSchema, {
29393
+ kind: "query",
29394
+ auth: "admin"
29395
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
29396
+ kind: "mutation",
29397
+ auth: "admin"
29149
29398
  });
29150
29399
  /**
29151
29400
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -29528,6 +29777,25 @@ var SceneMonitorStatusSchema = object({
29528
29777
  monitors: array(SceneMonitorSchema),
29529
29778
  lastFetchedAt: number()
29530
29779
  });
29780
+ /**
29781
+ * One camera's row in a `listScenesBatch` answer.
29782
+ *
29783
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
29784
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
29785
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
29786
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
29787
+ * configured. Fanned out per camera the difference was visible — one query
29788
+ * errored while the others resolved — and a batch that returned only the rows
29789
+ * it managed would have destroyed it, silently, by making an unreachable camera
29790
+ * indistinguishable from one that answered `monitors: []`.
29791
+ *
29792
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
29793
+ * could not be read"; `status.monitors: []` means "read, and it has none".
29794
+ */
29795
+ var SceneMonitorStatusForDeviceSchema = object({
29796
+ deviceId: number(),
29797
+ status: SceneMonitorStatusSchema.nullable()
29798
+ });
29531
29799
  var sceneMonitorCapability = {
29532
29800
  name: "scene-monitor",
29533
29801
  scope: "device",
@@ -29537,6 +29805,22 @@ var sceneMonitorCapability = {
29537
29805
  deviceTypes: [DeviceType.Camera],
29538
29806
  methods: {
29539
29807
  listScenes: method(object({ deviceId: number() }), SceneMonitorStatusSchema),
29808
+ /**
29809
+ * The same answer, for a SET of cameras, in one round trip.
29810
+ *
29811
+ * `/scenes` renders every camera and re-reads them on a 30s safety-net
29812
+ * poll behind the push slice. Fanned out client-side that was one query
29813
+ * per camera — 29 round trips through the browser, the hub and the
29814
+ * post-analysis runner every 30 seconds to read an in-memory map the
29815
+ * owner had already merged. The work is unchanged (`statusFor` per
29816
+ * device, all in-process at the owner); what collapses is the transport.
29817
+ *
29818
+ * A camera that cannot answer still gets a row, with `status: null` —
29819
+ * see {@link SceneMonitorStatusForDeviceSchema}. Never fewer rows than
29820
+ * ids: a caller that asked for twenty-nine and got twenty-seven cannot
29821
+ * tell which two are missing, or that any are.
29822
+ */
29823
+ listScenesBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()),
29540
29824
  createScene: method(object({
29541
29825
  deviceId: number(),
29542
29826
  label: string(),
@@ -31372,6 +31656,27 @@ var CameraOccupancySnapshotSchema = object({
31372
31656
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
31373
31657
  });
31374
31658
  /**
31659
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
31660
+ *
31661
+ * THREE outcomes, and the single-camera method could only express two of them
31662
+ * because `snapshot: null` was already spoken for:
31663
+ *
31664
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
31665
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
31666
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
31667
+ * - `read: 'unreadable'` — the owner could not answer for this
31668
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
31669
+ *
31670
+ * Collapsing the last two is the failure this field exists to prevent: a
31671
+ * hydration that threw would otherwise render as an empty Stationary section,
31672
+ * which is a definite claim about a camera nobody could read.
31673
+ */
31674
+ var CameraOccupancySnapshotForDeviceSchema = object({
31675
+ deviceId: number(),
31676
+ read: _enum(["read", "unreadable"]),
31677
+ snapshot: CameraOccupancySnapshotSchema.nullable()
31678
+ });
31679
+ /**
31375
31680
  * Time-series resolution. The history methods return one bucket per
31376
31681
  * step over the requested range. Smaller resolutions cost more
31377
31682
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -31428,6 +31733,20 @@ var zoneAnalyticsCapability = {
31428
31733
  * (no inference result emitted since boot or since binding was
31429
31734
  * activated). */
31430
31735
  getCurrentSnapshot: method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()),
31736
+ /**
31737
+ * The same snapshot, for a SET of cameras, in one round trip.
31738
+ *
31739
+ * The Events page's Stationary section polls this every 15s for every
31740
+ * selected camera. Fanned out client-side that is one query per camera to
31741
+ * read a `Map.get` at the owner — the answer costs nothing, the round trip
31742
+ * costs everything. Batched, N transports become one and the per-device
31743
+ * work is unchanged.
31744
+ *
31745
+ * Every requested deviceId gets a row, tagged `read` — see
31746
+ * {@link CameraOccupancySnapshotForDeviceSchema}. A camera the owner could
31747
+ * not answer for is `'unreadable'`, never an empty reading.
31748
+ */
31749
+ getCurrentSnapshotBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()),
31431
31750
  /** Time-series object count inside one zone. `className` optional —
31432
31751
  * omit to count every class in the zone. */
31433
31752
  getZoneHistory: method(object({
@@ -34145,6 +34464,12 @@ Object.freeze({
34145
34464
  addonId: null,
34146
34465
  access: "delete"
34147
34466
  },
34467
+ "deviceManager.renameLocation": {
34468
+ capName: "device-manager",
34469
+ capScope: "system",
34470
+ addonId: null,
34471
+ access: "create"
34472
+ },
34148
34473
  "deviceManager.runDeviceAction": {
34149
34474
  capName: "device-manager",
34150
34475
  capScope: "system",
@@ -35837,13 +36162,19 @@ Object.freeze({
35837
36162
  addonId: null,
35838
36163
  access: "view"
35839
36164
  },
35840
- "pipelineAnalytics.getGroup": {
36165
+ "pipelineAnalytics.getKeyEvents": {
35841
36166
  capName: "pipeline-analytics",
35842
36167
  capScope: "device",
35843
36168
  addonId: null,
35844
36169
  access: "view"
35845
36170
  },
35846
- "pipelineAnalytics.getKeyEvents": {
36171
+ "pipelineAnalytics.getKeyEventsBatch": {
36172
+ capName: "pipeline-analytics",
36173
+ capScope: "device",
36174
+ addonId: null,
36175
+ access: "view"
36176
+ },
36177
+ "pipelineAnalytics.getMediaReclaimStatus": {
35847
36178
  capName: "pipeline-analytics",
35848
36179
  capScope: "device",
35849
36180
  addonId: null,
@@ -35933,12 +36264,6 @@ Object.freeze({
35933
36264
  addonId: null,
35934
36265
  access: "view"
35935
36266
  },
35936
- "pipelineAnalytics.listGroups": {
35937
- capName: "pipeline-analytics",
35938
- capScope: "device",
35939
- addonId: null,
35940
- access: "view"
35941
- },
35942
36267
  "pipelineAnalytics.listOpsLog": {
35943
36268
  capName: "pipeline-analytics",
35944
36269
  capScope: "device",
@@ -36023,6 +36348,12 @@ Object.freeze({
36023
36348
  addonId: null,
36024
36349
  access: "create"
36025
36350
  },
36351
+ "pipelineAnalytics.reclaimDebugMedia": {
36352
+ capName: "pipeline-analytics",
36353
+ capScope: "device",
36354
+ addonId: null,
36355
+ access: "create"
36356
+ },
36026
36357
  "pipelineAnalytics.reconcileFromDisk": {
36027
36358
  capName: "pipeline-analytics",
36028
36359
  capScope: "device",
@@ -36947,6 +37278,12 @@ Object.freeze({
36947
37278
  addonId: null,
36948
37279
  access: "view"
36949
37280
  },
37281
+ "recording.getPlacement": {
37282
+ capName: "recording",
37283
+ capScope: "system",
37284
+ addonId: null,
37285
+ access: "view"
37286
+ },
36950
37287
  "recording.getPlaybackManifest": {
36951
37288
  capName: "recording",
36952
37289
  capScope: "system",
@@ -37025,6 +37362,12 @@ Object.freeze({
37025
37362
  addonId: null,
37026
37363
  access: "view"
37027
37364
  },
37365
+ "recording.reconcileLedgerAgainstDisk": {
37366
+ capName: "recording",
37367
+ capScope: "system",
37368
+ addonId: null,
37369
+ access: "create"
37370
+ },
37028
37371
  "recording.refreshStorageLocationsForMigration": {
37029
37372
  capName: "recording",
37030
37373
  capScope: "system",
@@ -37067,6 +37410,12 @@ Object.freeze({
37067
37410
  addonId: null,
37068
37411
  access: "create"
37069
37412
  },
37413
+ "recording.setDevicePlacement": {
37414
+ capName: "recording",
37415
+ capScope: "system",
37416
+ addonId: null,
37417
+ access: "create"
37418
+ },
37070
37419
  "recording.startStorageMigrationMove": {
37071
37420
  capName: "recording",
37072
37421
  capScope: "system",
@@ -37151,6 +37500,12 @@ Object.freeze({
37151
37500
  addonId: null,
37152
37501
  access: "view"
37153
37502
  },
37503
+ "sceneMonitor.listScenesBatch": {
37504
+ capName: "scene-monitor",
37505
+ capScope: "device",
37506
+ addonId: null,
37507
+ access: "view"
37508
+ },
37154
37509
  "sceneMonitor.recheckNow": {
37155
37510
  capName: "scene-monitor",
37156
37511
  capScope: "device",
@@ -37505,12 +37860,36 @@ Object.freeze({
37505
37860
  addonId: null,
37506
37861
  access: "create"
37507
37862
  },
37863
+ "storageMigration.cleanupCancel": {
37864
+ capName: "storage-migration",
37865
+ capScope: "system",
37866
+ addonId: null,
37867
+ access: "create"
37868
+ },
37869
+ "storageMigration.cleanupStart": {
37870
+ capName: "storage-migration",
37871
+ capScope: "system",
37872
+ addonId: null,
37873
+ access: "create"
37874
+ },
37875
+ "storageMigration.cleanupStatus": {
37876
+ capName: "storage-migration",
37877
+ capScope: "system",
37878
+ addonId: null,
37879
+ access: "view"
37880
+ },
37508
37881
  "storageMigration.drain": {
37509
37882
  capName: "storage-migration",
37510
37883
  capScope: "system",
37511
37884
  addonId: null,
37512
37885
  access: "create"
37513
37886
  },
37887
+ "storageMigration.history": {
37888
+ capName: "storage-migration",
37889
+ capScope: "system",
37890
+ addonId: null,
37891
+ access: "view"
37892
+ },
37514
37893
  "storageMigration.movers": {
37515
37894
  capName: "storage-migration",
37516
37895
  capScope: "system",
@@ -38507,6 +38886,12 @@ Object.freeze({
38507
38886
  addonId: null,
38508
38887
  access: "view"
38509
38888
  },
38889
+ "zoneAnalytics.getCurrentSnapshotBatch": {
38890
+ capName: "zone-analytics",
38891
+ capScope: "device",
38892
+ addonId: null,
38893
+ access: "view"
38894
+ },
38510
38895
  "zoneAnalytics.getUnzonedHistory": {
38511
38896
  capName: "zone-analytics",
38512
38897
  capScope: "device",
@@ -39501,14 +39886,14 @@ Object.freeze({
39501
39886
  form: "single",
39502
39887
  optional: true
39503
39888
  }],
39504
- "pipelineAnalytics.getGroup": [{
39889
+ "pipelineAnalytics.getKeyEvents": [{
39505
39890
  name: "deviceId",
39506
39891
  form: "single",
39507
39892
  optional: false
39508
39893
  }],
39509
- "pipelineAnalytics.getKeyEvents": [{
39510
- name: "deviceId",
39511
- form: "single",
39894
+ "pipelineAnalytics.getKeyEventsBatch": [{
39895
+ name: "deviceIds",
39896
+ form: "array",
39512
39897
  optional: false
39513
39898
  }],
39514
39899
  "pipelineAnalytics.getMotionEvents": [{
@@ -39566,11 +39951,6 @@ Object.freeze({
39566
39951
  form: "single",
39567
39952
  optional: false
39568
39953
  }],
39569
- "pipelineAnalytics.listGroups": [{
39570
- name: "deviceIds",
39571
- form: "array",
39572
- optional: false
39573
- }],
39574
39954
  "pipelineAnalytics.listOpsLog": [{
39575
39955
  name: "deviceId",
39576
39956
  form: "single",
@@ -39616,6 +39996,11 @@ Object.freeze({
39616
39996
  form: "single",
39617
39997
  optional: true
39618
39998
  }],
39999
+ "pipelineAnalytics.reclaimDebugMedia": [{
40000
+ name: "deviceIds",
40001
+ form: "array",
40002
+ optional: true
40003
+ }],
39619
40004
  "pipelineAnalytics.reconcileFromDisk": [{
39620
40005
  name: "deviceId",
39621
40006
  form: "single",
@@ -39951,6 +40336,11 @@ Object.freeze({
39951
40336
  form: "single",
39952
40337
  optional: false
39953
40338
  }],
40339
+ "recording.reconcileLedgerAgainstDisk": [{
40340
+ name: "deviceId",
40341
+ form: "single",
40342
+ optional: true
40343
+ }],
39954
40344
  "recording.relocateFootage": [{
39955
40345
  name: "deviceId",
39956
40346
  form: "single",
@@ -39976,6 +40366,11 @@ Object.freeze({
39976
40366
  form: "single",
39977
40367
  optional: false
39978
40368
  }],
40369
+ "recording.setDevicePlacement": [{
40370
+ name: "deviceId",
40371
+ form: "single",
40372
+ optional: false
40373
+ }],
39979
40374
  "recording.startStorageMigrationMove": [{
39980
40375
  name: "deviceId",
39981
40376
  form: "single",
@@ -40016,6 +40411,11 @@ Object.freeze({
40016
40411
  form: "single",
40017
40412
  optional: false
40018
40413
  }],
40414
+ "sceneMonitor.listScenesBatch": [{
40415
+ name: "deviceIds",
40416
+ form: "array",
40417
+ optional: false
40418
+ }],
40019
40419
  "sceneMonitor.recheckNow": [{
40020
40420
  name: "deviceId",
40021
40421
  form: "single",
@@ -40277,6 +40677,11 @@ Object.freeze({
40277
40677
  form: "single",
40278
40678
  optional: false
40279
40679
  }],
40680
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
40681
+ name: "deviceIds",
40682
+ form: "array",
40683
+ optional: false
40684
+ }],
40280
40685
  "zoneAnalytics.getUnzonedHistory": [{
40281
40686
  name: "deviceId",
40282
40687
  form: "single",