camstack 1.2.112 → 1.2.114

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.
@@ -14522,7 +14522,7 @@ function date4(params) {
14522
14522
  // ../../node_modules/zod/v4/classic/external.js
14523
14523
  config(en_default());
14524
14524
 
14525
- // ../types/dist/sleep-Cfij6Jj9.mjs
14525
+ // ../types/dist/sleep-Bd-Y4RUt.mjs
14526
14526
  var WELL_KNOWN_TABS = [
14527
14527
  {
14528
14528
  id: "overview",
@@ -16158,6 +16158,18 @@ var StorageCleanupJobSchema = external_exports.object({
16158
16158
  error: external_exports.string().nullable()
16159
16159
  });
16160
16160
  var StorageCleanupStatusInputSchema = external_exports.object({ jobId: external_exports.string().optional() });
16161
+ var STORAGE_LOCATION_MODES = [
16162
+ "active",
16163
+ "readonly",
16164
+ "drain",
16165
+ "disabled"
16166
+ ];
16167
+ var StorageLocationModeSchema = external_exports.enum(STORAGE_LOCATION_MODES);
16168
+ var StorageEvictionPolicySchema = external_exports.enum([
16169
+ "normal",
16170
+ "never",
16171
+ "drain"
16172
+ ]);
16161
16173
  var StorageLocationTypeSchema = external_exports.string().regex(/^[a-z][a-zA-Z0-9-]*$/);
16162
16174
  var StorageLocationSchema = external_exports.object({
16163
16175
  id: external_exports.string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -16189,6 +16201,21 @@ var StorageLocationSchema = external_exports.object({
16189
16201
  * stops existing rather than being re-derived on every read.
16190
16202
  */
16191
16203
  enabled: external_exports.boolean().optional(),
16204
+ /**
16205
+ * THE state of this location (D385), and the only authority on what may be
16206
+ * written, read or evicted here. Interpreted in exactly one place —
16207
+ * `storage-location-mode.ts` — which also folds the legacy
16208
+ * `enabled` / `config.readOnly` pair into a mode so an old row is never
16209
+ * ambiguous.
16210
+ *
16211
+ * OPTIONAL only for the wire and for rows written before D385: absence is
16212
+ * resolved by `resolveLocationMode`, and the orchestrator stamps every
16213
+ * unstamped row ONCE at hydrate so absence stops existing rather than being
16214
+ * re-derived on every read. `enabled` survives one release as a DERIVED
16215
+ * mirror (`mode === 'active'`); `withLocationMode` is the only writer of
16216
+ * either, so the two cannot disagree.
16217
+ */
16218
+ mode: StorageLocationModeSchema.optional(),
16192
16219
  /** COMPUTED at read time by the orchestrator (statfs of the backing volume
16193
16220
  * for node-local locations it can reach) — never persisted, absent when the
16194
16221
  * volume is remote/unreachable. The single capacity truth every UI reads. */
@@ -16196,10 +16223,38 @@ var StorageLocationSchema = external_exports.object({
16196
16223
  totalBytes: external_exports.number(),
16197
16224
  availableBytes: external_exports.number()
16198
16225
  }).nullable().optional(),
16226
+ /**
16227
+ * How much of that volume CamStack ITSELF holds on this location (D388) —
16228
+ * COMPUTED at read time from the `storage-occupancy` providers' own figures,
16229
+ * never persisted, never a filesystem walk.
16230
+ *
16231
+ * **ABSENT MEANS UNKNOWN, never zero.** No provider has reported for this
16232
+ * location yet — nobody stores here, the owning addon is down, or the first
16233
+ * refresh has not completed. A UI must omit the segment rather than draw it
16234
+ * at zero, which would claim we occupy nothing (D315). It is an OBJECT and
16235
+ * not a bare number precisely so that a `?? 0` on the consuming side has to
16236
+ * be spelled out loud instead of appearing by accident.
16237
+ *
16238
+ * `measuredAtMs` is the OLDEST contributing measurement, so it is honest
16239
+ * about the whole figure rather than about its freshest part.
16240
+ */
16241
+ owned: external_exports.object({
16242
+ bytes: external_exports.number().int().nonnegative(),
16243
+ measuredAtMs: external_exports.number().int().nonnegative()
16244
+ }).optional(),
16199
16245
  createdAt: external_exports.number(),
16200
16246
  updatedAt: external_exports.number()
16201
16247
  });
16202
16248
  var LegacyStorageLocationDefaultSchema = external_exports.object({ isDefault: external_exports.boolean().optional() });
16249
+ var STORAGE_BYTES_PER_GB = 1024 ** 3;
16250
+ var StorageDrainProgressSchema = external_exports.object({
16251
+ locationId: external_exports.string(),
16252
+ startedAtMs: external_exports.number(),
16253
+ startBytes: external_exports.number(),
16254
+ bytesRemaining: external_exports.number(),
16255
+ drained: external_exports.boolean(),
16256
+ estimatedEmptyAtMs: external_exports.number().nullable()
16257
+ });
16203
16258
  var StorageLocationRefSchema = external_exports.union([StorageLocationTypeSchema, external_exports.string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/)]);
16204
16259
  var StorageAccessSchema = external_exports.enum(["local-path", "cap-mediated"]);
16205
16260
  var StorageLocationDeclarationSchema = external_exports.object({
@@ -30138,6 +30193,12 @@ var storageCapability = {
30138
30193
  kind: "mutation",
30139
30194
  auth: "admin"
30140
30195
  }),
30196
+ /**
30197
+ * How far each draining location has got (D386). A pure READ of what the
30198
+ * last pressure sweep computed — it starts no work, and a location that is
30199
+ * not draining simply does not appear.
30200
+ */
30201
+ listDrainProgress: method(external_exports.void(), external_exports.array(StorageDrainProgressSchema).readonly()),
30141
30202
  testLocation: method(external_exports.object({ id: external_exports.string() }), external_exports.object({
30142
30203
  ok: external_exports.boolean(),
30143
30204
  error: external_exports.string().optional()
@@ -30267,6 +30328,37 @@ var storageMigrationCapability = {
30267
30328
  history: method(external_exports.object({}), external_exports.array(StorageMigrationJobSchema).readonly(), { auth: "admin" })
30268
30329
  }
30269
30330
  };
30331
+ var StorageOccupancyReportSchema = external_exports.object({
30332
+ locationId: external_exports.string(),
30333
+ /** Bytes this provider holds on THAT location — not its eviction domain, and
30334
+ * not net of what it is willing to delete. */
30335
+ ownedBytes: external_exports.number().int().nonnegative(),
30336
+ /** When the provider last actually measured this. The orchestrator carries it
30337
+ * through so a UI can say how old the figure is instead of implying "now". */
30338
+ measuredAtMs: external_exports.number().int().nonnegative()
30339
+ });
30340
+ var storageOccupancyCapability = {
30341
+ name: "storage-occupancy",
30342
+ scope: "system",
30343
+ mode: "collection",
30344
+ internal: true,
30345
+ methods: {
30346
+ /**
30347
+ * Occupancy for the given locations, in ONE round trip.
30348
+ *
30349
+ * A provider answers only for the locations it actually holds bytes on and
30350
+ * OMITS the rest — an omitted location is "I hold nothing measurable here",
30351
+ * which the orchestrator merges as a contribution of nothing rather than as
30352
+ * a claim that the location is empty. Only a location no provider reports
30353
+ * at all stays unknown.
30354
+ *
30355
+ * This must be CHEAP and must never walk a filesystem: it is on the admin
30356
+ * UI's `listLocations` path. The owner keeps its own figure fresh (D224) and
30357
+ * answers from what it already has.
30358
+ */
30359
+ getOccupancy: method(external_exports.object({ locationIds: external_exports.array(external_exports.string()).readonly() }), external_exports.array(StorageOccupancyReportSchema).readonly(), { auth: "admin" })
30360
+ }
30361
+ };
30270
30362
  var ProviderInfoSchema = external_exports.discriminatedUnion("shouldSaveDiskSpace", [external_exports.object({
30271
30363
  providerId: external_exports.string().min(1),
30272
30364
  displayName: external_exports.string().min(1),
@@ -32224,85 +32316,6 @@ var batteryCapability = {
32224
32316
  * whether persisting is worth a SQLite commit. */
32225
32317
  volatileStateFields: ["lastUpdated"]
32226
32318
  };
32227
- var NETWORK_LINK_TYPES = [
32228
- "wifi",
32229
- "ethernet",
32230
- "cellular",
32231
- "unknown"
32232
- ];
32233
- var NetworkLinkStatusSchema = external_exports.object({
32234
- /** The link the device is on. `'unknown'` = not read yet, not "no link". */
32235
- type: external_exports.enum(NETWORK_LINK_TYPES),
32236
- /**
32237
- * Link quality, 0..100 inclusive, normalised by the provider from whatever
32238
- * the firmware reports (bars, RSSI, a vendor scale). **`null` means NOT
32239
- * KNOWN or NOT APPLICABLE** — a wired link has no signal, and a wireless
32240
- * one whose reading has not landed must not be drawn at 0 %. Consumers
32241
- * SKIP a null rather than coerce it.
32242
- */
32243
- signalPercent: external_exports.number().min(0).max(100).nullable(),
32244
- /** Raw received signal strength in dBm, when the firmware reports one. */
32245
- rssiDbm: external_exports.number().optional(),
32246
- /** Network name of a wireless link, when the firmware reports it. */
32247
- ssid: external_exports.string().optional(),
32248
- /** Ms epoch of the last observation. Lets consumers reason about freshness. */
32249
- lastUpdated: external_exports.number()
32250
- });
32251
- var NETWORK_LINK_UNKNOWN = {
32252
- type: "unknown",
32253
- signalPercent: null,
32254
- lastUpdated: 0
32255
- };
32256
- var networkLinkCapability = {
32257
- name: "network-link",
32258
- scope: "device",
32259
- deviceNative: true,
32260
- mode: "singleton",
32261
- deviceTypes: [
32262
- DeviceType.Camera,
32263
- DeviceType.Sensor,
32264
- DeviceType.Button,
32265
- DeviceType.Switch,
32266
- DeviceType.Light,
32267
- DeviceType.Lock,
32268
- DeviceType.Siren
32269
- ],
32270
- methods: {},
32271
- events: {
32272
- /**
32273
- * Emitted whenever the cached status changes (a link switch, a signal
32274
- * reading that moved). Mirrored on the parent chain by the
32275
- * DeviceEventPropagator like `battery.onStatusChanged`.
32276
- */
32277
- onStatusChanged: { data: external_exports.object({
32278
- deviceId: external_exports.number(),
32279
- status: NetworkLinkStatusSchema
32280
- }) }
32281
- },
32282
- status: {
32283
- schema: NetworkLinkStatusSchema,
32284
- kind: "push",
32285
- empty: NETWORK_LINK_UNKNOWN
32286
- },
32287
- /**
32288
- * Runtime-state slice — every provider stores the same shape under
32289
- * `device.runtimeState['network-link']`, read once by the badge and the
32290
- * Home Assistant projector regardless of the driver.
32291
- */
32292
- runtimeState: NetworkLinkStatusSchema,
32293
- /**
32294
- * Runtime-state durability: **restored** — a link reading is slow to
32295
- * change and a sleeping battery camera may not report for hours; the
32296
- * restored slice is what the badge shows until the next read.
32297
- *
32298
- * See `RuntimeStateDurability`. Enforced by
32299
- * `scripts/check-runtime-state-durability.ts`.
32300
- */
32301
- durability: "restored",
32302
- /** Clock fields: written, but excluded from the compare that decides
32303
- * whether persisting is worth a SQLite commit. */
32304
- volatileStateFields: ["lastUpdated"]
32305
- };
32306
32319
  var BinaryStatusSchema = external_exports.object({
32307
32320
  on: external_exports.boolean(),
32308
32321
  /** Ms epoch of the last transition. 0 if never observed. */
@@ -35443,104 +35456,346 @@ var nativeObjectDetectionCapability = {
35443
35456
  * whether persisting is worth a SQLite commit. */
35444
35457
  volatileStateFields: ["lastFetchedAt"]
35445
35458
  };
35446
- var StreamNetworkStatsSchema = external_exports.object({
35447
- nominalBitrateKbps: external_exports.number(),
35448
- observedBitrateKbps: external_exports.number(),
35449
- peakBitrateKbps: external_exports.number(),
35450
- packetLossPercent: external_exports.number(),
35451
- lastUpdated: external_exports.number()
35452
- });
35453
- var ClientNetworkStatsSchema = external_exports.object({
35454
- rttMs: external_exports.number(),
35455
- jitterMs: external_exports.number(),
35456
- estimatedBandwidthKbps: external_exports.number(),
35457
- packetLossPercent: external_exports.number().min(0).max(100),
35458
- lastUpdated: external_exports.number()
35459
- });
35460
- var DeviceNetworkStatsSchema = external_exports.object({
35461
- deviceId: external_exports.number(),
35462
- streams: external_exports.record(external_exports.string(), StreamNetworkStatsSchema),
35463
- client: ClientNetworkStatsSchema.optional()
35464
- });
35465
- var networkQualityCapability = {
35466
- name: "network-quality",
35467
- scope: "system",
35468
- mode: "singleton",
35469
- methods: {
35470
- getDeviceStats: method(external_exports.object({ deviceId: external_exports.number() }), DeviceNetworkStatsSchema.nullable()),
35471
- getAllStats: method(external_exports.void(), external_exports.array(DeviceNetworkStatsSchema).readonly()),
35472
- reportClientStats: method(external_exports.object({
35473
- deviceId: external_exports.number(),
35474
- rttMs: external_exports.number().min(0).max(6e4),
35475
- jitterMs: external_exports.number().min(0).max(1e4),
35476
- estimatedBandwidthKbps: external_exports.number().min(0).max(1e6),
35477
- packetLossPercent: external_exports.number().min(0).max(100)
35478
- }), external_exports.void(), { kind: "mutation" })
35479
- },
35480
- /** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
35481
- mount: { kind: "server-provided" }
35482
- };
35483
- var TopologyServiceSchema = external_exports.object({
35484
- addonId: external_exports.string(),
35485
- capabilities: external_exports.array(external_exports.string()).readonly(),
35486
- status: external_exports.string()
35487
- });
35488
- var TopologyProcessSchema = external_exports.object({
35489
- pid: external_exports.number(),
35490
- name: external_exports.string(),
35491
- state: external_exports.string(),
35492
- cpuPercent: external_exports.number(),
35493
- memoryRss: external_exports.number(),
35494
- uptimeSeconds: external_exports.number(),
35495
- services: external_exports.array(TopologyServiceSchema).readonly(),
35496
- groupId: external_exports.string().optional()
35459
+ var NavigationMoveCommandSchema = external_exports.object({
35460
+ pan: external_exports.number().min(-1).max(1).optional(),
35461
+ tilt: external_exports.number().min(-1).max(1).optional(),
35462
+ speed: external_exports.number().min(0).max(1).optional()
35497
35463
  });
35498
- var TopologyCategoryAddonSchema = external_exports.object({
35464
+ var NavigationActionIdSchema = external_exports.enum([
35465
+ "goHome",
35466
+ "locate",
35467
+ "spotClean",
35468
+ "findPet",
35469
+ "personFollow",
35470
+ "stop",
35471
+ "startClean",
35472
+ "pauseClean",
35473
+ "dockWash",
35474
+ "autoEmpty",
35475
+ "flashOn",
35476
+ "flashOff"
35477
+ ]);
35478
+ var NavigationEntryKindSchema = external_exports.enum(["action", "sound"]);
35479
+ var NavigationActionEntrySchema = external_exports.object({
35499
35480
  id: external_exports.string(),
35500
- status: external_exports.string(),
35501
- cpuPercent: external_exports.number(),
35502
- memoryRss: external_exports.number()
35481
+ kind: NavigationEntryKindSchema,
35482
+ label: external_exports.string(),
35483
+ icon: external_exports.string(),
35484
+ /** Present only on `kind:'sound'` entries — the id to pass to `playSound`. */
35485
+ soundId: external_exports.number().int().optional(),
35486
+ /** Per-device feature flag — render this entry only when true. */
35487
+ enabled: external_exports.boolean()
35503
35488
  });
35504
- var TopologyCategorySchema = external_exports.object({
35505
- category: external_exports.string(),
35506
- total: external_exports.number(),
35507
- healthy: external_exports.number(),
35508
- addons: external_exports.array(TopologyCategoryAddonSchema).readonly()
35489
+ var NavigationPointSchema = external_exports.object({
35490
+ x: external_exports.number(),
35491
+ y: external_exports.number()
35509
35492
  });
35510
- var TopologyRootPackageSchema = external_exports.object({
35511
- name: external_exports.string(),
35512
- version: external_exports.string()
35493
+ var NavigationFeaturesSchema = external_exports.object({
35494
+ move: external_exports.boolean(),
35495
+ stop: external_exports.boolean(),
35496
+ goToPoint: external_exports.boolean(),
35497
+ runAction: external_exports.boolean(),
35498
+ playSound: external_exports.boolean(),
35499
+ light: external_exports.boolean(),
35500
+ lightMode: external_exports.boolean()
35513
35501
  });
35514
- var TopologyNodeSchema = external_exports.object({
35515
- id: external_exports.string(),
35516
- name: external_exports.string(),
35517
- hostname: external_exports.string(),
35518
- platform: external_exports.string(),
35519
- arch: external_exports.string(),
35520
- cpuModel: external_exports.string().nullable(),
35521
- cpuCores: external_exports.number(),
35522
- memoryMB: external_exports.number(),
35523
- engines: external_exports.array(external_exports.string()).readonly(),
35524
- isHub: external_exports.boolean(),
35525
- isOnline: external_exports.boolean(),
35526
- cpuPercent: external_exports.number(),
35527
- memoryPercent: external_exports.number(),
35528
- uptime: external_exports.number(),
35529
- lastSeen: external_exports.string(),
35530
- localIps: external_exports.array(external_exports.string()).readonly(),
35531
- addons: external_exports.array(external_exports.object({
35532
- id: external_exports.string(),
35533
- capabilities: external_exports.array(external_exports.string()).readonly(),
35534
- status: external_exports.string()
35535
- })).readonly(),
35536
- processes: external_exports.array(TopologyProcessSchema).readonly(),
35537
- categories: external_exports.array(TopologyCategorySchema).readonly(),
35538
- rootPackage: TopologyRootPackageSchema.nullable()
35502
+ var NavigationLightModeSchema = external_exports.enum(["auto", "manual"]);
35503
+ var NavigationModeSchema = external_exports.enum([
35504
+ "idle",
35505
+ "cleaning",
35506
+ "spot",
35507
+ "following",
35508
+ "goto",
35509
+ "returning",
35510
+ "paused",
35511
+ "unknown"
35512
+ ]);
35513
+ var NavigationStatusSchema = external_exports.object({
35514
+ mode: NavigationModeSchema,
35515
+ following: external_exports.boolean(),
35516
+ flash: external_exports.boolean(),
35517
+ lightMode: NavigationLightModeSchema,
35518
+ lightLevel: external_exports.number().min(40).max(100),
35519
+ /** Ms epoch when the slice was last updated. */
35520
+ lastChangedAt: external_exports.number()
35539
35521
  });
35540
- var CapUsageEdgeSchema = external_exports.object({
35541
- callerAddonId: external_exports.string(),
35542
- providerAddonId: external_exports.string(),
35543
- capName: external_exports.string(),
35522
+ var NavigationRuntimeStateSchema = NavigationStatusSchema.extend({ lastFetchedAt: external_exports.number() });
35523
+ var navigationCapability = {
35524
+ name: "navigation",
35525
+ scope: "device",
35526
+ deviceNative: true,
35527
+ mode: "singleton",
35528
+ deviceTypes: [DeviceType.Camera],
35529
+ deviceConfig: { ui: {
35530
+ kind: "widget",
35531
+ widgetId: "host/navigation-panel",
35532
+ tab: "navigation",
35533
+ topTab: true,
35534
+ label: "Navigation",
35535
+ order: 0
35536
+ } },
35537
+ methods: {
35538
+ /**
35539
+ * Momentary drive nudge (the robot moves). `protected` — mirrors
35540
+ * `ptz.continuousMove` so the Viewer navigation panel (and the PTZ-mimic
35541
+ * path) works for any authenticated user, not admin-only. The UI sends
35542
+ * these at ~1 Hz while a control is held; the provider forwards each one to
35543
+ * a single drive write WITHOUT debouncing.
35544
+ */
35545
+ move: method(NavigationMoveCommandSchema.extend({ deviceId: external_exports.number() }), external_exports.void(), { kind: "mutation" }),
35546
+ /** Halt all motion immediately (zero drive vector). */
35547
+ stop: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.void(), { kind: "mutation" }),
35548
+ /** Send the robot to a point on its live map. */
35549
+ goToPoint: method(NavigationPointSchema.extend({ deviceId: external_exports.number() }), external_exports.void(), { kind: "mutation" }),
35550
+ /**
35551
+ * Enumerate the discrete controls THIS device supports (data-driven UI +
35552
+ * PTZ mimic). Camera-probed subset of {@link NAVIGATION_ACTION_CATALOG}.
35553
+ */
35554
+ listActions: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.array(NavigationActionEntrySchema)),
35555
+ /**
35556
+ * Run one discrete action (a `kind:'action'` dictionary entry). Invalid /
35557
+ * unsupported action ids are rejected by the provider.
35558
+ */
35559
+ runAction: method(external_exports.object({
35560
+ deviceId: external_exports.number(),
35561
+ actionId: NavigationActionIdSchema
35562
+ }), external_exports.void(), { kind: "mutation" }),
35563
+ /** Play a sound by its wire id (the `soundId` of a `kind:'sound'` entry). */
35564
+ playSound: method(external_exports.object({
35565
+ deviceId: external_exports.number(),
35566
+ soundId: external_exports.number().int()
35567
+ }), external_exports.void(), { kind: "mutation" }),
35568
+ /**
35569
+ * Turn the on-camera fill light on / off (the `OpenFullLight` control —
35570
+ * works anytime, no active stream required).
35571
+ */
35572
+ setLightOn: method(external_exports.object({
35573
+ deviceId: external_exports.number(),
35574
+ on: external_exports.boolean()
35575
+ }), external_exports.void(), { kind: "mutation" }),
35576
+ /**
35577
+ * Set the fill-light mode (auto vs manual). `manual` optionally carries the
35578
+ * initial `level`. The auto/manual + level control is a CAMERA-service
35579
+ * action that generally needs an active camera stream/monitor session — the
35580
+ * UI shows the manual level slider ONLY when `mode === 'manual'`.
35581
+ */
35582
+ setLightMode: method(external_exports.object({
35583
+ deviceId: external_exports.number(),
35584
+ mode: NavigationLightModeSchema,
35585
+ level: external_exports.number().min(40).max(100).optional()
35586
+ }), external_exports.void(), { kind: "mutation" }),
35587
+ /** Set the MANUAL fill-light level (40..100). Implies `manual` mode. */
35588
+ setLightLevel: method(external_exports.object({
35589
+ deviceId: external_exports.number(),
35590
+ level: external_exports.number().min(40).max(100)
35591
+ }), external_exports.void(), { kind: "mutation" }),
35592
+ /**
35593
+ * Per-device FEATURE-FLAG report for the general primitives — drives which
35594
+ * controls the UI shows (the per-entry flags for the dictionary come back on
35595
+ * `listActions`).
35596
+ */
35597
+ getFeatures: method(external_exports.object({ deviceId: external_exports.number() }), NavigationFeaturesSchema)
35598
+ },
35599
+ events: { onStatusChanged: { data: external_exports.object({
35600
+ deviceId: external_exports.number(),
35601
+ status: NavigationStatusSchema
35602
+ }) } },
35603
+ status: {
35604
+ schema: NavigationStatusSchema,
35605
+ kind: "push"
35606
+ },
35607
+ /**
35608
+ * Runtime-state slice mirrored by the kernel. The navigation panel watches it
35609
+ * for live mode / follow / flash changes.
35610
+ */
35611
+ runtimeState: NavigationRuntimeStateSchema,
35612
+ /**
35613
+ * Runtime-state durability: **session** — like `vacuum-control`, a restored
35614
+ * `mode: cleaning` / `following: true` is a robot that is not actually doing
35615
+ * that. The live handle re-publishes on connect.
35616
+ *
35617
+ * See `RuntimeStateDurability`. Enforced by
35618
+ * `scripts/check-runtime-state-durability.ts`.
35619
+ */
35620
+ durability: "session"
35621
+ };
35622
+ var NETWORK_LINK_TYPES = [
35623
+ "wifi",
35624
+ "ethernet",
35625
+ "cellular",
35626
+ "unknown"
35627
+ ];
35628
+ var NetworkLinkStatusSchema = external_exports.object({
35629
+ /** The link the device is on. `'unknown'` = not read yet, not "no link". */
35630
+ type: external_exports.enum(NETWORK_LINK_TYPES),
35631
+ /**
35632
+ * Link quality, 0..100 inclusive, normalised by the provider from whatever
35633
+ * the firmware reports (bars, RSSI, a vendor scale). **`null` means NOT
35634
+ * KNOWN or NOT APPLICABLE** — a wired link has no signal, and a wireless
35635
+ * one whose reading has not landed must not be drawn at 0 %. Consumers
35636
+ * SKIP a null rather than coerce it.
35637
+ */
35638
+ signalPercent: external_exports.number().min(0).max(100).nullable(),
35639
+ /** Raw received signal strength in dBm, when the firmware reports one. */
35640
+ rssiDbm: external_exports.number().optional(),
35641
+ /** Network name of a wireless link, when the firmware reports it. */
35642
+ ssid: external_exports.string().optional(),
35643
+ /** Ms epoch of the last observation. Lets consumers reason about freshness. */
35644
+ lastUpdated: external_exports.number()
35645
+ });
35646
+ var NETWORK_LINK_UNKNOWN = {
35647
+ type: "unknown",
35648
+ signalPercent: null,
35649
+ lastUpdated: 0
35650
+ };
35651
+ var networkLinkCapability = {
35652
+ name: "network-link",
35653
+ scope: "device",
35654
+ deviceNative: true,
35655
+ mode: "singleton",
35656
+ deviceTypes: [
35657
+ DeviceType.Camera,
35658
+ DeviceType.Sensor,
35659
+ DeviceType.Button,
35660
+ DeviceType.Switch,
35661
+ DeviceType.Light,
35662
+ DeviceType.Lock,
35663
+ DeviceType.Siren
35664
+ ],
35665
+ methods: {},
35666
+ events: {
35667
+ /**
35668
+ * Emitted whenever the cached status changes (a link switch, a signal
35669
+ * reading that moved). Mirrored on the parent chain by the
35670
+ * DeviceEventPropagator like `battery.onStatusChanged`.
35671
+ */
35672
+ onStatusChanged: { data: external_exports.object({
35673
+ deviceId: external_exports.number(),
35674
+ status: NetworkLinkStatusSchema
35675
+ }) }
35676
+ },
35677
+ status: {
35678
+ schema: NetworkLinkStatusSchema,
35679
+ kind: "push",
35680
+ empty: NETWORK_LINK_UNKNOWN
35681
+ },
35682
+ /**
35683
+ * Runtime-state slice — every provider stores the same shape under
35684
+ * `device.runtimeState['network-link']`, read once by the badge and the
35685
+ * Home Assistant projector regardless of the driver.
35686
+ */
35687
+ runtimeState: NetworkLinkStatusSchema,
35688
+ /**
35689
+ * Runtime-state durability: **restored** — a link reading is slow to
35690
+ * change and a sleeping battery camera may not report for hours; the
35691
+ * restored slice is what the badge shows until the next read.
35692
+ *
35693
+ * See `RuntimeStateDurability`. Enforced by
35694
+ * `scripts/check-runtime-state-durability.ts`.
35695
+ */
35696
+ durability: "restored",
35697
+ /** Clock fields: written, but excluded from the compare that decides
35698
+ * whether persisting is worth a SQLite commit. */
35699
+ volatileStateFields: ["lastUpdated"]
35700
+ };
35701
+ var StreamNetworkStatsSchema = external_exports.object({
35702
+ nominalBitrateKbps: external_exports.number(),
35703
+ observedBitrateKbps: external_exports.number(),
35704
+ peakBitrateKbps: external_exports.number(),
35705
+ packetLossPercent: external_exports.number(),
35706
+ lastUpdated: external_exports.number()
35707
+ });
35708
+ var ClientNetworkStatsSchema = external_exports.object({
35709
+ rttMs: external_exports.number(),
35710
+ jitterMs: external_exports.number(),
35711
+ estimatedBandwidthKbps: external_exports.number(),
35712
+ packetLossPercent: external_exports.number().min(0).max(100),
35713
+ lastUpdated: external_exports.number()
35714
+ });
35715
+ var DeviceNetworkStatsSchema = external_exports.object({
35716
+ deviceId: external_exports.number(),
35717
+ streams: external_exports.record(external_exports.string(), StreamNetworkStatsSchema),
35718
+ client: ClientNetworkStatsSchema.optional()
35719
+ });
35720
+ var networkQualityCapability = {
35721
+ name: "network-quality",
35722
+ scope: "system",
35723
+ mode: "singleton",
35724
+ methods: {
35725
+ getDeviceStats: method(external_exports.object({ deviceId: external_exports.number() }), DeviceNetworkStatsSchema.nullable()),
35726
+ getAllStats: method(external_exports.void(), external_exports.array(DeviceNetworkStatsSchema).readonly()),
35727
+ reportClientStats: method(external_exports.object({
35728
+ deviceId: external_exports.number(),
35729
+ rttMs: external_exports.number().min(0).max(6e4),
35730
+ jitterMs: external_exports.number().min(0).max(1e4),
35731
+ estimatedBandwidthKbps: external_exports.number().min(0).max(1e6),
35732
+ packetLossPercent: external_exports.number().min(0).max(100)
35733
+ }), external_exports.void(), { kind: "mutation" })
35734
+ },
35735
+ /** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
35736
+ mount: { kind: "server-provided" }
35737
+ };
35738
+ var TopologyServiceSchema = external_exports.object({
35739
+ addonId: external_exports.string(),
35740
+ capabilities: external_exports.array(external_exports.string()).readonly(),
35741
+ status: external_exports.string()
35742
+ });
35743
+ var TopologyProcessSchema = external_exports.object({
35744
+ pid: external_exports.number(),
35745
+ name: external_exports.string(),
35746
+ state: external_exports.string(),
35747
+ cpuPercent: external_exports.number(),
35748
+ memoryRss: external_exports.number(),
35749
+ uptimeSeconds: external_exports.number(),
35750
+ services: external_exports.array(TopologyServiceSchema).readonly(),
35751
+ groupId: external_exports.string().optional()
35752
+ });
35753
+ var TopologyCategoryAddonSchema = external_exports.object({
35754
+ id: external_exports.string(),
35755
+ status: external_exports.string(),
35756
+ cpuPercent: external_exports.number(),
35757
+ memoryRss: external_exports.number()
35758
+ });
35759
+ var TopologyCategorySchema = external_exports.object({
35760
+ category: external_exports.string(),
35761
+ total: external_exports.number(),
35762
+ healthy: external_exports.number(),
35763
+ addons: external_exports.array(TopologyCategoryAddonSchema).readonly()
35764
+ });
35765
+ var TopologyRootPackageSchema = external_exports.object({
35766
+ name: external_exports.string(),
35767
+ version: external_exports.string()
35768
+ });
35769
+ var TopologyNodeSchema = external_exports.object({
35770
+ id: external_exports.string(),
35771
+ name: external_exports.string(),
35772
+ hostname: external_exports.string(),
35773
+ platform: external_exports.string(),
35774
+ arch: external_exports.string(),
35775
+ cpuModel: external_exports.string().nullable(),
35776
+ cpuCores: external_exports.number(),
35777
+ memoryMB: external_exports.number(),
35778
+ engines: external_exports.array(external_exports.string()).readonly(),
35779
+ isHub: external_exports.boolean(),
35780
+ isOnline: external_exports.boolean(),
35781
+ cpuPercent: external_exports.number(),
35782
+ memoryPercent: external_exports.number(),
35783
+ uptime: external_exports.number(),
35784
+ lastSeen: external_exports.string(),
35785
+ localIps: external_exports.array(external_exports.string()).readonly(),
35786
+ addons: external_exports.array(external_exports.object({
35787
+ id: external_exports.string(),
35788
+ capabilities: external_exports.array(external_exports.string()).readonly(),
35789
+ status: external_exports.string()
35790
+ })).readonly(),
35791
+ processes: external_exports.array(TopologyProcessSchema).readonly(),
35792
+ categories: external_exports.array(TopologyCategorySchema).readonly(),
35793
+ rootPackage: TopologyRootPackageSchema.nullable()
35794
+ });
35795
+ var CapUsageEdgeSchema = external_exports.object({
35796
+ callerAddonId: external_exports.string(),
35797
+ providerAddonId: external_exports.string(),
35798
+ capName: external_exports.string(),
35544
35799
  callsPerMin: external_exports.number(),
35545
35800
  lastCallAtMs: external_exports.number()
35546
35801
  });
@@ -37008,169 +37263,6 @@ var ptzAutotrackCapability = {
37008
37263
  */
37009
37264
  durability: "session"
37010
37265
  };
37011
- var NavigationMoveCommandSchema = external_exports.object({
37012
- pan: external_exports.number().min(-1).max(1).optional(),
37013
- tilt: external_exports.number().min(-1).max(1).optional(),
37014
- speed: external_exports.number().min(0).max(1).optional()
37015
- });
37016
- var NavigationActionIdSchema = external_exports.enum([
37017
- "goHome",
37018
- "locate",
37019
- "spotClean",
37020
- "findPet",
37021
- "personFollow",
37022
- "stop",
37023
- "startClean",
37024
- "pauseClean",
37025
- "dockWash",
37026
- "autoEmpty",
37027
- "flashOn",
37028
- "flashOff"
37029
- ]);
37030
- var NavigationEntryKindSchema = external_exports.enum(["action", "sound"]);
37031
- var NavigationActionEntrySchema = external_exports.object({
37032
- id: external_exports.string(),
37033
- kind: NavigationEntryKindSchema,
37034
- label: external_exports.string(),
37035
- icon: external_exports.string(),
37036
- /** Present only on `kind:'sound'` entries — the id to pass to `playSound`. */
37037
- soundId: external_exports.number().int().optional(),
37038
- /** Per-device feature flag — render this entry only when true. */
37039
- enabled: external_exports.boolean()
37040
- });
37041
- var NavigationPointSchema = external_exports.object({
37042
- x: external_exports.number(),
37043
- y: external_exports.number()
37044
- });
37045
- var NavigationFeaturesSchema = external_exports.object({
37046
- move: external_exports.boolean(),
37047
- stop: external_exports.boolean(),
37048
- goToPoint: external_exports.boolean(),
37049
- runAction: external_exports.boolean(),
37050
- playSound: external_exports.boolean(),
37051
- light: external_exports.boolean(),
37052
- lightMode: external_exports.boolean()
37053
- });
37054
- var NavigationLightModeSchema = external_exports.enum(["auto", "manual"]);
37055
- var NavigationModeSchema = external_exports.enum([
37056
- "idle",
37057
- "cleaning",
37058
- "spot",
37059
- "following",
37060
- "goto",
37061
- "returning",
37062
- "paused",
37063
- "unknown"
37064
- ]);
37065
- var NavigationStatusSchema = external_exports.object({
37066
- mode: NavigationModeSchema,
37067
- following: external_exports.boolean(),
37068
- flash: external_exports.boolean(),
37069
- lightMode: NavigationLightModeSchema,
37070
- lightLevel: external_exports.number().min(40).max(100),
37071
- /** Ms epoch when the slice was last updated. */
37072
- lastChangedAt: external_exports.number()
37073
- });
37074
- var NavigationRuntimeStateSchema = NavigationStatusSchema.extend({ lastFetchedAt: external_exports.number() });
37075
- var navigationCapability = {
37076
- name: "navigation",
37077
- scope: "device",
37078
- deviceNative: true,
37079
- mode: "singleton",
37080
- deviceTypes: [DeviceType.Camera],
37081
- deviceConfig: { ui: {
37082
- kind: "widget",
37083
- widgetId: "host/navigation-panel",
37084
- tab: "navigation",
37085
- topTab: true,
37086
- label: "Navigation",
37087
- order: 0
37088
- } },
37089
- methods: {
37090
- /**
37091
- * Momentary drive nudge (the robot moves). `protected` — mirrors
37092
- * `ptz.continuousMove` so the Viewer navigation panel (and the PTZ-mimic
37093
- * path) works for any authenticated user, not admin-only. The UI sends
37094
- * these at ~1 Hz while a control is held; the provider forwards each one to
37095
- * a single drive write WITHOUT debouncing.
37096
- */
37097
- move: method(NavigationMoveCommandSchema.extend({ deviceId: external_exports.number() }), external_exports.void(), { kind: "mutation" }),
37098
- /** Halt all motion immediately (zero drive vector). */
37099
- stop: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.void(), { kind: "mutation" }),
37100
- /** Send the robot to a point on its live map. */
37101
- goToPoint: method(NavigationPointSchema.extend({ deviceId: external_exports.number() }), external_exports.void(), { kind: "mutation" }),
37102
- /**
37103
- * Enumerate the discrete controls THIS device supports (data-driven UI +
37104
- * PTZ mimic). Camera-probed subset of {@link NAVIGATION_ACTION_CATALOG}.
37105
- */
37106
- listActions: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.array(NavigationActionEntrySchema)),
37107
- /**
37108
- * Run one discrete action (a `kind:'action'` dictionary entry). Invalid /
37109
- * unsupported action ids are rejected by the provider.
37110
- */
37111
- runAction: method(external_exports.object({
37112
- deviceId: external_exports.number(),
37113
- actionId: NavigationActionIdSchema
37114
- }), external_exports.void(), { kind: "mutation" }),
37115
- /** Play a sound by its wire id (the `soundId` of a `kind:'sound'` entry). */
37116
- playSound: method(external_exports.object({
37117
- deviceId: external_exports.number(),
37118
- soundId: external_exports.number().int()
37119
- }), external_exports.void(), { kind: "mutation" }),
37120
- /**
37121
- * Turn the on-camera fill light on / off (the `OpenFullLight` control —
37122
- * works anytime, no active stream required).
37123
- */
37124
- setLightOn: method(external_exports.object({
37125
- deviceId: external_exports.number(),
37126
- on: external_exports.boolean()
37127
- }), external_exports.void(), { kind: "mutation" }),
37128
- /**
37129
- * Set the fill-light mode (auto vs manual). `manual` optionally carries the
37130
- * initial `level`. The auto/manual + level control is a CAMERA-service
37131
- * action that generally needs an active camera stream/monitor session — the
37132
- * UI shows the manual level slider ONLY when `mode === 'manual'`.
37133
- */
37134
- setLightMode: method(external_exports.object({
37135
- deviceId: external_exports.number(),
37136
- mode: NavigationLightModeSchema,
37137
- level: external_exports.number().min(40).max(100).optional()
37138
- }), external_exports.void(), { kind: "mutation" }),
37139
- /** Set the MANUAL fill-light level (40..100). Implies `manual` mode. */
37140
- setLightLevel: method(external_exports.object({
37141
- deviceId: external_exports.number(),
37142
- level: external_exports.number().min(40).max(100)
37143
- }), external_exports.void(), { kind: "mutation" }),
37144
- /**
37145
- * Per-device FEATURE-FLAG report for the general primitives — drives which
37146
- * controls the UI shows (the per-entry flags for the dictionary come back on
37147
- * `listActions`).
37148
- */
37149
- getFeatures: method(external_exports.object({ deviceId: external_exports.number() }), NavigationFeaturesSchema)
37150
- },
37151
- events: { onStatusChanged: { data: external_exports.object({
37152
- deviceId: external_exports.number(),
37153
- status: NavigationStatusSchema
37154
- }) } },
37155
- status: {
37156
- schema: NavigationStatusSchema,
37157
- kind: "push"
37158
- },
37159
- /**
37160
- * Runtime-state slice mirrored by the kernel. The navigation panel watches it
37161
- * for live mode / follow / flash changes.
37162
- */
37163
- runtimeState: NavigationRuntimeStateSchema,
37164
- /**
37165
- * Runtime-state durability: **session** — like `vacuum-control`, a restored
37166
- * `mode: cleaning` / `following: true` is a robot that is not actually doing
37167
- * that. The live handle re-publishes on connect.
37168
- *
37169
- * See `RuntimeStateDurability`. Enforced by
37170
- * `scripts/check-runtime-state-durability.ts`.
37171
- */
37172
- durability: "session"
37173
- };
37174
37266
  var rebootCapability = {
37175
37267
  name: "reboot",
37176
37268
  scope: "device",
@@ -44839,6 +44931,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
44839
44931
  addonId: null,
44840
44932
  access: "view"
44841
44933
  },
44934
+ "storage.listDrainProgress": {
44935
+ capName: "storage",
44936
+ capScope: "system",
44937
+ addonId: null,
44938
+ access: "view"
44939
+ },
44842
44940
  "storage.listLocationDeclarations": {
44843
44941
  capName: "storage",
44844
44942
  capScope: "system",
@@ -44983,6 +45081,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
44983
45081
  addonId: null,
44984
45082
  access: "view"
44985
45083
  },
45084
+ "storageOccupancy.getOccupancy": {
45085
+ capName: "storage-occupancy",
45086
+ capScope: "system",
45087
+ addonId: null,
45088
+ access: "view"
45089
+ },
44986
45090
  "storageProvider.abortUpload": {
44987
45091
  capName: "storage-provider",
44988
45092
  capScope: "system",