@camstack/addon-provider-unifi 0.2.57 → 0.2.59

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.
package/dist/addon.js CHANGED
@@ -10527,6 +10527,89 @@ var LocationStatSchema = object({
10527
10527
  fileCount: number(),
10528
10528
  present: boolean()
10529
10529
  });
10530
+ /** Lifecycle of a backup run. Terminal states: succeeded / failed / cancelled. */
10531
+ var BackupRunStateSchema = _enum([
10532
+ "queued",
10533
+ "running",
10534
+ "succeeded",
10535
+ "failed",
10536
+ "cancelled"
10537
+ ]);
10538
+ /**
10539
+ * Where a running backup currently is. `queued` before it starts,
10540
+ * `building` while the tar.gz is being staged, `uploading` during the
10541
+ * per-destination fan-out, `done` once terminal.
10542
+ */
10543
+ var BackupRunPhaseSchema = _enum([
10544
+ "queued",
10545
+ "building",
10546
+ "uploading",
10547
+ "done"
10548
+ ]);
10549
+ /**
10550
+ * Observable state of one backup run — readable WHILE it runs via
10551
+ * `backup.listRuns`. This is what makes the execution queue and
10552
+ * `backup.cancel` usable: the 2026-09-04 incident (two concurrent
10553
+ * multi-GB builds, staging 5.1 GB → 18 GB, load 62) was only
10554
+ * diagnosable with `du` because nothing reported that runs existed or
10555
+ * how large the staged archive had grown.
10556
+ */
10557
+ var BackupRunSchema = object({
10558
+ /** Stable run id — the handle `backup.cancel` takes. */
10559
+ id: string(),
10560
+ state: BackupRunStateSchema,
10561
+ phase: BackupRunPhaseSchema,
10562
+ /**
10563
+ * Resolved destination location ids. Empty while queued (targets are
10564
+ * resolved when the run starts, against the then-current policies).
10565
+ */
10566
+ destinationIds: array(string()).readonly(),
10567
+ label: string().optional(),
10568
+ /** ms-epoch when the run was submitted (trigger call / schedule fire). */
10569
+ requestedAt: number(),
10570
+ /** ms-epoch when the run left the queue and started building. */
10571
+ startedAt: number().optional(),
10572
+ /** ms-epoch when the run reached a terminal state. */
10573
+ finishedAt: number().optional(),
10574
+ /** Compressed bytes of the staging archive written so far. */
10575
+ stagedBytes: number(),
10576
+ /** Final staged archive size, once the build phase completes. */
10577
+ archiveSizeBytes: number().optional(),
10578
+ /** Bytes pushed to the destination currently uploading. */
10579
+ uploadedBytes: number(),
10580
+ /** Destinations where the archive fully landed (uploaded + indexed). */
10581
+ completedDestinationIds: array(string()).readonly(),
10582
+ /** Destinations that failed during the fan-out. */
10583
+ failedDestinationIds: array(string()).readonly(),
10584
+ /** Failure message when `state === 'failed'`. */
10585
+ error: string().optional(),
10586
+ /**
10587
+ * 1-based place in the execution queue — 1 = runs next. Present only
10588
+ * while `state === 'queued'`. Stamped by the orchestrator from the
10589
+ * queue's OWN pending order, never derived from timestamps, so the
10590
+ * UI cannot show an order the executor will not honour.
10591
+ */
10592
+ queuePosition: number().int().min(1).optional()
10593
+ });
10594
+ /**
10595
+ * Result of `backup.trigger`. The call still resolves when the run
10596
+ * terminates (compat with schedule-driven runs and the admin UI), but
10597
+ * it now names the run and says whether it had to WAIT: a trigger that
10598
+ * arrives while another run is in flight is enqueued (or joined onto
10599
+ * an identical already-queued run), never started concurrently.
10600
+ */
10601
+ var BackupTriggerResultSchema = object({
10602
+ /** The run this trigger mapped to — poll it via `listRuns`, stop it via `cancel`. */
10603
+ runId: string(),
10604
+ /** True when the run waited behind an in-flight run instead of starting immediately. */
10605
+ queued: boolean(),
10606
+ /** True when this trigger was coalesced onto an identical already-queued run. */
10607
+ joined: boolean(),
10608
+ /** True when the run was cancelled before completing every destination. */
10609
+ cancelled: boolean(),
10610
+ /** One entry per destination the archive landed at (partial on cancel). */
10611
+ entries: array(BackupEntrySchema).readonly()
10612
+ });
10530
10613
  /**
10531
10614
  * A backup schedule — the N:M "entry" that binds one cron cadence to a
10532
10615
  * SET of destination locations. Supersedes the per-location cron on
@@ -10574,7 +10657,10 @@ method(_void(), array(BackupDestinationInfoSchema).readonly(), { auth: "admin" }
10574
10657
  * retention (manual runs).
10575
10658
  */
