@camstack/types 1.2.162 → 1.2.163

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/addon.js +4 -3
  2. package/dist/addon.mjs +4 -3
  3. package/dist/capabilities/alerts.cap.d.ts +5 -5
  4. package/dist/capabilities/index.d.ts +7 -5
  5. package/dist/capabilities/notifier.cap.d.ts +3 -3
  6. package/dist/capabilities/pet-feeder.cap.d.ts +4 -4
  7. package/dist/capabilities/snapshot.cap.d.ts +2 -2
  8. package/dist/capabilities/storage-occupancy.cap.d.ts +72 -0
  9. package/dist/capabilities/storage-provider.cap.d.ts +110 -0
  10. package/dist/capabilities/storage.cap.d.ts +43 -0
  11. package/dist/capabilities/stream-broker.cap.d.ts +1 -1
  12. package/dist/enums/event-category.d.ts +13 -0
  13. package/dist/enums.js +1 -1
  14. package/dist/enums.mjs +1 -1
  15. package/dist/{event-category-b8kBSOTT.js → event-category-BVo6ta6_.js} +13 -0
  16. package/dist/{event-category-zAv7pMUz.mjs → event-category-CnLqLOKs.mjs} +13 -0
  17. package/dist/generated/addon-api.d.ts +22 -0
  18. package/dist/generated/capability-router-map.d.ts +5 -2
  19. package/dist/generated/collection-array-methods.d.ts +1 -1
  20. package/dist/generated/method-access-map.d.ts +1 -1
  21. package/dist/generated/system-proxy.d.ts +1 -1
  22. package/dist/index.d.ts +4 -2
  23. package/dist/index.js +881 -552
  24. package/dist/index.mjs +862 -553
  25. package/dist/interfaces/event-bus.d.ts +19 -0
  26. package/dist/interfaces/storage-location-mode.d.ts +135 -0
  27. package/dist/interfaces/storage-location.d.ts +54 -6
  28. package/dist/node.d.ts +13 -11
  29. package/dist/node.js +935 -876
  30. package/dist/node.mjs +936 -879
  31. package/dist/{sleep-DDIFZGbc.js → sleep-BZtO-eFY.js} +1 -1
  32. package/dist/{sleep-Cfij6Jj9.mjs → sleep-Bd-Y4RUt.mjs} +1 -1
  33. package/dist/storage/physical-root.d.ts +25 -0
  34. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_event_category = require("./event-category-b8kBSOTT.js");
3
- const require_sleep = require("./sleep-DDIFZGbc.js");
2
+ const require_event_category = require("./event-category-BVo6ta6_.js");
3
+ const require_sleep = require("./sleep-BZtO-eFY.js");
4
4
  const require_canonical_hash = require("./canonical-hash-DNV8S5ET.js");
5
5
  const require_enums = require("./enums.js");
6
6
  const require_err_msg = require("./err-msg-COpsHMw2.js");
@@ -2643,6 +2643,158 @@ var RECOGNITION_TYPES = [
2643
2643
  "custom"
2644
2644
  ];
2645
2645
  //#endregion
2646
+ //#region src/interfaces/storage-location-mode.ts
2647
+ /**
2648
+ * The storage-location STATE MODEL (D385) — one typed state, one policy module.
2649
+ *
2650
+ * A location's state used to be split across two authorities: the typed
2651
+ * `enabled` field (THE write switch since D383) and an untyped `config.readOnly`
2652
+ * key. They did not mean the same thing — `enabled: false` was still evicted
2653
+ * under disk pressure while `config.readOnly` was deliberately excluded — and
2654
+ * neither name said which. Every consumer re-derived the difference, and the
2655
+ * three questions that actually matter were answered in six places.
2656
+ *
2657
+ * This module is the ONLY place in the repo allowed to interpret the state. It
2658
+ * answers three questions and nothing else:
2659
+ *
2660
+ * - may this location be WRITTEN to? {@link modeMayWrite}
2661
+ * - may this location be READ? {@link modeMayRead}
2662
+ * - what is its eviction policy? {@link evictionPolicyForMode}
2663
+ *
2664
+ * | mode | write | read | eviction |
2665
+ * | ---------- | ----- | ---- | ------------------------------ |
2666
+ * | `active` | yes | yes | `normal` (pressure + usage cap) |
2667
+ * | `readonly` | no | yes | `never` |
2668
+ * | `drain` | no | yes | `drain` (paced, until empty) |
2669
+ * | `disabled` | no | no | `never` |
2670
+ *
2671
+ * `scripts/check-storage-location-mode-single-owner.ts` fails the build when
2672
+ * anything outside this module reads `config['readOnly']` or compares `enabled`
2673
+ * directly. A rule nothing checks has already been broken somewhere.
2674
+ */
2675
+ var STORAGE_LOCATION_MODES = [
2676
+ "active",
2677
+ "readonly",
2678
+ "drain",
2679
+ "disabled"
2680
+ ];
2681
+ /**
2682
+ * The one typed state of a storage location. Authoritative Zod schema — the TS
2683
+ * alias below is `z.infer<>` of it, never a second spelling.
2684
+ */
2685
+ var StorageLocationModeSchema = zod.z.enum(STORAGE_LOCATION_MODES);
2686
+ /**
2687
+ * What disk-pressure and usage-cap relief may do to a location.
2688
+ *
2689
+ * - `normal` — the existing behaviour: free-percent guard plus `maxUsedGb`.
2690
+ * - `never` — excluded entirely. A retiring disk the operator is copying off
2691
+ * must not race the relocate mover, and a disabled one is not the
2692
+ * system's to erase.
2693
+ * - `drain` — a monotonically descending effective usage cap, paced to what
2694
+ * the ACTIVE locations of the same class gained (D386). This is
2695
+ * the only policy that shrinks a location the system is not
2696
+ * writing to.
2697
+ */
2698
+ var StorageEvictionPolicySchema = zod.z.enum([
2699
+ "normal",
2700
+ "never",
2701
+ "drain"
2702
+ ]);
2703
+ /**
2704
+ * The legacy untyped drain key. Named ONCE, here, so the guard has exactly one
2705
+ * sanctioned reader and the string never appears anywhere else.
2706
+ */
2707
+ var LEGACY_READ_ONLY_CONFIG_KEY = "readOnly";
2708
+ /** Is this mode a write target? Only `active` is. */
2709
+ function modeMayWrite(mode) {
2710
+ return mode === "active";
2711
+ }
2712
+ /** May this mode be read (playback, timeline, scrub, relocate source)? */
2713
+ function modeMayRead(mode) {
2714
+ return mode !== "disabled";
2715
+ }
2716
+ /** What eviction may do here. See {@link StorageEvictionPolicy}. */
2717
+ function evictionPolicyForMode(mode) {
2718
+ switch (mode) {
2719
+ case "active": return "normal";
2720
+ case "drain": return "drain";
2721
+ case "readonly":
2722
+ case "disabled": return "never";
2723
+ }
2724
+ }
2725
+ /**
2726
+ * The mode a LEGACY row implies, or `null` when it implies nothing — the row is
2727
+ * already stamped, or it carried neither flag.
2728
+ *
2729
+ * Both legacy flags fold to `readonly`, which is the CONSERVATIVE direction: a
2730
+ * state change must never start deleting footage on its own, and it must never
2731
+ * make footage that was still being served disappear. `enabled: false` used to
2732
+ * leave the location evictable under pressure; folding it to `readonly` stops
2733
+ * that, which is a strictly safer answer than the one it replaces.
2734
+ */
2735
+ function legacyModeOf(location) {
2736
+ if (location.mode !== void 0) return null;
2737
+ if (location.config["readOnly"] === true) return "readonly";
2738
+ if (location.enabled === false) return "readonly";
2739
+ return null;
2740
+ }
2741
+ /**
2742
+ * The state of a location, stamped or folded. THE one interpretation: a row
2743
+ * that predates D385 is never ambiguous, and a stamped `mode` always wins over
2744
+ * whatever the legacy pair still says.
2745
+ */
2746
+ function resolveLocationMode(location) {
2747
+ return (isStorageLocationMode(location.mode) ? location.mode : void 0) ?? legacyModeOf(location) ?? "active";
2748
+ }
2749
+ /** Is this one of the four states? The stamped value crosses a wire, and a
2750
+ * value nobody defined must not be rendered as if it were a state. */
2751
+ function isStorageLocationMode(value) {
2752
+ return STORAGE_LOCATION_MODES.some((mode) => mode === value);
2753
+ }
2754
+ /** May this location be written to? */
2755
+ function mayWriteToLocation(location) {
2756
+ return modeMayWrite(resolveLocationMode(location));
2757
+ }
2758
+ /** May this location be read? A `disabled` one may not — and that is an
2759
+ * operator CHOICE, which callers must report as unavailable rather than as an
2760
+ * unknown-location fault. */
2761
+ function mayReadLocation(location) {
2762
+ return modeMayRead(resolveLocationMode(location));
2763
+ }
2764
+ /** What eviction may do to this location. */
2765
+ function evictionPolicyOfLocation(location) {
2766
+ return evictionPolicyForMode(resolveLocationMode(location));
2767
+ }
2768
+ /**
2769
+ * The config blob with the retired drain key removed. Returns the SAME object
2770
+ * when there was nothing to strip, so a caller can tell "changed" from
2771
+ * "unchanged" by identity and skip a pointless persist.
2772
+ */
2773
+ function strippedOfLegacyReadOnly(config) {
2774
+ if (!("readOnly" in config)) return config;
2775
+ const next = { ...config };
2776
+ delete next[LEGACY_READ_ONLY_CONFIG_KEY];
2777
+ return next;
2778
+ }
2779
+ /**
2780
+ * Set a location's mode, and with it everything that must agree with it.
2781
+ *
2782
+ * `enabled` stays readable for one release as a DERIVED value (`mode ===
2783
+ * 'active'`) so consumers that have not moved yet keep working, and the legacy
2784
+ * `config.readOnly` key is DELETED. This function is the only way to write
2785
+ * either, which is what makes it impossible for the two to disagree.
2786
+ *
2787
+ * Returns a new object — the input is never mutated.
2788
+ */
2789
+ function withLocationMode(location, mode) {
2790
+ return {
2791
+ ...location,
2792
+ mode,
2793
+ enabled: modeMayWrite(mode),
2794
+ config: strippedOfLegacyReadOnly(location.config)
2795
+ };
2796
+ }
2797
+ //#endregion
2646
2798
  //#region src/interfaces/storage-location.ts
2647
2799
  /**
2648
2800
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -2710,6 +2862,21 @@ var StorageLocationSchema = zod.z.object({
2710
2862
  * stops existing rather than being re-derived on every read.
2711
2863
  */
2712
2864
  enabled: zod.z.boolean().optional(),