10576
10659
  retentionCount: number().int().min(1).max(1e3).optional()
10577
- }).optional(), array(BackupEntrySchema).readonly(), {
10660
+ }).optional(), BackupTriggerResultSchema, {
10661
+ kind: "mutation",
10662
+ auth: "admin"
10663
+ }), method(_void(), array(BackupRunSchema).readonly(), { auth: "admin" }), method(object({ runId: string() }), object({ cancelled: boolean() }), {
10578
10664
  kind: "mutation",
10579
10665
  auth: "admin"
10580
10666
  }), method(_void(), array(BackupEntrySchema).readonly(), { auth: "admin" }), method(_void(), array(LocationStatSchema).readonly(), { auth: "admin" }), method(object({
@@ -11291,6 +11377,14 @@ method(object({
11291
11377
  }), object({ success: literal(true) }), {
11292
11378
  kind: "mutation",
11293
11379
  auth: "admin"
11380
+ }), method(object({ deviceId: number().int().nonnegative() }), object({
11381
+ derivedStreamsDeleted: array(string()).readonly(),
11382
+ assignmentsPurged: boolean(),
11383
+ probeSnapshotsDropped: number().int().nonnegative(),
11384
+ rtspTokenRowsDeleted: number().int().nonnegative()
11385
+ }), {
11386
+ kind: "mutation",
11387
+ auth: "admin"
11294
11388
  }), method(object({
11295
11389
  deviceId: number(),
11296
11390
  /** Absent = the LOWEST assigned profile — a notification attachment is
@@ -19183,7 +19277,20 @@ var RecentTracksQueryInput = object({
19183
19277
  projection: TrackProjectionSchema.optional(),
19184
19278
  /** Include stationary-promoted rows (parked objects). Default false: the
19185
19279
  * feed lists passages; parking records live on the stationary registry. */
19186
- includeStationary: boolean().optional()
19280
+ includeStationary: boolean().optional(),
19281
+ /**
19282
+ * Restrict to these track classes. ABSENT or EMPTY means no filter.
19283
+ *
19284
+ * The same filter `listTracks` takes, because the timeline's class chips must
19285
+ * mean the same thing whether the scope is one camera or twelve. Until this
19286
+ * existed the scoped feed downloaded a page and narrowed it on the phone
19287
+ * while the single-camera path narrowed the read — one filter, two costs.
19288
+ *
19289
+ * A SUPERSET prefilter on `classes[]`, like its single-camera twin: rows
19290
+ * whose class list is unreadable are kept, and the client's rule stays the
19291
+ * exact one.
19292
+ */
19293
+ classes: array(string()).optional()
19187
19294
  });
19188
19295
  var RecentTracksPageSchema = object({
19189
19296
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -23353,8 +23460,22 @@ var automationControlCapability = {
23353
23460
  * threshold.
23354
23461
  */
23355
23462
  var BatteryStatusSchema = object({
23356
- /** 0..100 inclusive. Firmware-reported. */
23357
- percentage: number().min(0).max(100),
23463
+ /**
23464
+ * 0..100 inclusive, firmware-reported. **`null` means NOT YET KNOWN** — the
23465
+ * provider has registered the capability but no reading has landed.
23466
+ *
23467
+ * It is nullable because it was not, and the only value a provider could
23468
+ * seed with was `0`. A battery camera behind an NVR therefore announced
23469
+ * itself at 0% on every start and corrected itself a moment later, which is
23470
+ * indistinguishable from a real flat battery: it fires the low-battery alert
23471
+ * every time the hub restarts. Unknown is not empty (D315), and on a battery
23472
+ * reading the difference is an alarm.
23473
+ *
23474
+ * `vacuum-control` and `lawn-mower-control` already model it this way.
23475
+ * Consumers must SKIP a null rather than coerce it — `battery-band` already
23476
+ * declines to band a non-finite reading, which is the correct shape.
23477
+ */
23478
+ percentage: number().min(0).max(100).nullable(),
23358
23479
  /**
23359
23480
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
23360
23481
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -33132,6 +33253,12 @@ Object.freeze({
33132
33253
  addonId: null,
33133
33254
  access: "create"
33134
33255
  },
33256
+ "backup.cancel": {
33257
+ capName: "backup",
33258
+ capScope: "system",
33259
+ addonId: null,
33260
+ access: "create"
33261
+ },
33135
33262
  "backup.delete": {
33136
33263
  capName: "backup",
33137
33264
  capScope: "system",
@@ -33174,6 +33301,12 @@ Object.freeze({
33174
33301
  addonId: null,
33175
33302
  access: "view"
33176
33303
  },
33304
+ "backup.listRuns": {
33305
+ capName: "backup",
33306
+ capScope: "system",
33307
+ addonId: null,
33308
+ access: "view"
33309
+ },
33177
33310
  "backup.listSchedules": {
33178
33311
  capName: "backup",
33179
33312
  capScope: "system",
@@ -37740,6 +37873,12 @@ Object.freeze({
37740
37873
  addonId: null,
37741
37874
  access: "create"
37742
37875
  },
37876
+ "streamBroker.forgetDeviceHardware": {
37877
+ capName: "stream-broker",
37878
+ capScope: "system",
37879
+ addonId: null,
37880
+ access: "delete"
37881
+ },
37743
37882
  "streamBroker.getAllRtspEntries": {
37744
37883
  capName: "stream-broker",
37745
37884
  capScope: "system",
@@ -40198,6 +40337,11 @@ Object.freeze({
40198
40337
  form: "single",
40199
40338
  optional: false
40200
40339
  }],
40340
+ "streamBroker.forgetDeviceHardware": [{
40341
+ name: "deviceId",
40342
+ form: "single",
40343
+ optional: false
40344
+ }],
40201
40345
  "streamBroker.getDeviceAudioMute": [{
40202
40346
  name: "deviceId",
40203
40347
  form: "single",
package/dist/addon.mjs CHANGED
@@ -10526,6 +10526,89 @@ var LocationStatSchema = object({
10526
10526
  fileCount: number(),
10527
10527
  present: boolean()
10528
10528
  });
10529
+ /** Lifecycle of a backup run. Terminal states: succeeded / failed / cancelled. */
10530
+ var BackupRunStateSchema = _enum([
10531
+ "queued",
10532
+ "running",
10533
+ "succeeded",
10534
+ "failed",
10535
+ "cancelled"
10536
+ ]);
10537
+ /**
10538
+ * Where a running backup currently is. `queued` before it starts,
10539
+ * `building` while the tar.gz is being staged, `uploading` during the
10540
+ * per-destination fan-out, `done` once terminal.
10541
+ */
10542
+ var BackupRunPhaseSchema = _enum([
10543
+ "queued",
10544
+ "building",
10545
+ "uploading",
10546
+ "done"
10547
+ ]);
10548
+ /**
10549
+ * Observable state of one backup run — readable WHILE it runs via
10550
+ * `backup.listRuns`. This is what makes the execution queue and
10551
+ * `backup.cancel` usable: the 2026-09-04 incident (two concurrent
10552
+ * multi-GB builds, staging 5.1 GB → 18 GB, load 62) was only
10553
+ * diagnosable with `du` because nothing reported that runs existed or
10554
+ * how large the staged archive had grown.
10555
+ */
10556
+ var BackupRunSchema = object({
10557
+ /** Stable run id — the handle `backup.cancel` takes. */
10558
+ id: string(),
10559
+ state: BackupRunStateSchema,
10560
+ phase: BackupRunPhaseSchema,
10561
+ /**
10562
+ * Resolved destination location ids. Empty while queued (targets are
10563
+ * resolved when the run starts, against the then-current policies).
10564
+ */
10565
+ destinationIds: array(string()).readonly(),
10566
+ label: string().optional(),
10567
+ /** ms-epoch when the run was submitted (trigger call / schedule fire). */
10568
+ requestedAt: number(),
10569
+ /** ms-epoch when the run left the queue and started building. */
10570
+ startedAt: number().optional(),
10571
+ /** ms-epoch when the run reached a terminal state. */
10572
+ finishedAt: number().optional(),
10573
+ /** Compressed bytes of the staging archive written so far. */
10574
+ stagedBytes: number(),
10575
+ /** Final staged archive size, once the build phase completes. */
10576
+ archiveSizeBytes: number().optional(),
10577
+ /** Bytes pushed to the destination currently uploading. */
10578
+ uploadedBytes: number(),
10579
+ /** Destinations where the archive fully landed (uploaded + indexed). */
10580
+ completedDestinationIds: array(string()).readonly(),
10581
+ /** Destinations that failed during the fan-out. */
10582
+ failedDestinationIds: array(string()).readonly(),
10583
+ /** Failure message when `state === 'failed'`. */
10584
+ error: string().optional(),
10585
+ /**
10586
+ * 1-based place in the execution queue — 1 = runs next. Present only
10587
+ * while `state === 'queued'`. Stamped by the orchestrator from the
10588
+ * queue's OWN pending order, never derived from timestamps, so the
10589
+ * UI cannot show an order the executor will not honour.
10590
+ */
10591
+ queuePosition: number().int().min(1).optional()
10592
+ });
10593
+ /**
10594
+ * Result of `backup.trigger`. The call still resolves when the run
10595
+ * terminates (compat with schedule-driven runs and the admin UI), but
10596
+ * it now names the run and says whether it had to WAIT: a trigger that
10597
+ * arrives while another run is in flight is enqueued (or joined onto
10598
+ * an identical already-queued run), never started concurrently.
10599
+ */
10600
+ var BackupTriggerResultSchema = object({
10601
+ /** The run this trigger mapped to — poll it via `listRuns`, stop it via `cancel`. */
10602
+ runId: string(),
10603
+ /** True when the run waited behind an in-flight run instead of starting immediately. */
10604
+ queued: boolean(),
10605
+ /** True when this trigger was coalesced onto an identical already-queued run. */
10606
+ joined: boolean(),
10607
+ /** True when the run was cancelled before completing every destination. */
10608
+ cancelled: boolean(),
10609
+ /** One entry per destination the archive landed at (partial on cancel). */
10610
+ entries: array(BackupEntrySchema).readonly()
10611
+ });
10529
10612
  /**
10530
10613
  * A backup schedule — the N:M "entry" that binds one cron cadence to a
10531
10614
  * SET of destination locations. Supersedes the per-location cron on
@@ -10573,7 +10656,10 @@ method(_void(), array(BackupDestinationInfoSchema).readonly(), { auth: "admin" }
10573
10656
  * retention (manual runs).
10574
10657
  */
10575
10658
  retentionCount: number().int().min(1).max(1e3).optional()
10576
- }).optional(), array(BackupEntrySchema).readonly(), {
10659
+ }).optional(), BackupTriggerResultSchema, {
10660
+ kind: "mutation",
10661
+ auth: "admin"
10662
+ }), method(_void(), array(BackupRunSchema).readonly(), { auth: "admin" }), method(object({ runId: string() }), object({ cancelled: boolean() }), {
10577
10663
  kind: "mutation",
10578
10664
  auth: "admin"
10579
10665
  }), method(_void(), array(BackupEntrySchema).readonly(), { auth: "admin" }), method(_void(), array(LocationStatSchema).readonly(), { auth: "admin" }), method(object({
@@ -11290,6 +11376,14 @@ method(object({
11290
11376
  }), object({ success: literal(true) }), {
11291
11377
  kind: "mutation",
11292
11378
  auth: "admin"
11379
+ }), method(object({ deviceId: number().int().nonnegative() }), object({
11380
+ derivedStreamsDeleted: array(string()).readonly(),
11381
+ assignmentsPurged: boolean(),
11382
+ probeSnapshotsDropped: number().int().nonnegative(),
11383
+ rtspTokenRowsDeleted: number().int().nonnegative()
11384
+ }), {
11385
+ kind: "mutation",
11386
+ auth: "admin"
11293
11387
  }), method(object({
11294
11388
  deviceId: number(),
11295
11389
  /** Absent = the LOWEST assigned profile — a notification attachment is
@@ -19182,7 +19276,20 @@ var RecentTracksQueryInput = object({
19182
19276
  projection: TrackProjectionSchema.optional(),
19183
19277
  /** Include stationary-promoted rows (parked objects). Default false: the
19184
19278
  * feed lists passages; parking records live on the stationary registry. */
19185
- includeStationary: boolean().optional()
19279
+ includeStationary: boolean().optional(),
19280
+ /**
19281
+ * Restrict to these track classes. ABSENT or EMPTY means no filter.
19282
+ *
19283
+ * The same filter `listTracks` takes, because the timeline's class chips must
19284
+ * mean the same thing whether the scope is one camera or twelve. Until this
19285
+ * existed the scoped feed downloaded a page and narrowed it on the phone
19286
+ * while the single-camera path narrowed the read — one filter, two costs.
19287
+ *
19288
+ * A SUPERSET prefilter on `classes[]`, like its single-camera twin: rows
19289
+ * whose class list is unreadable are kept, and the client's rule stays the
19290
+ * exact one.
19291
+ */
19292
+ classes: array(string()).optional()
19186
19293
  });
19187
19294
  var RecentTracksPageSchema = object({
19188
19295
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -23352,8 +23459,22 @@ var automationControlCapability = {
23352
23459
  * threshold.
23353
23460
  */
23354
23461
  var BatteryStatusSchema = object({
23355
- /** 0..100 inclusive. Firmware-reported. */
23356
- percentage: number().min(0).max(100),
23462
+ /**
23463
+ * 0..100 inclusive, firmware-reported. **`null` means NOT YET KNOWN** — the
23464
+ * provider has registered the capability but no reading has landed.
23465
+ *
23466
+ * It is nullable because it was not, and the only value a provider could
23467
+ * seed with was `0`. A battery camera behind an NVR therefore announced
23468
+ * itself at 0% on every start and corrected itself a moment later, which is
23469
+ * indistinguishable from a real flat battery: it fires the low-battery alert
23470
+ * every time the hub restarts. Unknown is not empty (D315), and on a battery
23471
+ * reading the difference is an alarm.
23472
+ *
23473
+ * `vacuum-control` and `lawn-mower-control` already model it this way.
23474
+ * Consumers must SKIP a null rather than coerce it — `battery-band` already
23475
+ * declines to band a non-finite reading, which is the correct shape.
23476
+ */
23477
+ percentage: number().min(0).max(100).nullable(),
23357
23478
  /**
23358
23479
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
23359
23480
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -33131,6 +33252,12 @@ Object.freeze({
33131
33252
  addonId: null,
33132
33253
  access: "create"
33133
33254
  },
33255
+ "backup.cancel": {
33256
+ capName: "backup",
33257
+ capScope: "system",
33258
+ addonId: null,
33259
+ access: "create"
33260
+ },
33134
33261
  "backup.delete": {
33135
33262
  capName: "backup",
33136
33263
  capScope: "system",
@@ -33173,6 +33300,12 @@ Object.freeze({
33173
33300
  addonId: null,
33174
33301
  access: "view"
33175
33302
  },
33303
+ "backup.listRuns": {
33304
+ capName: "backup",
33305
+ capScope: "system",
33306
+ addonId: null,
33307
+ access: "view"
33308
+ },
33176
33309
  "backup.listSchedules": {
33177
33310
  capName: "backup",
33178
33311
  capScope: "system",
@@ -37739,6 +37872,12 @@ Object.freeze({
37739
37872
  addonId: null,
37740
37873
  access: "create"
37741
37874
  },
37875
+ "streamBroker.forgetDeviceHardware": {
37876
+ capName: "stream-broker",
37877
+ capScope: "system",
37878
+ addonId: null,
37879
+ access: "delete"
37880
+ },
37742
37881
  "streamBroker.getAllRtspEntries": {
37743
37882
  capName: "stream-broker",
37744
37883
  capScope: "system",
@@ -40197,6 +40336,11 @@ Object.freeze({
40197
40336
  form: "single",
40198
40337
  optional: false
40199
40338
  }],
40339
+ "streamBroker.forgetDeviceHardware": [{
40340
+ name: "deviceId",
40341
+ form: "single",
40342
+ optional: false
40343
+ }],
40200
40344
  "streamBroker.getDeviceAudioMute": [{
40201
40345
  name: "deviceId",
40202
40346
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-unifi",
3
- "version": "0.2.57",
3
+ "version": "0.2.59",
4
4
  "description": "UniFi Network controller device-provider addon for CamStack — local-controller infra switches/APs (as containers) + network-client presence. NO cameras/Protect.",
5
5
  "keywords": [
6
6
  "camstack",