2865
+ /**
2866
+ * THE state of this location (D385), and the only authority on what may be
2867
+ * written, read or evicted here. Interpreted in exactly one place —
2868
+ * `storage-location-mode.ts` — which also folds the legacy
2869
+ * `enabled` / `config.readOnly` pair into a mode so an old row is never
2870
+ * ambiguous.
2871
+ *
2872
+ * OPTIONAL only for the wire and for rows written before D385: absence is
2873
+ * resolved by `resolveLocationMode`, and the orchestrator stamps every
2874
+ * unstamped row ONCE at hydrate so absence stops existing rather than being
2875
+ * re-derived on every read. `enabled` survives one release as a DERIVED
2876
+ * mirror (`mode === 'active'`); `withLocationMode` is the only writer of
2877
+ * either, so the two cannot disagree.
2878
+ */
2879
+ mode: StorageLocationModeSchema.optional(),
2713
2880
  /** COMPUTED at read time by the orchestrator (statfs of the backing volume
2714
2881
  * for node-local locations it can reach) — never persisted, absent when the
2715
2882
  * volume is remote/unreachable. The single capacity truth every UI reads. */
@@ -2717,6 +2884,25 @@ var StorageLocationSchema = zod.z.object({
2717
2884
  totalBytes: zod.z.number(),
2718
2885
  availableBytes: zod.z.number()
2719
2886
  }).nullable().optional(),
2887
+ /**
2888
+ * How much of that volume CamStack ITSELF holds on this location (D388) —
2889
+ * COMPUTED at read time from the `storage-occupancy` providers' own figures,
2890
+ * never persisted, never a filesystem walk.
2891
+ *
2892
+ * **ABSENT MEANS UNKNOWN, never zero.** No provider has reported for this
2893
+ * location yet — nobody stores here, the owning addon is down, or the first
2894
+ * refresh has not completed. A UI must omit the segment rather than draw it
2895
+ * at zero, which would claim we occupy nothing (D315). It is an OBJECT and
2896
+ * not a bare number precisely so that a `?? 0` on the consuming side has to
2897
+ * be spelled out loud instead of appearing by accident.
2898
+ *
2899
+ * `measuredAtMs` is the OLDEST contributing measurement, so it is honest
2900
+ * about the whole figure rather than about its freshest part.
2901
+ */
2902
+ owned: zod.z.object({
2903
+ bytes: zod.z.number().int().nonnegative(),
2904
+ measuredAtMs: zod.z.number().int().nonnegative()
2905
+ }).optional(),
2720
2906
  createdAt: zod.z.number(),
2721
2907
  updatedAt: zod.z.number()
2722
2908
  });
@@ -2732,16 +2918,45 @@ var StorageLocationSchema = zod.z.object({
2732
2918
  * the repo may read it; `scripts/check-no-storage-default.ts` enforces that.
2733
2919
  */
2734
2920
  var LegacyStorageLocationDefaultSchema = zod.z.object({ isDefault: zod.z.boolean().optional() });
2921
+ function isLocationEnabled(location) {
2922
+ return mayWriteToLocation({
2923
+ ...location,
2924
+ config: location.config ?? {}
2925
+ });
2926
+ }
2735
2927
  /**
2736
- * Is this location a write target? THE one place that decides what an absent
2737
- * `enabled` means on a persisted row — legacy rows predate the flag and were
2738
- * active, so absence is enabled. Every consumer asks here rather than spelling
2739
- * `enabled !== false` again, so the tri-state has exactly one interpretation.
2928
+ * Bytes in one GB, for every storage figure an operator types.
2929
+ *
2930
+ * BINARY (1024³), everywhere. The repo had both: the orchestrator's
2931
+ * `maxUsedGb` → bytes conversion used 1024³ while the recorder's placement
2932
+ * headroom used 10⁹ for the SAME stored key, so a location with a cap set was
2933
+ * silently 7.4% off depending on which side of the pipe asked. The persisted
2934
+ * values were entered against the binary unit and the admin UI reads it, so
2935
+ * that is the one that stays. Every GB knob — `maxUsedGb`, `minFreeGb` —
2936
+ * converts through {@link gbToBytes} and nowhere else.
2740
2937
  */
2741
- function isLocationEnabled(location) {
2742
- return location.enabled !== false;
2938
+ var STORAGE_BYTES_PER_GB = 1024 ** 3;
2939
+ /** GB → bytes, binary. Fractional GB is admitted and floored. */
2940
+ function gbToBytes(gb) {
2941
+ return Math.floor(gb * STORAGE_BYTES_PER_GB);
2743
2942
  }
2744
2943
  /**
2944
+ * How far a `drain` has got (D386) — the read a UI renders, and nothing more.
2945
+ *
2946
+ * `estimatedEmptyAtMs` is derived from the growth the ratchet has actually
2947
+ * OBSERVED and is `null` when it has observed none. Never a fabricated date: a
2948
+ * drain with no observed growth has no honest ETA, and inventing one is how an
2949
+ * operator learns not to believe the screen.
2950
+ */
2951
+ var StorageDrainProgressSchema = zod.z.object({
2952
+ locationId: zod.z.string(),
2953
+ startedAtMs: zod.z.number(),
2954
+ startBytes: zod.z.number(),
2955
+ bytesRemaining: zod.z.number(),
2956
+ drained: zod.z.boolean(),
2957
+ estimatedEmptyAtMs: zod.z.number().nullable()
2958
+ });
2959
+ /**
2745
2960
  * Reference accepted by consumer-facing `api.storage.*` calls.
2746
2961
  * Either:
2747
2962
  * - a `StorageLocationType` (e.g. `'backups'`) → the sole location of that type
@@ -23201,6 +23416,12 @@ var storageCapability = {
23201
23416
  kind: "mutation",
23202
23417
  auth: "admin"
23203
23418
  }),
23419
+ /**
23420
+ * How far each draining location has got (D386). A pure READ of what the
23421
+ * last pressure sweep computed — it starts no work, and a location that is
23422
+ * not draining simply does not appear.
23423
+ */
23424
+ listDrainProgress: require_sleep.method(zod.z.void(), zod.z.array(StorageDrainProgressSchema).readonly()),
23204
23425
  testLocation: require_sleep.method(zod.z.object({ id: zod.z.string() }), zod.z.object({
23205
23426
  ok: zod.z.boolean(),
23206
23427
  error: zod.z.string().optional()
@@ -23353,6 +23574,73 @@ var storageMigrationCapability = {
23353
23574
  }
23354
23575
  };
23355
23576
  //#endregion
23577
+ //#region src/capabilities/storage-occupancy.cap.ts
23578
+ /**
23579
+ * `storage-occupancy` — how many bytes an addon actually HOLDS on a storage
23580
+ * location (D388).
23581
+ *
23582
+ * ## Why this is not `storage-evictable`
23583
+ *
23584
+ * `storage-evictable.getEvictableUsage` looks like the same question and is
23585
+ * not, in two ways that both matter and both bite hardest on the locations an
23586
+ * operator most wants a figure for:
23587
+ *
23588
+ * - it reports the whole eviction DOMAIN, not the location. `recordings:default`
23589
+ * and `recordingsLow:default` deliberately share one root and evict as one
23590
+ * oldest-first pool, so both answer with the SAME combined total. As an
23591
+ * occupancy figure that double-counts the disk.
23592
+ * - it reports ZERO for a location whose eviction policy is `never` (D385) —
23593
+ * a `readonly` or `disabled` disk. Those are exactly the disks an operator
23594
+ * is retiring and staring at.
23595
+ *
23596
+ * So this is its own contract with its own quantity, and the quantity is
23597
+ * OCCUPIED: every byte the addon holds on that location, whether or not it
23598
+ * would ever be willing to delete it. A provider that can only answer
23599
+ * "evictable" must not register here — a number that silently means different
23600
+ * things per class is worse than no number.
23601
+ *
23602
+ * ## Absence is an answer
23603
+ *
23604
+ * A location nobody reports for is UNKNOWN, never zero (D315). The orchestrator
23605
+ * stamps `StorageLocation.owned` only for locations it has a report for, and
23606
+ * the field is an OBJECT rather than a bare number so that a `?? 0` on the
23607
+ * consuming side has to be written out loud instead of appearing by accident.
23608
+ *
23609
+ * `internal: true` — consumed by the orchestrator's `listLocations` stamp, never
23610
+ * a public client surface. Clients read the stamped `StorageLocation.owned`.
23611
+ */
23612
+ /** One provider's occupancy answer for one location. */
23613
+ var StorageOccupancyReportSchema = zod.z.object({
23614
+ locationId: zod.z.string(),
23615
+ /** Bytes this provider holds on THAT location — not its eviction domain, and
23616
+ * not net of what it is willing to delete. */
23617
+ ownedBytes: zod.z.number().int().nonnegative(),
23618
+ /** When the provider last actually measured this. The orchestrator carries it
23619
+ * through so a UI can say how old the figure is instead of implying "now". */
23620
+ measuredAtMs: zod.z.number().int().nonnegative()
23621
+ });
23622
+ var storageOccupancyCapability = {
23623
+ name: "storage-occupancy",
23624
+ scope: "system",
23625
+ mode: "collection",
23626
+ internal: true,
23627
+ methods: {
23628
+ /**
23629
+ * Occupancy for the given locations, in ONE round trip.
23630
+ *
23631
+ * A provider answers only for the locations it actually holds bytes on and
23632
+ * OMITS the rest — an omitted location is "I hold nothing measurable here",
23633
+ * which the orchestrator merges as a contribution of nothing rather than as
23634
+ * a claim that the location is empty. Only a location no provider reports
23635
+ * at all stays unknown.
23636
+ *
23637
+ * This must be CHEAP and must never walk a filesystem: it is on the admin
23638
+ * UI's `listLocations` path. The owner keeps its own figure fresh (D224) and
23639
+ * answers from what it already has.
23640
+ */
23641
+ getOccupancy: require_sleep.method(zod.z.object({ locationIds: zod.z.array(zod.z.string()).readonly() }), zod.z.array(StorageOccupancyReportSchema).readonly(), { auth: "admin" }) }
23642
+ };
23643
+ //#endregion
23356
23644
  //#region src/capabilities/storage-provider.cap.ts
23357
23645
  var ProviderInfoSchema = zod.z.discriminatedUnion("shouldSaveDiskSpace", [zod.z.object({
23358
23646
  providerId: zod.z.string().min(1),
@@ -25769,125 +26057,6 @@ onStatusChanged: { data: zod.z.object({
25769
26057
  volatileStateFields: ["lastUpdated"]
25770
26058
  };
25771
26059
  //#endregion
25772
- //#region src/capabilities/network-link.cap.ts
25773
- /**
25774
- * How a device reaches the network, and how well.
25775
- *
25776
- * NOT `connectivity`: that cap is an UPSTREAM system's binary view of whether
25777
- * an entity is reachable (a Home Assistant `binary_sensor` with
25778
- * `device_class: connectivity`). This one is the device's OWN link — the
25779
- * medium it is on and the signal it sees — the way `battery` is the device's
25780
- * own charge.
25781
- *
25782
- * `'wifi'`, `'ethernet'` and `'cellular'` are links the firmware actually
25783
- * reported. `'unknown'` is the ABSENCE of an answer — the provider has
25784
- * registered the capability but has not read the link yet (D315: unknown is
25785
- * not empty). Consumers skip it rather than draw a wire or a bar.
25786
- */
25787
- var NETWORK_LINK_TYPES = [
25788
- "wifi",
25789
- "ethernet",
25790
- "cellular",
25791
- "unknown"
25792
- ];
25793
- /**
25794
- * Network-link snapshot. Same shape for every provider (a Reolink wifi
25795
- * camera, a Home Assistant device with a signal-strength sensor, a Tapo
25796
- * plug): one slice under `device.runtimeState['network-link']`, one badge,
25797
- * one Home Assistant projection.
25798
- */
25799
- var NetworkLinkStatusSchema = zod.z.object({
25800
- /** The link the device is on. `'unknown'` = not read yet, not "no link". */
25801
- type: zod.z.enum(NETWORK_LINK_TYPES),
25802
- /**
25803
- * Link quality, 0..100 inclusive, normalised by the provider from whatever
25804
- * the firmware reports (bars, RSSI, a vendor scale). **`null` means NOT
25805
- * KNOWN or NOT APPLICABLE** — a wired link has no signal, and a wireless
25806
- * one whose reading has not landed must not be drawn at 0 %. Consumers
25807
- * SKIP a null rather than coerce it.
25808
- */
25809
- signalPercent: zod.z.number().min(0).max(100).nullable(),
25810
- /** Raw received signal strength in dBm, when the firmware reports one. */
25811
- rssiDbm: zod.z.number().optional(),
25812
- /** Network name of a wireless link, when the firmware reports it. */
25813
- ssid: zod.z.string().optional(),
25814
- /** Ms epoch of the last observation. Lets consumers reason about freshness. */
25815
- lastUpdated: zod.z.number()
25816
- });
25817
- /** The slice a provider seeds before its first read: nothing is known yet. */
25818
- var NETWORK_LINK_UNKNOWN = {
25819
- type: "unknown",
25820
- signalPercent: null,
25821
- lastUpdated: 0
25822
- };
25823
- /**
25824
- * Normalise a signal the firmware reports as BARS (0..`maxBars`) to 0..100.
25825
- * Out-of-range or non-finite input is not a reading: `null`.
25826
- */
25827
- function signalPercentFromBars(bars, maxBars) {
25828
- if (bars === void 0 || !Number.isFinite(bars) || maxBars <= 0) return null;
25829
- if (bars < 0 || bars > maxBars) return null;
25830
- return Math.round(bars / maxBars * 100);
25831
- }
25832
- /**
25833
- * Normalise an RSSI in dBm to 0..100 on the usual wifi scale: -100 dBm and
25834
- * below is 0 %, -50 dBm and above is 100 %, linear between. Non-finite or
25835
- * positive input is not an RSSI: `null`.
25836
- */
25837
- function signalPercentFromRssi(rssiDbm) {
25838
- if (rssiDbm === void 0 || !Number.isFinite(rssiDbm) || rssiDbm > 0) return null;
25839
- return Math.round((Math.min(-50, Math.max(-100, rssiDbm)) + 100) * 2);
25840
- }
25841
- var networkLinkCapability = {
25842
- name: "network-link",
25843
- scope: "device",
25844
- deviceNative: true,
25845
- mode: "singleton",
25846
- deviceTypes: [
25847
- require_sleep.DeviceType.Camera,
25848
- require_sleep.DeviceType.Sensor,
25849
- require_sleep.DeviceType.Button,
25850
- require_sleep.DeviceType.Switch,
25851
- require_sleep.DeviceType.Light,
25852
- require_sleep.DeviceType.Lock,
25853
- require_sleep.DeviceType.Siren
25854
- ],
25855
- methods: {},
25856
- events: {
25857
- /**
25858
- * Emitted whenever the cached status changes (a link switch, a signal
25859
- * reading that moved). Mirrored on the parent chain by the
25860
- * DeviceEventPropagator like `battery.onStatusChanged`.
25861
- */
25862
- onStatusChanged: { data: zod.z.object({
25863
- deviceId: zod.z.number(),
25864
- status: NetworkLinkStatusSchema
25865
- }) } },
25866
- status: {
25867
- schema: NetworkLinkStatusSchema,
25868
- kind: "push",
25869
- empty: NETWORK_LINK_UNKNOWN
25870
- },
25871
- /**
25872
- * Runtime-state slice — every provider stores the same shape under
25873
- * `device.runtimeState['network-link']`, read once by the badge and the
25874
- * Home Assistant projector regardless of the driver.
25875
- */
25876
- runtimeState: NetworkLinkStatusSchema,
25877
- /**
25878
- * Runtime-state durability: **restored** — a link reading is slow to
25879
- * change and a sleeping battery camera may not report for hours; the
25880
- * restored slice is what the badge shows until the next read.
25881
- *
25882
- * See `RuntimeStateDurability`. Enforced by
25883
- * `scripts/check-runtime-state-durability.ts`.
25884
- */
25885
- durability: "restored",
25886
- /** Clock fields: written, but excluded from the compare that decides
25887
- * whether persisting is worth a SQLite commit. */
25888
- volatileStateFields: ["lastUpdated"]
25889
- };
25890
- //#endregion
25891
26060
  //#region src/capabilities/binary.cap.ts
25892
26061
  /**
25893
26062
  * Generic boolean sensor — last-resort fallback when no domain-
@@ -29897,6 +30066,550 @@ var nativeObjectDetectionCapability = {
29897
30066
  volatileStateFields: ["lastFetchedAt"]
29898
30067
  };
29899
30068
  //#endregion
30069
+ //#region src/capabilities/navigation.cap.ts
30070
+ /**
30071
+ * `navigation` — a device-scoped capability that natively expresses the FULL
30072
+ * navigation / action surface of a robot that DRIVES ITSELF and carries an
30073
+ * on-board camera (the Dreame robot-vacuum camera is the first provider).
30074
+ *
30075
+ * Why a NEW cap rather than overloading `ptz`:
30076
+ * - `ptz` moves a *gimbal* on a fixed camera (pan/tilt/zoom of the lens). A
30077
+ * robot vacuum has no gimbal — the whole chassis drives, turns and spins.
30078
+ * The two are different physical models: PTZ is absolute-position + presets,
30079
+ * navigation is momentary drive nudges + discrete robot ACTIONS
30080
+ * (dock / spot-clean / follow-pet / go-to-point / …).
30081
+ * - This cap is the SOURCE OF TRUTH. Two consumers adapt from it rather than
30082
+ * the reverse:
30083
+ * 1. a native CamStack navigation panel (data-driven from `listActions`
30084
+ * / `getOptions`), and
30085
+ * 2. the PTZ surface — a thin adapter mimics `ptz` from `navigation` so a
30086
+ * robot camera shows up in the existing PTZ control path without every
30087
+ * PTZ provider learning about robots. The mapping lives in the adapter,
30088
+ * not here (see the addon design note):
30089
+ * ptz.continuousMove({pan,tilt}) → navigation.move({pan,tilt})
30090
+ * ptz.stop() → navigation.stop()
30091
+ * ptz.goHome() → navigation.runAction('goHome')
30092
+ * ptz.getPresets() → navigation.listActions() (id→preset)
30093
+ * ptz.goToPreset(id) → navigation.runAction(id)
30094
+ *
30095
+ * ## Continuous drive
30096
+ *
30097
+ * `move` is MOMENTARY. Fluid navigation comes from the UI (or the PTZ adapter)
30098
+ * sending `move({pan,tilt})` REPEATEDLY at ~1 Hz while a direction is held, and
30099
+ * one `stop()` on release — exactly like the robot app's remote-drive joystick.
30100
+ * The provider forwards EACH `move` to one drive write; it must NOT debounce or
30101
+ * coalesce them. The UI owns the cadence.
30102
+ *
30103
+ * ## The action dictionary
30104
+ *
30105
+ * The discrete controls (dock / locate / spot-clean / follow / flash / sounds)
30106
+ * are a DATA-DRIVEN dictionary the cap exposes via `listActions()`. Each entry
30107
+ * carries `{ id, kind, label, icon }` (plus `soundId` for sound entries) so the
30108
+ * native panel AND the PTZ mimic render buttons WITHOUT hardcoding a
30109
+ * vendor-specific list. `kind: 'action'` entries are triggered with
30110
+ * `runAction({ actionId })`; `kind: 'sound'` entries with `playSound({ soundId })`
30111
+ * (the entry carries the `soundId` to pass). The general primitives — `move`,
30112
+ * `stop`, `goToPoint` — stay as first-class methods, not dictionary entries.
30113
+ *
30114
+ * NOTE (provider wiring): the first provider (Dreame) drives this with the RAW
30115
+ * `callAction(siid,aiid,in)` / `setProperty({siid,piid,value})` MIoT primitives
30116
+ * that the currently-published `@apocaliss92/nodedreame` already exposes on
30117
+ * every device handle. A future nodedreame publish adds a typed
30118
+ * `DreameCameraController` (drive / playPetSound / spotClean / findPet / …); the
30119
+ * provider can then swap the raw calls for the typed methods with no change to
30120
+ * THIS contract.
30121
+ */
30122
+ /**
30123
+ * A momentary drive nudge. The robot MOVES (no gimbal) for as long as the caller
30124
+ * keeps sending nudges (~1 Hz); an explicit `stop` (or letting the nudges lapse)
30125
+ * halts it.
30126
+ *
30127
+ * - `pan` — turn: negative = left, positive = right, 0 = straight.
30128
+ * - `tilt` — throttle: positive = forward, negative = spin / turn-around.
30129
+ * - `speed` — optional intensity hint [0, 1]; the provider may scale the drive
30130
+ * vector by it (drivers without proportional drive ignore it).
30131
+ *
30132
+ * `pan` / `tilt` are normalized [-1, 1]. Both optional so a caller can nudge one
30133
+ * axis alone; an all-undefined nudge is a no-op.
30134
+ */
30135
+ var NavigationMoveCommandSchema = zod.z.object({
30136
+ pan: zod.z.number().min(-1).max(1).optional(),
30137
+ tilt: zod.z.number().min(-1).max(1).optional(),
30138
+ speed: zod.z.number().min(0).max(1).optional()
30139
+ });
30140
+ /**
30141
+ * The enumerated discrete actions a navigation-capable robot can perform via
30142
+ * `runAction`. This is the CLOSED vocabulary; a given device advertises the
30143
+ * subset it supports through `listActions`. Sounds are NOT here — they go through
30144
+ * `playSound` (see the `sound` dictionary entries).
30145
+ */
30146
+ var NavigationActionIdSchema = zod.z.enum([
30147
+ "goHome",
30148
+ "locate",
30149
+ "spotClean",
30150
+ "findPet",
30151
+ "personFollow",
30152
+ "stop",
30153
+ "startClean",
30154
+ "pauseClean",
30155
+ "dockWash",
30156
+ "autoEmpty",
30157
+ "flashOn",
30158
+ "flashOff"
30159
+ ]);
30160
+ /** Whether a dictionary entry is a `runAction` action or a `playSound` sound. */
30161
+ var NavigationEntryKindSchema = zod.z.enum(["action", "sound"]);
30162
+ /**
30163
+ * One entry in the navigation action dictionary — the DATA-DRIVEN unit both the
30164
+ * native panel and the PTZ mimic render as a button.
30165
+ *
30166
+ * - `id` — stable id. For `kind:'action'` it is a {@link NavigationActionId}
30167
+ * (pass to `runAction`); for `kind:'sound'` it is a namespaced id
30168
+ * (`sound:meow`) whose `soundId` is passed to `playSound`.
30169
+ * - `icon` — icon HINT (lucide-style name; the UI maps it to its own set).
30170
+ * - `label` — operator-facing English label.
30171
+ * - `soundId` — wire sound id, present only on `kind:'sound'` entries.
30172
+ * - `enabled` — per-device FEATURE FLAG. `listActions` reports it so the UI /
30173
+ * PTZ render ONLY enabled entries. Data-driven: the provider
30174
+ * flips it from config, never by editing code.
30175
+ */
30176
+ var NavigationActionEntrySchema = zod.z.object({
30177
+ id: zod.z.string(),
30178
+ kind: NavigationEntryKindSchema,
30179
+ label: zod.z.string(),
30180
+ icon: zod.z.string(),
30181
+ /** Present only on `kind:'sound'` entries — the id to pass to `playSound`. */
30182
+ soundId: zod.z.number().int().optional(),
30183
+ /** Per-device feature flag — render this entry only when true. */
30184
+ enabled: zod.z.boolean()
30185
+ });
30186
+ /**
30187
+ * Canonical dictionary of every navigation control (discrete actions + sounds)
30188
+ * with its default label + icon hint. A provider filters this to the subset a
30189
+ * device supports (and may override a label). Exported so the provider AND the
30190
+ * UI share ONE list instead of re-deriving labels / ids independently.
30191
+ *
30192
+ * Icons are lucide-style hints; the viewer maps them to its own icon set. Every
30193
+ * catalog default is `enabled: true`; a provider overrides per-device from
30194
+ * config (data-driven — enabling / disabling one later is a config change, not a
30195
+ * code change).
30196
+ */
30197
+ var NAVIGATION_ACTION_CATALOG = [
30198
+ {
30199
+ id: "startClean",
30200
+ kind: "action",
30201
+ label: "Start cleaning",
30202
+ icon: "play",
30203
+ enabled: true
30204
+ },
30205
+ {
30206
+ id: "pauseClean",
30207
+ kind: "action",
30208
+ label: "Pause cleaning",
30209
+ icon: "pause",
30210
+ enabled: true
30211
+ },
30212
+ {
30213
+ id: "stop",
30214
+ kind: "action",
30215
+ label: "Stop",
30216
+ icon: "hand",
30217
+ enabled: true
30218
+ },
30219
+ {
30220
+ id: "goHome",
30221
+ kind: "action",
30222
+ label: "Return to dock",
30223
+ icon: "home",
30224
+ enabled: true
30225
+ },
30226
+ {
30227
+ id: "locate",
30228
+ kind: "action",
30229
+ label: "Locate",
30230
+ icon: "bell",
30231
+ enabled: true
30232
+ },
30233
+ {
30234
+ id: "spotClean",
30235
+ kind: "action",
30236
+ label: "Spot clean here",
30237
+ icon: "crosshair",
30238
+ enabled: true
30239
+ },
30240
+ {
30241
+ id: "findPet",
30242
+ kind: "action",
30243
+ label: "Find pet",
30244
+ icon: "paw-print",
30245
+ enabled: true
30246
+ },
30247
+ {
30248
+ id: "personFollow",
30249
+ kind: "action",
30250
+ label: "Follow me",
30251
+ icon: "footprints",
30252
+ enabled: true
30253
+ },
30254
+ {
30255
+ id: "dockWash",
30256
+ kind: "action",
30257
+ label: "Wash mop",
30258
+ icon: "droplets",
30259
+ enabled: true
30260
+ },
30261
+ {
30262
+ id: "autoEmpty",
30263
+ kind: "action",
30264
+ label: "Empty dustbin",
30265
+ icon: "trash-2",
30266
+ enabled: true
30267
+ },
30268
+ {
30269
+ id: "flashOn",
30270
+ kind: "action",
30271
+ label: "Flash on",
30272
+ icon: "flashlight",
30273
+ enabled: true
30274
+ },
30275
+ {
30276
+ id: "flashOff",
30277
+ kind: "action",
30278
+ label: "Flash off",
30279
+ icon: "flashlight-off",
30280
+ enabled: true
30281
+ },
30282
+ {
30283
+ id: "sound:meow",
30284
+ kind: "sound",
30285
+ label: "Meow",
30286
+ icon: "cat",
30287
+ soundId: 684,
30288
+ enabled: true
30289
+ },
30290
+ {
30291
+ id: "sound:bark",
30292
+ kind: "sound",
30293
+ label: "Woof",
30294
+ icon: "dog",
30295
+ soundId: 685,
30296
+ enabled: true
30297
+ },
30298
+ {
30299
+ id: "sound:footsteps",
30300
+ kind: "sound",
30301
+ label: "Footsteps",
30302
+ icon: "footprints",
30303
+ soundId: 686,
30304
+ enabled: true
30305
+ },
30306
+ {
30307
+ id: "sound:purring",
30308
+ kind: "sound",
30309
+ label: "Purring",
30310
+ icon: "cat",
30311
+ soundId: 687,
30312
+ enabled: true
30313
+ },
30314
+ {
30315
+ id: "sound:tickTock",
30316
+ kind: "sound",
30317
+ label: "Tick-tock",
30318
+ icon: "clock",
30319
+ soundId: 688,
30320
+ enabled: true
30321
+ }
30322
+ ];
30323
+ /** Coordinates for `goToPoint` — a point on the robot's live map. */
30324
+ var NavigationPointSchema = zod.z.object({
30325
+ x: zod.z.number(),
30326
+ y: zod.z.number()
30327
+ });
30328
+ /**
30329
+ * Per-device FEATURE-FLAG report for the general (non-dictionary) primitives.
30330
+ * The cap reports which are enabled so the UI / PTZ render only the controls
30331
+ * that are turned on for THIS device. Data-driven: the provider derives these
30332
+ * from config + probe, never hardcoded in the UI. The per-DICTIONARY-entry flags
30333
+ * live on {@link NavigationActionEntrySchema.enabled}; these gate the primitives
30334
+ * that are not dictionary entries.
30335
+ *
30336
+ * - `move` / `stop` — the momentary drive joystick.
30337
+ * - `goToPoint` — send-to-map-coordinate. Ships OFF on Dreame until the
30338
+ * map-coordinate plumbing is wired.
30339
+ * - `runAction` — the discrete action buttons (dictionary `kind:'action'`).
30340
+ * - `playSound` — the sound buttons (dictionary `kind:'sound'`).
30341
+ * - `light` — the on/off fill-light toggle (works anytime).
30342
+ * - `lightMode` — the auto/manual selector + manual level slider (a
30343
+ * camera-service control; needs an active stream).
30344
+ */
30345
+ var NavigationFeaturesSchema = zod.z.object({
30346
+ move: zod.z.boolean(),
30347
+ stop: zod.z.boolean(),
30348
+ goToPoint: zod.z.boolean(),
30349
+ runAction: zod.z.boolean(),
30350
+ playSound: zod.z.boolean(),
30351
+ light: zod.z.boolean(),
30352
+ lightMode: zod.z.boolean()
30353
+ });
30354
+ /** Light mode: `auto` lets the camera choose brightness; `manual` uses `level`. */
30355
+ var NavigationLightModeSchema = zod.z.enum(["auto", "manual"]);
30356
+ /** Manual fill-light level bounds (the camera-service accepts 40..100). */
30357
+ var NAVIGATION_LIGHT_LEVEL_MIN = 40;
30358
+ var NAVIGATION_LIGHT_LEVEL_MAX = 100;
30359
+ /** Coarse work mode the robot reports — drives the UI's active-state chips. */
30360
+ var NavigationModeSchema = zod.z.enum([
30361
+ "idle",
30362
+ "cleaning",
30363
+ "spot",
30364
+ "following",
30365
+ "goto",
30366
+ "returning",
30367
+ "paused",
30368
+ "unknown"
30369
+ ]);
30370
+ /**
30371
+ * Live navigation state so the UI can reflect what the robot is doing:
30372
+ * - `mode` — coarse activity (idle / cleaning / following / …).
30373
+ * - `following` — person/pet follow is currently armed.
30374
+ * - `flash` — the on-camera fill light is on.
30375
+ * - `lightMode` — auto vs manual fill-light mode.
30376
+ * - `lightLevel` — manual fill-light level (40..100); meaningful when
30377
+ * `lightMode === 'manual'`.
30378
+ */
30379
+ var NavigationStatusSchema = zod.z.object({
30380
+ mode: NavigationModeSchema,
30381
+ following: zod.z.boolean(),
30382
+ flash: zod.z.boolean(),
30383
+ lightMode: NavigationLightModeSchema,
30384
+ lightLevel: zod.z.number().min(40).max(100),
30385
+ /** Ms epoch when the slice was last updated. */
30386
+ lastChangedAt: zod.z.number()
30387
+ });
30388
+ /**
30389
+ * Runtime-state slice owned by this cap (kernel-managed: validated, mirrored,
30390
+ * observable). Adds `lastFetchedAt` on top of the status shape per the
30391
+ * convention.
30392
+ */
30393
+ var NavigationRuntimeStateSchema = NavigationStatusSchema.extend({ lastFetchedAt: zod.z.number() });
30394
+ var navigationCapability = {
30395
+ name: "navigation",
30396
+ scope: "device",
30397
+ deviceNative: true,
30398
+ mode: "singleton",
30399
+ deviceTypes: [require_sleep.DeviceType.Camera],
30400
+ deviceConfig: { ui: {
30401
+ kind: "widget",
30402
+ widgetId: "host/navigation-panel",
30403
+ tab: "navigation",
30404
+ topTab: true,
30405
+ label: "Navigation",
30406
+ order: 0
30407
+ } },
30408
+ methods: {
30409
+ /**
30410
+ * Momentary drive nudge (the robot moves). `protected` — mirrors
30411
+ * `ptz.continuousMove` so the Viewer navigation panel (and the PTZ-mimic
30412
+ * path) works for any authenticated user, not admin-only. The UI sends
30413
+ * these at ~1 Hz while a control is held; the provider forwards each one to
30414
+ * a single drive write WITHOUT debouncing.
30415
+ */
30416
+ move: require_sleep.method(NavigationMoveCommandSchema.extend({ deviceId: zod.z.number() }), zod.z.void(), { kind: "mutation" }),
30417
+ /** Halt all motion immediately (zero drive vector). */
30418
+ stop: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.void(), { kind: "mutation" }),
30419
+ /** Send the robot to a point on its live map. */
30420
+ goToPoint: require_sleep.method(NavigationPointSchema.extend({ deviceId: zod.z.number() }), zod.z.void(), { kind: "mutation" }),
30421
+ /**
30422
+ * Enumerate the discrete controls THIS device supports (data-driven UI +
30423
+ * PTZ mimic). Camera-probed subset of {@link NAVIGATION_ACTION_CATALOG}.
30424
+ */
30425
+ listActions: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.array(NavigationActionEntrySchema)),
30426
+ /**
30427
+ * Run one discrete action (a `kind:'action'` dictionary entry). Invalid /
30428
+ * unsupported action ids are rejected by the provider.
30429
+ */
30430
+ runAction: require_sleep.method(zod.z.object({
30431
+ deviceId: zod.z.number(),
30432
+ actionId: NavigationActionIdSchema
30433
+ }), zod.z.void(), { kind: "mutation" }),
30434
+ /** Play a sound by its wire id (the `soundId` of a `kind:'sound'` entry). */
30435
+ playSound: require_sleep.method(zod.z.object({
30436
+ deviceId: zod.z.number(),
30437
+ soundId: zod.z.number().int()
30438
+ }), zod.z.void(), { kind: "mutation" }),
30439
+ /**
30440
+ * Turn the on-camera fill light on / off (the `OpenFullLight` control —
30441
+ * works anytime, no active stream required).
30442
+ */
30443
+ setLightOn: require_sleep.method(zod.z.object({
30444
+ deviceId: zod.z.number(),
30445
+ on: zod.z.boolean()
30446
+ }), zod.z.void(), { kind: "mutation" }),
30447
+ /**
30448
+ * Set the fill-light mode (auto vs manual). `manual` optionally carries the
30449
+ * initial `level`. The auto/manual + level control is a CAMERA-service
30450
+ * action that generally needs an active camera stream/monitor session — the
30451
+ * UI shows the manual level slider ONLY when `mode === 'manual'`.
30452
+ */
30453
+ setLightMode: require_sleep.method(zod.z.object({
30454
+ deviceId: zod.z.number(),
30455
+ mode: NavigationLightModeSchema,
30456
+ level: zod.z.number().min(40).max(100).optional()
30457
+ }), zod.z.void(), { kind: "mutation" }),
30458
+ /** Set the MANUAL fill-light level (40..100). Implies `manual` mode. */
30459
+ setLightLevel: require_sleep.method(zod.z.object({
30460
+ deviceId: zod.z.number(),
30461
+ level: zod.z.number().min(40).max(100)
30462
+ }), zod.z.void(), { kind: "mutation" }),
30463
+ /**
30464
+ * Per-device FEATURE-FLAG report for the general primitives — drives which
30465
+ * controls the UI shows (the per-entry flags for the dictionary come back on
30466
+ * `listActions`).
30467
+ */
30468
+ getFeatures: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), NavigationFeaturesSchema)
30469
+ },
30470
+ events: { onStatusChanged: { data: zod.z.object({
30471
+ deviceId: zod.z.number(),
30472
+ status: NavigationStatusSchema
30473
+ }) } },
30474
+ status: {
30475
+ schema: NavigationStatusSchema,
30476
+ kind: "push"
30477
+ },
30478
+ /**
30479
+ * Runtime-state slice mirrored by the kernel. The navigation panel watches it
30480
+ * for live mode / follow / flash changes.
30481
+ */
30482
+ runtimeState: NavigationRuntimeStateSchema,
30483
+ /**
30484
+ * Runtime-state durability: **session** — like `vacuum-control`, a restored
30485
+ * `mode: cleaning` / `following: true` is a robot that is not actually doing
30486
+ * that. The live handle re-publishes on connect.
30487
+ *
30488
+ * See `RuntimeStateDurability`. Enforced by
30489
+ * `scripts/check-runtime-state-durability.ts`.
30490
+ */
30491
+ durability: "session"
30492
+ };
30493
+ //#endregion
30494
+ //#region src/capabilities/network-link.cap.ts
30495
+ /**
30496
+ * How a device reaches the network, and how well.
30497
+ *
30498
+ * NOT `connectivity`: that cap is an UPSTREAM system's binary view of whether
30499
+ * an entity is reachable (a Home Assistant `binary_sensor` with
30500
+ * `device_class: connectivity`). This one is the device's OWN link — the
30501
+ * medium it is on and the signal it sees — the way `battery` is the device's
30502
+ * own charge.
30503
+ *
30504
+ * `'wifi'`, `'ethernet'` and `'cellular'` are links the firmware actually
30505
+ * reported. `'unknown'` is the ABSENCE of an answer — the provider has
30506
+ * registered the capability but has not read the link yet (D315: unknown is
30507
+ * not empty). Consumers skip it rather than draw a wire or a bar.
30508
+ */
30509
+ var NETWORK_LINK_TYPES = [
30510
+ "wifi",
30511
+ "ethernet",
30512
+ "cellular",
30513
+ "unknown"
30514
+ ];
30515
+ /**
30516
+ * Network-link snapshot. Same shape for every provider (a Reolink wifi
30517
+ * camera, a Home Assistant device with a signal-strength sensor, a Tapo
30518
+ * plug): one slice under `device.runtimeState['network-link']`, one badge,
30519
+ * one Home Assistant projection.
30520
+ */
30521
+ var NetworkLinkStatusSchema = zod.z.object({
30522
+ /** The link the device is on. `'unknown'` = not read yet, not "no link". */
30523
+ type: zod.z.enum(NETWORK_LINK_TYPES),
30524
+ /**
30525
+ * Link quality, 0..100 inclusive, normalised by the provider from whatever
30526
+ * the firmware reports (bars, RSSI, a vendor scale). **`null` means NOT
30527
+ * KNOWN or NOT APPLICABLE** — a wired link has no signal, and a wireless
30528
+ * one whose reading has not landed must not be drawn at 0 %. Consumers
30529
+ * SKIP a null rather than coerce it.
30530
+ */
30531
+ signalPercent: zod.z.number().min(0).max(100).nullable(),
30532
+ /** Raw received signal strength in dBm, when the firmware reports one. */
30533
+ rssiDbm: zod.z.number().optional(),
30534
+ /** Network name of a wireless link, when the firmware reports it. */
30535
+ ssid: zod.z.string().optional(),
30536
+ /** Ms epoch of the last observation. Lets consumers reason about freshness. */
30537
+ lastUpdated: zod.z.number()
30538
+ });
30539
+ /** The slice a provider seeds before its first read: nothing is known yet. */
30540
+ var NETWORK_LINK_UNKNOWN = {
30541
+ type: "unknown",
30542
+ signalPercent: null,
30543
+ lastUpdated: 0
30544
+ };
30545
+ /**
30546
+ * Normalise a signal the firmware reports as BARS (0..`maxBars`) to 0..100.
30547
+ * Out-of-range or non-finite input is not a reading: `null`.
30548
+ */
30549
+ function signalPercentFromBars(bars, maxBars) {
30550
+ if (bars === void 0 || !Number.isFinite(bars) || maxBars <= 0) return null;
30551
+ if (bars < 0 || bars > maxBars) return null;
30552
+ return Math.round(bars / maxBars * 100);
30553
+ }
30554
+ /**
30555
+ * Normalise an RSSI in dBm to 0..100 on the usual wifi scale: -100 dBm and
30556
+ * below is 0 %, -50 dBm and above is 100 %, linear between. Non-finite or
30557
+ * positive input is not an RSSI: `null`.
30558
+ */
30559
+ function signalPercentFromRssi(rssiDbm) {
30560
+ if (rssiDbm === void 0 || !Number.isFinite(rssiDbm) || rssiDbm > 0) return null;
30561
+ return Math.round((Math.min(-50, Math.max(-100, rssiDbm)) + 100) * 2);
30562
+ }
30563
+ var networkLinkCapability = {
30564
+ name: "network-link",
30565
+ scope: "device",
30566
+ deviceNative: true,
30567
+ mode: "singleton",
30568
+ deviceTypes: [
30569
+ require_sleep.DeviceType.Camera,
30570
+ require_sleep.DeviceType.Sensor,
30571
+ require_sleep.DeviceType.Button,
30572
+ require_sleep.DeviceType.Switch,
30573
+ require_sleep.DeviceType.Light,
30574
+ require_sleep.DeviceType.Lock,
30575
+ require_sleep.DeviceType.Siren
30576
+ ],
30577
+ methods: {},
30578
+ events: {
30579
+ /**
30580
+ * Emitted whenever the cached status changes (a link switch, a signal
30581
+ * reading that moved). Mirrored on the parent chain by the
30582
+ * DeviceEventPropagator like `battery.onStatusChanged`.
30583
+ */
30584
+ onStatusChanged: { data: zod.z.object({
30585
+ deviceId: zod.z.number(),
30586
+ status: NetworkLinkStatusSchema
30587
+ }) } },
30588
+ status: {
30589
+ schema: NetworkLinkStatusSchema,
30590
+ kind: "push",
30591
+ empty: NETWORK_LINK_UNKNOWN
30592
+ },
30593
+ /**
30594
+ * Runtime-state slice — every provider stores the same shape under
30595
+ * `device.runtimeState['network-link']`, read once by the badge and the
30596
+ * Home Assistant projector regardless of the driver.
30597
+ */
30598
+ runtimeState: NetworkLinkStatusSchema,
30599
+ /**
30600
+ * Runtime-state durability: **restored** — a link reading is slow to
30601
+ * change and a sleeping battery camera may not report for hours; the
30602
+ * restored slice is what the badge shows until the next read.
30603
+ *
30604
+ * See `RuntimeStateDurability`. Enforced by
30605
+ * `scripts/check-runtime-state-durability.ts`.
30606
+ */
30607
+ durability: "restored",
30608
+ /** Clock fields: written, but excluded from the compare that decides
30609
+ * whether persisting is worth a SQLite commit. */
30610
+ volatileStateFields: ["lastUpdated"]
30611
+ };
30612
+ //#endregion
29900
30613
  //#region src/capabilities/network-quality.cap.ts
29901
30614
  /**
29902
30615
  * network-quality — system-scoped singleton capability tracking RTT,
@@ -31829,431 +32542,6 @@ var ptzAutotrackCapability = {
31829
32542
  durability: "session"
31830
32543
  };
31831
32544
  //#endregion
31832
- //#region src/capabilities/navigation.cap.ts
31833
- /**
31834
- * `navigation` — a device-scoped capability that natively expresses the FULL
31835
- * navigation / action surface of a robot that DRIVES ITSELF and carries an
31836
- * on-board camera (the Dreame robot-vacuum camera is the first provider).
31837
- *
31838
- * Why a NEW cap rather than overloading `ptz`:
31839
- * - `ptz` moves a *gimbal* on a fixed camera (pan/tilt/zoom of the lens). A
31840
- * robot vacuum has no gimbal — the whole chassis drives, turns and spins.
31841
- * The two are different physical models: PTZ is absolute-position + presets,
31842
- * navigation is momentary drive nudges + discrete robot ACTIONS
31843
- * (dock / spot-clean / follow-pet / go-to-point / …).
31844
- * - This cap is the SOURCE OF TRUTH. Two consumers adapt from it rather than
31845
- * the reverse:
31846
- * 1. a native CamStack navigation panel (data-driven from `listActions`
31847
- * / `getOptions`), and
31848
- * 2. the PTZ surface — a thin adapter mimics `ptz` from `navigation` so a
31849
- * robot camera shows up in the existing PTZ control path without every
31850
- * PTZ provider learning about robots. The mapping lives in the adapter,
31851
- * not here (see the addon design note):
31852
- * ptz.continuousMove({pan,tilt}) → navigation.move({pan,tilt})
31853
- * ptz.stop() → navigation.stop()
31854
- * ptz.goHome() → navigation.runAction('goHome')
31855
- * ptz.getPresets() → navigation.listActions() (id→preset)
31856
- * ptz.goToPreset(id) → navigation.runAction(id)
31857
- *
31858
- * ## Continuous drive
31859
- *
31860
- * `move` is MOMENTARY. Fluid navigation comes from the UI (or the PTZ adapter)
31861
- * sending `move({pan,tilt})` REPEATEDLY at ~1 Hz while a direction is held, and
31862
- * one `stop()` on release — exactly like the robot app's remote-drive joystick.
31863
- * The provider forwards EACH `move` to one drive write; it must NOT debounce or
31864
- * coalesce them. The UI owns the cadence.
31865
- *
31866
- * ## The action dictionary
31867
- *
31868
- * The discrete controls (dock / locate / spot-clean / follow / flash / sounds)
31869
- * are a DATA-DRIVEN dictionary the cap exposes via `listActions()`. Each entry
31870
- * carries `{ id, kind, label, icon }` (plus `soundId` for sound entries) so the
31871
- * native panel AND the PTZ mimic render buttons WITHOUT hardcoding a
31872
- * vendor-specific list. `kind: 'action'` entries are triggered with
31873
- * `runAction({ actionId })`; `kind: 'sound'` entries with `playSound({ soundId })`
31874
- * (the entry carries the `soundId` to pass). The general primitives — `move`,
31875
- * `stop`, `goToPoint` — stay as first-class methods, not dictionary entries.
31876
- *
31877
- * NOTE (provider wiring): the first provider (Dreame) drives this with the RAW
31878
- * `callAction(siid,aiid,in)` / `setProperty({siid,piid,value})` MIoT primitives
31879
- * that the currently-published `@apocaliss92/nodedreame` already exposes on
31880
- * every device handle. A future nodedreame publish adds a typed
31881
- * `DreameCameraController` (drive / playPetSound / spotClean / findPet / …); the
31882
- * provider can then swap the raw calls for the typed methods with no change to
31883
- * THIS contract.
31884
- */
31885
- /**
31886
- * A momentary drive nudge. The robot MOVES (no gimbal) for as long as the caller
31887
- * keeps sending nudges (~1 Hz); an explicit `stop` (or letting the nudges lapse)
31888
- * halts it.
31889
- *
31890
- * - `pan` — turn: negative = left, positive = right, 0 = straight.
31891
- * - `tilt` — throttle: positive = forward, negative = spin / turn-around.
31892
- * - `speed` — optional intensity hint [0, 1]; the provider may scale the drive
31893
- * vector by it (drivers without proportional drive ignore it).
31894
- *
31895
- * `pan` / `tilt` are normalized [-1, 1]. Both optional so a caller can nudge one
31896
- * axis alone; an all-undefined nudge is a no-op.
31897
- */
31898
- var NavigationMoveCommandSchema = zod.z.object({
31899
- pan: zod.z.number().min(-1).max(1).optional(),
31900
- tilt: zod.z.number().min(-1).max(1).optional(),
31901
- speed: zod.z.number().min(0).max(1).optional()
31902
- });
31903
- /**
31904
- * The enumerated discrete actions a navigation-capable robot can perform via
31905
- * `runAction`. This is the CLOSED vocabulary; a given device advertises the
31906
- * subset it supports through `listActions`. Sounds are NOT here — they go through
31907
- * `playSound` (see the `sound` dictionary entries).
31908
- */
31909
- var NavigationActionIdSchema = zod.z.enum([
31910
- "goHome",
31911
- "locate",
31912
- "spotClean",
31913
- "findPet",
31914
- "personFollow",
31915
- "stop",
31916
- "startClean",
31917
- "pauseClean",
31918
- "dockWash",
31919
- "autoEmpty",
31920
- "flashOn",
31921
- "flashOff"
31922
- ]);
31923
- /** Whether a dictionary entry is a `runAction` action or a `playSound` sound. */
31924
- var NavigationEntryKindSchema = zod.z.enum(["action", "sound"]);
31925
- /**
31926
- * One entry in the navigation action dictionary — the DATA-DRIVEN unit both the
31927
- * native panel and the PTZ mimic render as a button.
31928
- *
31929
- * - `id` — stable id. For `kind:'action'` it is a {@link NavigationActionId}
31930
- * (pass to `runAction`); for `kind:'sound'` it is a namespaced id
31931
- * (`sound:meow`) whose `soundId` is passed to `playSound`.
31932
- * - `icon` — icon HINT (lucide-style name; the UI maps it to its own set).
31933
- * - `label` — operator-facing English label.
31934
- * - `soundId` — wire sound id, present only on `kind:'sound'` entries.
31935
- * - `enabled` — per-device FEATURE FLAG. `listActions` reports it so the UI /
31936
- * PTZ render ONLY enabled entries. Data-driven: the provider
31937
- * flips it from config, never by editing code.
31938
- */
31939
- var NavigationActionEntrySchema = zod.z.object({
31940
- id: zod.z.string(),
31941
- kind: NavigationEntryKindSchema,
31942
- label: zod.z.string(),
31943
- icon: zod.z.string(),
31944
- /** Present only on `kind:'sound'` entries — the id to pass to `playSound`. */
31945
- soundId: zod.z.number().int().optional(),
31946
- /** Per-device feature flag — render this entry only when true. */
31947
- enabled: zod.z.boolean()
31948
- });
31949
- /**
31950
- * Canonical dictionary of every navigation control (discrete actions + sounds)
31951
- * with its default label + icon hint. A provider filters this to the subset a
31952
- * device supports (and may override a label). Exported so the provider AND the
31953
- * UI share ONE list instead of re-deriving labels / ids independently.
31954
- *
31955
- * Icons are lucide-style hints; the viewer maps them to its own icon set. Every
31956
- * catalog default is `enabled: true`; a provider overrides per-device from
31957
- * config (data-driven — enabling / disabling one later is a config change, not a
31958
- * code change).
31959
- */
31960
- var NAVIGATION_ACTION_CATALOG = [
31961
- {
31962
- id: "startClean",
31963
- kind: "action",
31964
- label: "Start cleaning",
31965
- icon: "play",
31966
- enabled: true
31967
- },
31968
- {
31969
- id: "pauseClean",
31970
- kind: "action",
31971
- label: "Pause cleaning",
31972
- icon: "pause",
31973
- enabled: true
31974
- },
31975
- {
31976
- id: "stop",
31977
- kind: "action",
31978
- label: "Stop",
31979
- icon: "hand",
31980
- enabled: true
31981
- },
31982
- {
31983
- id: "goHome",
31984
- kind: "action",
31985
- label: "Return to dock",
31986
- icon: "home",
31987
- enabled: true
31988
- },
31989
- {
31990
- id: "locate",
31991
- kind: "action",
31992
- label: "Locate",
31993
- icon: "bell",
31994
- enabled: true
31995
- },
31996
- {
31997
- id: "spotClean",
31998
- kind: "action",
31999
- label: "Spot clean here",
32000
- icon: "crosshair",
32001
- enabled: true
32002
- },
32003
- {
32004
- id: "findPet",
32005
- kind: "action",
32006
- label: "Find pet",
32007
- icon: "paw-print",
32008
- enabled: true
32009
- },
32010
- {
32011
- id: "personFollow",
32012
- kind: "action",
32013
- label: "Follow me",
32014
- icon: "footprints",
32015
- enabled: true
32016
- },
32017
- {
32018
- id: "dockWash",
32019
- kind: "action",
32020
- label: "Wash mop",
32021
- icon: "droplets",
32022
- enabled: true
32023
- },
32024
- {
32025
- id: "autoEmpty",
32026
- kind: "action",
32027
- label: "Empty dustbin",
32028
- icon: "trash-2",
32029
- enabled: true
32030
- },
32031
- {
32032
- id: "flashOn",
32033
- kind: "action",
32034
- label: "Flash on",
32035
- icon: "flashlight",
32036
- enabled: true
32037
- },
32038
- {
32039
- id: "flashOff",
32040
- kind: "action",
32041
- label: "Flash off",
32042
- icon: "flashlight-off",
32043
- enabled: true
32044
- },
32045
- {
32046
- id: "sound:meow",
32047
- kind: "sound",
32048
- label: "Meow",
32049
- icon: "cat",
32050
- soundId: 684,
32051
- enabled: true
32052
- },
32053
- {
32054
- id: "sound:bark",
32055
- kind: "sound",
32056
- label: "Woof",
32057
- icon: "dog",
32058
- soundId: 685,
32059
- enabled: true
32060
- },
32061
- {
32062
- id: "sound:footsteps",
32063
- kind: "sound",
32064
- label: "Footsteps",
32065
- icon: "footprints",
32066
- soundId: 686,
32067
- enabled: true
32068
- },
32069
- {
32070
- id: "sound:purring",
32071
- kind: "sound",
32072
- label: "Purring",
32073
- icon: "cat",
32074
- soundId: 687,
32075
- enabled: true
32076
- },
32077
- {
32078
- id: "sound:tickTock",
32079
- kind: "sound",
32080
- label: "Tick-tock",
32081
- icon: "clock",
32082
- soundId: 688,
32083
- enabled: true
32084
- }
32085
- ];
32086
- /** Coordinates for `goToPoint` — a point on the robot's live map. */
32087
- var NavigationPointSchema = zod.z.object({
32088
- x: zod.z.number(),
32089
- y: zod.z.number()
32090
- });
32091
- /**
32092
- * Per-device FEATURE-FLAG report for the general (non-dictionary) primitives.
32093
- * The cap reports which are enabled so the UI / PTZ render only the controls
32094
- * that are turned on for THIS device. Data-driven: the provider derives these
32095
- * from config + probe, never hardcoded in the UI. The per-DICTIONARY-entry flags
32096
- * live on {@link NavigationActionEntrySchema.enabled}; these gate the primitives
32097
- * that are not dictionary entries.
32098
- *
32099
- * - `move` / `stop` — the momentary drive joystick.
32100
- * - `goToPoint` — send-to-map-coordinate. Ships OFF on Dreame until the
32101
- * map-coordinate plumbing is wired.
32102
- * - `runAction` — the discrete action buttons (dictionary `kind:'action'`).
32103
- * - `playSound` — the sound buttons (dictionary `kind:'sound'`).
32104
- * - `light` — the on/off fill-light toggle (works anytime).
32105
- * - `lightMode` — the auto/manual selector + manual level slider (a
32106
- * camera-service control; needs an active stream).
32107
- */
32108
- var NavigationFeaturesSchema = zod.z.object({
32109
- move: zod.z.boolean(),
32110
- stop: zod.z.boolean(),
32111
- goToPoint: zod.z.boolean(),
32112
- runAction: zod.z.boolean(),
32113
- playSound: zod.z.boolean(),
32114
- light: zod.z.boolean(),
32115
- lightMode: zod.z.boolean()
32116
- });
32117
- /** Light mode: `auto` lets the camera choose brightness; `manual` uses `level`. */
32118
- var NavigationLightModeSchema = zod.z.enum(["auto", "manual"]);
32119
- /** Manual fill-light level bounds (the camera-service accepts 40..100). */
32120
- var NAVIGATION_LIGHT_LEVEL_MIN = 40;
32121
- var NAVIGATION_LIGHT_LEVEL_MAX = 100;
32122
- /** Coarse work mode the robot reports — drives the UI's active-state chips. */
32123
- var NavigationModeSchema = zod.z.enum([
32124
- "idle",
32125
- "cleaning",
32126
- "spot",
32127
- "following",
32128
- "goto",
32129
- "returning",
32130
- "paused",
32131
- "unknown"
32132
- ]);
32133
- /**
32134
- * Live navigation state so the UI can reflect what the robot is doing:
32135
- * - `mode` — coarse activity (idle / cleaning / following / …).
32136
- * - `following` — person/pet follow is currently armed.
32137
- * - `flash` — the on-camera fill light is on.
32138
- * - `lightMode` — auto vs manual fill-light mode.
32139
- * - `lightLevel` — manual fill-light level (40..100); meaningful when
32140
- * `lightMode === 'manual'`.
32141
- */
32142
- var NavigationStatusSchema = zod.z.object({
32143
- mode: NavigationModeSchema,
32144
- following: zod.z.boolean(),
32145
- flash: zod.z.boolean(),
32146
- lightMode: NavigationLightModeSchema,
32147
- lightLevel: zod.z.number().min(40).max(100),
32148
- /** Ms epoch when the slice was last updated. */
32149
- lastChangedAt: zod.z.number()
32150
- });
32151
- /**
32152
- * Runtime-state slice owned by this cap (kernel-managed: validated, mirrored,
32153
- * observable). Adds `lastFetchedAt` on top of the status shape per the
32154
- * convention.
32155
- */
32156
- var NavigationRuntimeStateSchema = NavigationStatusSchema.extend({ lastFetchedAt: zod.z.number() });
32157
- var navigationCapability = {
32158
- name: "navigation",
32159
- scope: "device",
32160
- deviceNative: true,
32161
- mode: "singleton",
32162
- deviceTypes: [require_sleep.DeviceType.Camera],
32163
- deviceConfig: { ui: {
32164
- kind: "widget",
32165
- widgetId: "host/navigation-panel",
32166
- tab: "navigation",
32167
- topTab: true,
32168
- label: "Navigation",
32169
- order: 0
32170
- } },
32171
- methods: {
32172
- /**
32173
- * Momentary drive nudge (the robot moves). `protected` — mirrors
32174
- * `ptz.continuousMove` so the Viewer navigation panel (and the PTZ-mimic
32175
- * path) works for any authenticated user, not admin-only. The UI sends
32176
- * these at ~1 Hz while a control is held; the provider forwards each one to
32177
- * a single drive write WITHOUT debouncing.
32178
- */
32179
- move: require_sleep.method(NavigationMoveCommandSchema.extend({ deviceId: zod.z.number() }), zod.z.void(), { kind: "mutation" }),
32180
- /** Halt all motion immediately (zero drive vector). */
32181
- stop: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.void(), { kind: "mutation" }),
32182
- /** Send the robot to a point on its live map. */
32183
- goToPoint: require_sleep.method(NavigationPointSchema.extend({ deviceId: zod.z.number() }), zod.z.void(), { kind: "mutation" }),
32184
- /**
32185
- * Enumerate the discrete controls THIS device supports (data-driven UI +
32186
- * PTZ mimic). Camera-probed subset of {@link NAVIGATION_ACTION_CATALOG}.
32187
- */
32188
- listActions: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.array(NavigationActionEntrySchema)),
32189
- /**
32190
- * Run one discrete action (a `kind:'action'` dictionary entry). Invalid /
32191
- * unsupported action ids are rejected by the provider.
32192
- */
32193
- runAction: require_sleep.method(zod.z.object({
32194
- deviceId: zod.z.number(),
32195
- actionId: NavigationActionIdSchema
32196
- }), zod.z.void(), { kind: "mutation" }),
32197
- /** Play a sound by its wire id (the `soundId` of a `kind:'sound'` entry). */
32198
- playSound: require_sleep.method(zod.z.object({
32199
- deviceId: zod.z.number(),
32200
- soundId: zod.z.number().int()
32201
- }), zod.z.void(), { kind: "mutation" }),
32202
- /**
32203
- * Turn the on-camera fill light on / off (the `OpenFullLight` control —
32204
- * works anytime, no active stream required).
32205
- */
32206
- setLightOn: require_sleep.method(zod.z.object({
32207
- deviceId: zod.z.number(),
32208
- on: zod.z.boolean()
32209
- }), zod.z.void(), { kind: "mutation" }),
32210
- /**
32211
- * Set the fill-light mode (auto vs manual). `manual` optionally carries the
32212
- * initial `level`. The auto/manual + level control is a CAMERA-service
32213
- * action that generally needs an active camera stream/monitor session — the
32214
- * UI shows the manual level slider ONLY when `mode === 'manual'`.
32215
- */
32216
- setLightMode: require_sleep.method(zod.z.object({
32217
- deviceId: zod.z.number(),
32218
- mode: NavigationLightModeSchema,
32219
- level: zod.z.number().min(40).max(100).optional()
32220
- }), zod.z.void(), { kind: "mutation" }),
32221
- /** Set the MANUAL fill-light level (40..100). Implies `manual` mode. */
32222
- setLightLevel: require_sleep.method(zod.z.object({
32223
- deviceId: zod.z.number(),
32224
- level: zod.z.number().min(40).max(100)
32225
- }), zod.z.void(), { kind: "mutation" }),
32226
- /**
32227
- * Per-device FEATURE-FLAG report for the general primitives — drives which
32228
- * controls the UI shows (the per-entry flags for the dictionary come back on
32229
- * `listActions`).
32230
- */
32231
- getFeatures: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), NavigationFeaturesSchema)
32232
- },
32233
- events: { onStatusChanged: { data: zod.z.object({
32234
- deviceId: zod.z.number(),
32235
- status: NavigationStatusSchema
32236
- }) } },
32237
- status: {
32238
- schema: NavigationStatusSchema,
32239
- kind: "push"
32240
- },
32241
- /**
32242
- * Runtime-state slice mirrored by the kernel. The navigation panel watches it
32243
- * for live mode / follow / flash changes.
32244
- */
32245
- runtimeState: NavigationRuntimeStateSchema,
32246
- /**
32247
- * Runtime-state durability: **session** — like `vacuum-control`, a restored
32248
- * `mode: cleaning` / `following: true` is a robot that is not actually doing
32249
- * that. The live handle re-publishes on connect.
32250
- *
32251
- * See `RuntimeStateDurability`. Enforced by
32252
- * `scripts/check-runtime-state-durability.ts`.
32253
- */
32254
- durability: "session"
32255
- };
32256
- //#endregion
32257
32545
  //#region src/capabilities/reboot.cap.ts
32258
32546
  /**
32259
32547
  * reboot — device-scoped capability for "soft" device reboots (firmware
@@ -39914,6 +40202,7 @@ var CAPABILITY_NAMES = {
39914
40202
  storage: "storage",
39915
40203
  storageEvictable: "storage-evictable",
39916
40204
  storageMigration: "storage-migration",
40205
+ storageOccupancy: "storage-occupancy",
39917
40206
  storageProvider: "storage-provider",
39918
40207
  streamBroker: "stream-broker",
39919
40208
  streamCatalog: "stream-catalog",
@@ -40459,6 +40748,10 @@ var CAPABILITY_ROUTER_KEYS = [
40459
40748
  key: "storageMigration",
40460
40749
  name: "storage-migration"
40461
40750
  },
40751
+ {
40752
+ key: "storageOccupancy",
40753
+ name: "storage-occupancy"
40754
+ },
40462
40755
  {
40463
40756
  key: "storageProvider",
40464
40757
  name: "storage-provider"
@@ -40703,6 +40996,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
40703
40996
  storageCapability,
40704
40997
  storageEvictableCapability,
40705
40998
  storageMigrationCapability,
40999
+ storageOccupancyCapability,
40706
41000
  storageProviderCapability,
40707
41001
  streamBrokerCapability,
40708
41002
  streamCatalogCapability,
@@ -45677,6 +45971,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
45677
45971
  addonId: null,
45678
45972
  access: "view"
45679
45973
  },
45974
+ "storage.listDrainProgress": {
45975
+ capName: "storage",
45976
+ capScope: "system",
45977
+ addonId: null,
45978
+ access: "view"
45979
+ },
45680
45980
  "storage.listLocationDeclarations": {
45681
45981
  capName: "storage",
45682
45982
  capScope: "system",
@@ -45821,6 +46121,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
45821
46121
  addonId: null,
45822
46122
  access: "view"
45823
46123
  },
46124
+ "storageOccupancy.getOccupancy": {
46125
+ capName: "storage-occupancy",
46126
+ capScope: "system",
46127
+ addonId: null,
46128
+ access: "view"
46129
+ },
45824
46130
  "storageProvider.abortUpload": {
45825
46131
  capName: "storage-provider",
45826
46132
  capScope: "system",
@@ -46956,6 +47262,7 @@ var KNOWN_CAP_NAMES = [
46956
47262
  "storage",
46957
47263
  "storage-evictable",
46958
47264
  "storage-migration",
47265
+ "storage-occupancy",
46959
47266
  "storage-provider",
46960
47267
  "stream-broker",
46961
47268
  "stream-catalog",
@@ -47102,6 +47409,7 @@ var SYSTEM_CAP_NAMES = [
47102
47409
  "storage",
47103
47410
  "storage-evictable",
47104
47411
  "storage-migration",
47412
+ "storage-occupancy",
47105
47413
  "storage-provider",
47106
47414
  "stream-broker",
47107
47415
  "system",
@@ -50101,6 +50409,7 @@ function createSystemProxy(api) {
50101
50409
  listLocationDeclarations: (input) => dispatch("storage", "listLocationDeclarations", "query", input),
50102
50410
  upsertLocation: (input) => dispatch("storage", "upsertLocation", "mutation", input),
50103
50411
  deleteLocation: (input) => dispatch("storage", "deleteLocation", "mutation", input),
50412
+ listDrainProgress: (input) => dispatch("storage", "listDrainProgress", "query", input),
50104
50413
  testLocation: (input) => dispatch("storage", "testLocation", "query", input),
50105
50414
  listProviders: (input) => dispatch("storage", "listProviders", "query", input),
50106
50415
  testConfig: (input) => dispatch("storage", "testConfig", "query", input)
@@ -54550,6 +54859,7 @@ exports.IntercomAbilitySchema = IntercomAbilitySchema;
54550
54859
  exports.IntercomStatusSchema = IntercomStatusSchema;
54551
54860
  exports.KNOWN_CAP_NAMES = KNOWN_CAP_NAMES;
54552
54861
  exports.KeyEventSchema = KeyEventSchema;
54862
+ exports.LEGACY_READ_ONLY_CONFIG_KEY = LEGACY_READ_ONLY_CONFIG_KEY;
54553
54863
  exports.LOAD_CONTRIBUTION_ATTRIBUTIONS = LOAD_CONTRIBUTION_ATTRIBUTIONS;
54554
54864
  exports.LOAD_CONTRIBUTION_ROLES = LOAD_CONTRIBUTION_ROLES;
54555
54865
  exports.LOG_CHANNEL_TICK_MS = LOG_CHANNEL_TICK_MS;
@@ -55001,6 +55311,8 @@ exports.SOURCE_CAP_CHANGED_AT_FIELD = SOURCE_CAP_CHANGED_AT_FIELD;
55001
55311
  exports.SOURCE_DEVICE_TYPES = SOURCE_DEVICE_TYPES;
55002
55312
  exports.SOURCE_INFO_METADATA_KEY = SOURCE_INFO_METADATA_KEY;
55003
55313
  exports.STORAGE_ACCESS_FALLBACK = STORAGE_ACCESS_FALLBACK;
55314
+ exports.STORAGE_BYTES_PER_GB = STORAGE_BYTES_PER_GB;
55315
+ exports.STORAGE_LOCATION_MODES = STORAGE_LOCATION_MODES;
55004
55316
  exports.STREAM_PROFILE_META = STREAM_PROFILE_META;
55005
55317
  exports.STREAM_QUALITY_LABELS = STREAM_QUALITY_LABELS;
55006
55318
  exports.SUB_DETECTION_TYPES = SUB_DETECTION_TYPES;
@@ -55062,9 +55374,12 @@ exports.StorageCleanupInputSchema = StorageCleanupInputSchema;
55062
55374
  exports.StorageCleanupJobSchema = StorageCleanupJobSchema;
55063
55375
  exports.StorageCleanupPhaseSchema = StorageCleanupPhaseSchema;
55064
55376
  exports.StorageCleanupStatusInputSchema = StorageCleanupStatusInputSchema;
55377
+ exports.StorageDrainProgressSchema = StorageDrainProgressSchema;
55065
55378
  exports.StorageEndDownloadInputSchema = EndDownloadInputSchema;
55379
+ exports.StorageEvictionPolicySchema = StorageEvictionPolicySchema;
55066
55380
  exports.StorageFinalizeUploadInputSchema = FinalizeUploadInputSchema;
55067
55381
  exports.StorageLocationDeclarationSchema = StorageLocationDeclarationSchema;
55382
+ exports.StorageLocationModeSchema = StorageLocationModeSchema;
55068
55383
  exports.StorageLocationRefSchema = StorageLocationRefSchema;
55069
55384
  exports.StorageLocationSchema = StorageLocationSchema;
55070
55385
  exports.StorageLocationTypeSchema = StorageLocationTypeSchema;
@@ -55088,6 +55403,7 @@ exports.StorageMigrationPhaseSchema = StorageMigrationPhaseSchema;
55088
55403
  exports.StorageMigrationPlanSchema = StorageMigrationPlanSchema;
55089
55404
  exports.StorageMigrationResidueSchema = StorageMigrationResidueSchema;
55090
55405
  exports.StorageMigrationSourcesSchema = StorageMigrationSourcesSchema;
55406
+ exports.StorageOccupancyReportSchema = StorageOccupancyReportSchema;
55091
55407
  exports.StorageProviderInfoSchema = ProviderInfoSchema;
55092
55408
  exports.StorageReadChunkInputSchema = ReadChunkInputSchema;
55093
55409
  exports.StorageTestLocationResultSchema = TestLocationResultSchema;
@@ -55369,6 +55685,8 @@ exports.evaluateZoneRules = evaluateZoneRules;
55369
55685
  exports.event = require_sleep.event;
55370
55686
  exports.eventEmitterCapability = eventEmitterCapability;
55371
55687
  exports.eventsCapability = eventsCapability;
55688
+ exports.evictionPolicyForMode = evictionPolicyForMode;
55689
+ exports.evictionPolicyOfLocation = evictionPolicyOfLocation;
55372
55690
  exports.expandCapMethods = require_sleep.expandCapMethods;
55373
55691
  exports.extractNestedAddonId = extractNestedAddonId;
55374
55692
  exports.extractSourceInfoFromMetadata = extractSourceInfoFromMetadata;
@@ -55384,6 +55702,7 @@ exports.foldSnapshotByFunction = foldSnapshotByFunction;
55384
55702
  exports.formatForBackend = formatForBackend;
55385
55703
  exports.formatForRuntime = formatForRuntime;
55386
55704
  exports.gasCapability = gasCapability;
55705
+ exports.gbToBytes = gbToBytes;
55387
55706
  exports.generateAutomationBlock = generateAutomationBlock;
55388
55707
  exports.getAudioMacroClassIds = getAudioMacroClassIds;
55389
55708
  exports.getByPath = getByPath;
@@ -55428,12 +55747,14 @@ exports.isScheduleActive = isScheduleActive;
55428
55747
  exports.isSecretConfigField = isSecretConfigField;
55429
55748
  exports.isSoftwareDecode = require_canonical_hash.isSoftwareDecode;
55430
55749
  exports.isSourceCap = isSourceCap;
55750
+ exports.isStorageLocationMode = isStorageLocationMode;
55431
55751
  exports.isSystemDelivery = isSystemDelivery;
55432
55752
  exports.isVoidInput = isVoidInput;
55433
55753
  exports.jobKindSchema = jobKindSchema;
55434
55754
  exports.kebabToCamel = kebabToCamel;
55435
55755
  exports.knownValues = knownValues;
55436
55756
  exports.lawnMowerControlCapability = lawnMowerControlCapability;
55757
+ exports.legacyModeOf = legacyModeOf;
55437
55758
  exports.lifecycleJobSchema = lifecycleJobSchema;
55438
55759
  exports.lifecycleJobScopeSchema = lifecycleJobScopeSchema;
55439
55760
  exports.lifecycleJobStateSchema = lifecycleJobStateSchema;
@@ -55456,6 +55777,8 @@ exports.mapAudioLabelToMacro = mapAudioLabelToMacro;
55456
55777
  exports.markdownToHtmlLite = markdownToHtmlLite;
55457
55778
  exports.markdownToText = markdownToText;
55458
55779
  exports.maskUrlCredentials = maskUrlCredentials;
55780
+ exports.mayReadLocation = mayReadLocation;
55781
+ exports.mayWriteToLocation = mayWriteToLocation;
55459
55782
  exports.mediaPlayerCapability = mediaPlayerCapability;
55460
55783
  exports.mergeSourceInfo = mergeSourceInfo;
55461
55784
  exports.meshNetworkCapability = meshNetworkCapability;
@@ -55463,6 +55786,8 @@ exports.method = require_sleep.method;
55463
55786
  exports.methodAccessForHttpMethod = methodAccessForHttpMethod;
55464
55787
  exports.metricsProviderCapability = metricsProviderCapability;
55465
55788
  exports.migrateLegacyDeviceSignalConfig = migrateLegacyDeviceSignalConfig;
55789
+ exports.modeMayRead = modeMayRead;
55790
+ exports.modeMayWrite = modeMayWrite;
55466
55791
  exports.modelConvertCapability = modelConvertCapability;
55467
55792
  exports.modelDistributorCapability = modelDistributorCapability;
55468
55793
  exports.modelFormatForRuntime = modelFormatForRuntime;
@@ -55558,6 +55883,7 @@ exports.resolveDeviceProfile = resolveDeviceProfile;
55558
55883
  exports.resolveEgressDecodeHwAccel = resolveEgressDecodeHwAccel;
55559
55884
  exports.resolveFormat = resolveFormat;
55560
55885
  exports.resolveHydratedFieldValue = require_sleep.resolveHydratedFieldValue;
55886
+ exports.resolveLocationMode = resolveLocationMode;
55561
55887
  exports.resolveMethodAuth = resolveMethodAuth;
55562
55888
  exports.resolveModelFormat = resolveModelFormat;
55563
55889
  exports.resolveMutate = resolveMutate;
@@ -55604,6 +55930,7 @@ exports.stateVocabularyFor = stateVocabularyFor;
55604
55930
  exports.storageCapability = storageCapability;
55605
55931
  exports.storageEvictableCapability = storageEvictableCapability;
55606
55932
  exports.storageMigrationCapability = storageMigrationCapability;
55933
+ exports.storageOccupancyCapability = storageOccupancyCapability;
55607
55934
  exports.storageProviderCapability = storageProviderCapability;
55608
55935
  exports.streamBrokerCapability = streamBrokerCapability;
55609
55936
  exports.streamCatalogCapability = streamCatalogCapability;
@@ -55611,6 +55938,7 @@ exports.streamParamsCapability = streamParamsCapability;
55611
55938
  exports.streamPixels = streamPixels;
55612
55939
  exports.streamQualityLabel = streamQualityLabel;
55613
55940
  exports.stripRetiredBandPreBufferSec = stripRetiredBandPreBufferSec;
55941
+ exports.strippedOfLegacyReadOnly = strippedOfLegacyReadOnly;
55614
55942
  exports.subKindsOf = subKindsOf;
55615
55943
  exports.summarisePrivacyAudio = summarisePrivacyAudio;
55616
55944
  exports.summarizeEffectiveScope = summarizeEffectiveScope;
@@ -55661,6 +55989,7 @@ exports.wiringHealthSnapshotSchema = wiringHealthSnapshotSchema;
55661
55989
  exports.wiringNodeHealthSchema = wiringNodeHealthSchema;
55662
55990
  exports.wiringProbeKindSchema = wiringProbeKindSchema;
55663
55991
  exports.wiringProbeResultSchema = wiringProbeResultSchema;
55992
+ exports.withLocationMode = withLocationMode;
55664
55993
  exports.zodEntriesToConfigUI = zodEntriesToConfigUI;
55665
55994
  exports.zoneAnalyticsCapability = zoneAnalyticsCapability;
55666
55995
  exports.zoneRulesCapability = zoneRulesCapability;