@camstack/types 1.2.416 → 1.2.417

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 (54) hide show
  1. package/dist/addon.js +1 -1
  2. package/dist/addon.mjs +1 -1
  3. package/dist/capabilities/air-quality-sensor.cap.d.ts +2 -8
  4. package/dist/capabilities/ambient-light-sensor.cap.d.ts +3 -14
  5. package/dist/capabilities/battery.cap.d.ts +9 -2
  6. package/dist/capabilities/binary.cap.d.ts +0 -5
  7. package/dist/capabilities/capability-definition.d.ts +6 -6
  8. package/dist/capabilities/consumables.cap.d.ts +30 -41
  9. package/dist/capabilities/control.cap.d.ts +3 -25
  10. package/dist/capabilities/core-blocks.cap.d.ts +1 -0
  11. package/dist/capabilities/customize-catalog.d.ts +0 -8
  12. package/dist/capabilities/customize-spec.d.ts +2 -3
  13. package/dist/capabilities/device-manager.cap.d.ts +79 -0
  14. package/dist/capabilities/device-provider.cap.d.ts +36 -0
  15. package/dist/capabilities/enum-sensor.cap.d.ts +5 -17
  16. package/dist/capabilities/humidity-sensor.cap.d.ts +3 -14
  17. package/dist/capabilities/index.d.ts +1 -1
  18. package/dist/capabilities/motion.cap.d.ts +1 -1
  19. package/dist/capabilities/numeric-sensor.cap.d.ts +8 -16
  20. package/dist/capabilities/pet-feeder.cap.d.ts +1 -1
  21. package/dist/capabilities/power-meter.cap.d.ts +0 -11
  22. package/dist/capabilities/pressure-sensor.cap.d.ts +3 -9
  23. package/dist/capabilities/running-state.cap.d.ts +1 -2
  24. package/dist/capabilities/runtime-field-unit.d.ts +1 -0
  25. package/dist/capabilities/runtime-field-units.d.ts +8 -0
  26. package/dist/capabilities/switch.cap.d.ts +0 -5
  27. package/dist/capabilities/temperature-sensor.cap.d.ts +5 -20
  28. package/dist/capabilities/weather.cap.d.ts +4 -13
  29. package/dist/client.d.ts +2 -1
  30. package/dist/client.js +54 -5
  31. package/dist/client.mjs +52 -6
  32. package/dist/composition/composition-report.d.ts +8 -3
  33. package/dist/composition/state-fields.d.ts +1 -1
  34. package/dist/composition/validate-composition.d.ts +3 -2
  35. package/dist/device/cap-vocabulary/types.d.ts +2 -2
  36. package/dist/device/schema-fields.d.ts +3 -0
  37. package/dist/device/source-info.d.ts +12 -6
  38. package/dist/generated/addon-cap-facts.d.ts +1 -1
  39. package/dist/generated/customize-catalog.d.ts +11 -87
  40. package/dist/generated/history-catalog.d.ts +1 -1
  41. package/dist/index.d.ts +3 -1
  42. package/dist/index.js +755 -490
  43. package/dist/index.mjs +738 -490
  44. package/dist/{sleep-BErAxhul.mjs → sleep-CARd6wZF.mjs} +161 -128
  45. package/dist/{sleep-BPQ2ZyaI.js → sleep-gtskVoMw.js} +214 -127
  46. package/dist/units/convert.d.ts +5 -0
  47. package/dist/units/format-reading.d.ts +15 -0
  48. package/dist/units/index.d.ts +8 -1
  49. package/dist/units/ingest-reading.d.ts +21 -0
  50. package/dist/units/quantity.d.ts +47 -0
  51. package/dist/units/threshold.d.ts +6 -0
  52. package/dist/units/unit-table.d.ts +2 -228
  53. package/package.json +1 -1
  54. package/dist/capabilities/advanced-parameters.d.ts +0 -17
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-BPQ2ZyaI.js");
2
+ const require_sleep = require("./sleep-gtskVoMw.js");
3
3
  const require_event_category = require("./event-category-DXZ3f9Q5.js");
4
4
  const require_composition = require("./composition-DsmkwP9-.js");
5
5
  const require_node_color = require("./node-color.js");
@@ -12398,6 +12398,17 @@ function unwrapSchema(schema) {
12398
12398
  while (s instanceof zod.z.ZodNullable || s instanceof zod.z.ZodOptional || s instanceof zod.z.ZodDefault) s = s.unwrap();
12399
12399
  return s;
12400
12400
  }
12401
+ function isMetaBag(value) {
12402
+ return typeof value === "object" && value !== null;
12403
+ }
12404
+ function metaQuantity(schema) {
12405
+ const bag = zod.z.globalRegistry.get(schema);
12406
+ if (isMetaBag(bag) && require_sleep.isReadingQuantity(bag.quantity)) return bag.quantity;
12407
+ }
12408
+ /** Quantity declared with `readingMeta` on the field or on the unwrapped leaf. */
12409
+ function quantityOfField(schema) {
12410
+ return metaQuantity(schema) ?? metaQuantity(unwrapSchema(schema));
12411
+ }
12401
12412
  function leafKind(schema) {
12402
12413
  const s = unwrapSchema(schema);
12403
12414
  if (s instanceof zod.z.ZodEnum) return {
@@ -12426,11 +12437,15 @@ function enumerateSchemaFields(schema, prefix = "") {
12426
12437
  continue;
12427
12438
  }
12428
12439
  const leaf = leafKind(field);
12429
- if (leaf) out.push({
12430
- path,
12431
- kind: leaf.kind,
12432
- ...leaf.enumValues ? { enumValues: leaf.enumValues } : {}
12433
- });
12440
+ if (leaf) {
12441
+ const quantity = quantityOfField(field);
12442
+ out.push({
12443
+ path,
12444
+ kind: leaf.kind,
12445
+ ...leaf.enumValues ? { enumValues: leaf.enumValues } : {},
12446
+ ...quantity !== void 0 ? { quantity } : {}
12447
+ });
12448
+ }
12434
12449
  }
12435
12450
  return out;
12436
12451
  }
@@ -12491,7 +12506,7 @@ function requiredTopLevelKeys(schema) {
12491
12506
  /**
12492
12507
  * The proper prefixes of `path` whose schema accepts `undefined`: a required
12493
12508
  * leaf under one of them is required only once something writes that parent
12494
- * (`remaining.unit` is required only when `remaining` is present).
12509
+ * (`remaining.kind` is required only when `remaining` is present).
12495
12510
  */
12496
12511
  function optionalAncestors(schema, path) {
12497
12512
  const segments = path.split(".");
@@ -12608,7 +12623,8 @@ var CompositionProblemCodeSchema = zod.z.enum([
12608
12623
  "item-field-reserved",
12609
12624
  "target-missing",
12610
12625
  "target-unreadable",
12611
- "not-replaceable"
12626
+ "not-replaceable",
12627
+ "native-capability-replace"
12612
12628
  ]);
12613
12629
  /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.celsius`. */
12614
12630
  var CompositionProblemSchema = zod.z.object({
@@ -12674,9 +12690,11 @@ function compositionFieldKey(plan) {
12674
12690
  return `${plan.cap}.${path}`;
12675
12691
  }
12676
12692
  /**
12677
- * What a feature does to its target (D663): `new` builds a capability on a new
12678
- * device; on an existing device it `add`s a capability the device lacks, or
12679
- * `replace`s named fields of one it has natively.
12693
+ * What a feature does to its target (D663, D944): `new` builds a capability on a
12694
+ * new device; on an existing device it `add`s a capability the device lacks.
12695
+ * Replacing a capability the device already serves is refused
12696
+ * (`native-capability-replace`), not planned. `replace` remains on the claim
12697
+ * wire for a claim the hub already holds.
12680
12698
  */
12681
12699
  var CompositionFeatureModeSchema = zod.z.enum([
12682
12700
  "new",
@@ -13510,6 +13528,9 @@ function isNativeCap(target, cap) {
13510
13528
  const owner = target.nativeCaps.get(cap);
13511
13529
  return owner !== void 0 && owner !== "composer";
13512
13530
  }
13531
+ function isPlanProblem(value) {
13532
+ return "code" in value;
13533
+ }
13513
13534
  /** The target read, or the refusal that replaces the whole plan when it could not be (D391). */
13514
13535
  function planTargetOf(composition, target) {
13515
13536
  if (composition.target.kind === "new") return {
@@ -13525,26 +13546,59 @@ function planTargetOf(composition, target) {
13525
13546
  found: target
13526
13547
  };
13527
13548
  }
13528
- function featureTargetOf(target, cap) {
13549
+ var NATIVE_REPLACE_KEPT = new Set([
13550
+ "not-composable",
13551
+ "unknown-capability",
13552
+ "reserved-variant"
13553
+ ]);
13554
+ /** A native override is refused, unless the cap cannot be composed at all — that reason stays. */
13555
+ function nativeReplaceOrBlock(refusal, probed) {
13556
+ const kept = probed.problems.filter((item) => NATIVE_REPLACE_KEPT.has(item.code));
13557
+ if (kept.length > 0) return {
13558
+ problems: kept,
13559
+ fields: [],
13560
+ features: [],
13561
+ events: []
13562
+ };
13563
+ return refused(refusal);
13564
+ }
13565
+ function featureTargetOf(target, cap, at) {
13529
13566
  if (target.kind === "new") return {
13530
13567
  mode: "new",
13531
13568
  deviceType: target.type
13532
13569
  };
13570
+ if (isNativeCap(target.found, cap)) return problem("native-capability-replace", at, `native-capability-replace: stored override replaces native capability \`${cap}\` on device ${target.found.deviceId} — refused (D944)`);
13533
13571
  return {
13534
- mode: isNativeCap(target.found, cap) ? "replace" : "add",
13572
+ mode: "add",
13535
13573
  deviceType: target.found.type
13536
13574
  };
13537
13575
  }
13538
13576
  /**
13539
13577
  * The static plan. `target` describes an EXISTING target device (one read);
13540
- * it is ignored for a new one. On an existing target each feature REPLACES a
13541
- * cap the device has natively, else ADDS it (D663).
13578
+ * it is ignored for a new one. On an existing target each feature ADDS a cap
13579
+ * the device lacks. A feature that would replace a native capability is
13580
+ * refused by name (D944). A new device is unchanged.
13542
13581
  */
13543
13582
  function planComposition(composition, lookupCap, target) {
13544
13583
  const planTarget = planTargetOf(composition, target);
13545
13584
  if ("code" in planTarget) return refused(planTarget);
13546
13585
  const results = composition.features.map((feature, index) => {
13547
- const result = feature.kind === "passthrough" ? refused(problem("reserved-variant", `features[${index}]`, `whole-capability passthrough of \`${feature.cap}\` arrives in slice 3`)) : planFeature(feature, index, featureTargetOf(planTarget, feature.cap), lookupCap);
13586
+ const at = `features[${index}]`;
13587
+ if (feature.kind === "passthrough") return refused(problem("reserved-variant", at, `whole-capability passthrough of \`${feature.cap}\` arrives in slice 3`));
13588
+ const featureTarget = featureTargetOf(planTarget, feature.cap, at);
13589
+ if (isPlanProblem(featureTarget)) {
13590
+ if (!require_composition.compositionFeatureEnabled(feature) || planTarget.kind !== "existing") return {
13591
+ problems: [],
13592
+ fields: [],
13593
+ features: [],
13594
+ events: []
13595
+ };
13596
+ return nativeReplaceOrBlock(featureTarget, planFeature(feature, index, {
13597
+ mode: "add",
13598
+ deviceType: planTarget.found.type
13599
+ }, lookupCap));
13600
+ }
13601
+ const result = planFeature(feature, index, featureTarget, lookupCap);
13548
13602
  return require_composition.compositionFeatureEnabled(feature) ? result : {
13549
13603
  problems: result.problems,
13550
13604
  fields: [],
@@ -13685,7 +13739,11 @@ function cycleProblems(composition, ctx, fields, self) {
13685
13739
  function validateComposition(composition, ctx) {
13686
13740
  const plan = planComposition(composition, ctx.lookupCap, ctx.target ?? void 0);
13687
13741
  const self = candidateSelf(composition, ctx, plan);
13688
- const cycles = cycleProblems(composition, ctx, plan.fields, self);
13742
+ const refusedFeatures = plan.problems.filter((item) => item.code === "native-capability-replace").map((item) => item.path);
13743
+ const cycles = cycleProblems(composition, ctx, plan.fields, self).filter((item) => {
13744
+ if (refusedFeatures.some((at) => item.path === at || item.path.startsWith(`${at}.`))) return false;
13745
+ return refusedFeatures.length === 0 || item.path !== "features";
13746
+ });
13689
13747
  const problems = [
13690
13748
  ...plan.problems,
13691
13749
  ...sourceProblems(plan.fields, ctx, self),
@@ -16432,12 +16490,12 @@ function parseAdminBase(baseUrl) {
16432
16490
  * providers detect renames by matching `uniqueId` and patch `id`
16433
16491
  * in-place so dispatch keeps working.
16434
16492
  *
16435
- * **Rendering metadata is NOT stored here.** Display fields (`unit`,
16436
- * `precision`) live in the cap STATUS SLICE so they flow live with every
16437
- * state push and are the single source of truth. The `device_class` tag is
16438
- * an HA-specific input that is consumed at the discovery boundary
16439
- * (`ha-domain-mapping`) to derive the CamStack `DeviceRole`; it is never
16440
- * stored after that point.
16493
+ * **Display unit and precision are NOT stored here.** They are a read-time
16494
+ * preference (D944). A polymorphic reading stores its `quantity` here once,
16495
+ * at adopt, and a date/time value stores `dateTimeShape` the same way —
16496
+ * neither is repeated on each sample. The `device_class` tag is an
16497
+ * HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
16498
+ * to derive the CamStack `DeviceRole`; it is never stored after that point.
16441
16499
  *
16442
16500
  * The `raw` escape hatch carries provider-specific attributes that don't
16443
16501
  * fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
@@ -16451,6 +16509,14 @@ var SourceInfoSchema = zod.z.object({
16451
16509
  /** Immutable upstream identifier when available (HA `unique_id`, … ).
16452
16510
  * Used to detect rename when `id` changes. */
16453
16511
  uniqueId: zod.z.string().optional(),
16512
+ /** Quantity of a polymorphic reading, written once at adopt (D944). */
16513
+ quantity: require_sleep.ReadingQuantitySchema.optional(),
16514
+ /** Date / time / datetime shape of a string reading, written once at adopt. */
16515
+ dateTimeShape: zod.z.enum([
16516
+ "date",
16517
+ "time",
16518
+ "datetime"
16519
+ ]).optional(),
16454
16520
  /** Provider-specific extras that don't fit the structured slots.
16455
16521
  * Whitelist what goes here per provider — do NOT dump entire HA
16456
16522
  * attribute blobs (would explode the meta JSON column). */
@@ -16535,9 +16601,38 @@ function normalizeUnit(raw) {
16535
16601
  if (lower === "mph") return "mph";
16536
16602
  if (lower === "m/s") return "m/s";
16537
16603
  if (lower === "m") return "m";
16604
+ if (lower === "m²" || lower === "m2") return "m²";
16605
+ if (lower === "mm") return "mm";
16606
+ if (lower === "cm") return "cm";
16538
16607
  if (lower === "km") return "km";
16608
+ if (lower === "in" || lower === "inch" || lower === "inches") return "in";
16609
+ if (lower === "ft" || lower === "foot" || lower === "feet") return "ft";
16539
16610
  if (lower === "kg") return "kg";
16540
16611
  if (lower === "g") return "g";
16612
+ if (lower === "l" || lower === "liter" || lower === "litre" || lower === "liters" || lower === "litres") return "L";
16613
+ if (lower === "ml") return "mL";
16614
+ if (lower === "gal" || lower === "gallon" || lower === "gallons") return "gal";
16615
+ if (lower === "s" || lower === "sec" || lower === "second" || lower === "seconds") return "s";
16616
+ if (lower === "ms") return "ms";
16617
+ if (lower === "min" || lower === "minute" || lower === "minutes") return "min";
16618
+ if (lower === "h" || lower === "hr" || lower === "hour" || lower === "hours") return "h";
16619
+ if (lower === "d" || lower === "day" || lower === "days") return "d";
16620
+ if (lower === "b" || lower === "byte" || lower === "bytes") return "B";
16621
+ if (lower === "kib") return "KiB";
16622
+ if (lower === "mib") return "MiB";
16623
+ if (lower === "gib") return "GiB";
16624
+ if (lower === "tib") return "TiB";
16625
+ if (lower === "kb") return "kB";
16626
+ if (lower === "mb") return "MB";
16627
+ if (lower === "gb") return "GB";
16628
+ if (lower === "dbm") return "dBm";
16629
+ if (lower === "dbfs") return "dBFS";
16630
+ if (lower === "inhg") return "inHg";
16631
+ if (lower === "mm/h") return "mm/h";
16632
+ if (lower === "w/m²" || lower === "w/m2") return "W/m²";
16633
+ if (lower === "°" || lower === "deg" || lower === "degree" || lower === "degrees") return "°";
16634
+ if (lower === "kbps") return "kbps";
16635
+ if (lower === "fps") return "fps";
16541
16636
  return trimmed;
16542
16637
  }
16543
16638
  /**
@@ -37969,7 +38064,8 @@ var deviceManagerCapability = {
37969
38064
  "enum"
37970
38065
  ]),
37971
38066
  enumValues: zod.z.array(zod.z.string()).optional(),
37972
- item: zod.z.boolean().optional()
38067
+ item: zod.z.boolean().optional(),
38068
+ quantity: require_sleep.ReadingQuantitySchema.optional()
37973
38069
  })).readonly(),
37974
38070
  itemArray: zod.z.object({
37975
38071
  path: zod.z.string(),
@@ -44293,8 +44389,8 @@ var MaskPolygonShapeSchema = zod.z.object({
44293
44389
  /** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
44294
44390
  var MaskGridShapeSchema = zod.z.object({
44295
44391
  kind: zod.z.literal("grid"),
44296
- gridWidth: zod.z.number(),
44297
- gridHeight: zod.z.number(),
44392
+ gridWidth: zod.z.number().meta(require_sleep.readingMeta("none")),
44393
+ gridHeight: zod.z.number().meta(require_sleep.readingMeta("none")),
44298
44394
  cells: zod.z.array(zod.z.boolean())
44299
44395
  });
44300
44396
  /** Directed line / tripwire — an ordered list of normalized points (≥2). */
@@ -60558,27 +60654,17 @@ var addonsCapability = {
60558
60654
  */
60559
60655
  var AirQualitySensorStatusSchema = zod.z.object({
60560
60656
  /** Carbon dioxide concentration in ppm. */
60561
- co2Ppm: zod.z.number().min(0).optional(),
60657
+ co2Ppm: zod.z.number().min(0).optional().meta(require_sleep.readingMeta("concentration-volume")),
60562
60658
  /** Total volatile organic compounds in ppb. */
60563
- vocPpb: zod.z.number().min(0).optional(),
60659
+ vocPpb: zod.z.number().min(0).optional().meta(require_sleep.readingMeta("concentration-ppb")),
60564
60660
  /** Particulate matter ≤ 2.5 μm in µg/m³. */
60565
- pm25: zod.z.number().min(0).optional(),
60661
+ pm25: zod.z.number().min(0).optional().meta(require_sleep.readingMeta("concentration-mass")),
60566
60662
  /** Particulate matter ≤ 10 μm in µg/m³. */
60567
- pm10: zod.z.number().min(0).optional(),
60663
+ pm10: zod.z.number().min(0).optional().meta(require_sleep.readingMeta("concentration-mass")),
60568
60664
  /** Composite AQI value (typically 0..500). */
60569
- aqi: zod.z.number().optional(),
60665
+ aqi: zod.z.number().optional().meta(require_sleep.readingMeta("index")),
60570
60666
  /** Ms epoch when the slice was last updated. */
60571
- lastFetchedAt: zod.z.number(),
60572
- /** Live display unit of the single metric this slice carries (e.g. HA
60573
- * `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
60574
- * upstream `sensor.*` entity surfaces ONE device_class, so one unit
60575
- * per slice is unambiguous. */
60576
- unit: zod.z.string().optional(),
60577
- /** Suggested decimal places for numeric display.
60578
- * Populated live from the upstream source when provided (e.g. HA
60579
- * `attributes.suggested_display_precision`). Falls back to
60580
- * auto-formatting when absent. */
60581
- precision: zod.z.number().int().min(0).max(10).optional()
60667
+ lastFetchedAt: zod.z.number()
60582
60668
  });
60583
60669
  var airQualitySensorCapability = {
60584
60670
  name: "air-quality-sensor",
@@ -60615,12 +60701,12 @@ var airQualitySensorCapability = {
60615
60701
  },
60616
60702
  pm25: {
60617
60703
  kind: "gauge",
60618
- unit: "ug/m3",
60704
+ unit: "µg/m³",
60619
60705
  deadband: 1
60620
60706
  },
60621
60707
  pm10: {
60622
60708
  kind: "gauge",
60623
- unit: "ug/m3",
60709
+ unit: "µg/m³",
60624
60710
  deadband: 1
60625
60711
  },
60626
60712
  aqi: {
@@ -60640,22 +60726,13 @@ var airQualitySensorCapability = {
60640
60726
  * entries with `device_class: illuminance`.
60641
60727
  */
60642
60728
  var AmbientLightSensorStatusSchema = zod.z.object({
60643
- /** Current illuminance in lux (lx). */
60644
- lux: zod.z.number().min(0).meta({
60645
- unit: "lx",
60729
+ /** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
60730
+ lux: zod.z.number().min(0).nullable().meta(require_sleep.readingMeta("illuminance", {
60731
+ precision: 0,
60646
60732
  label: "Illuminance"
60647
- }),
60733
+ })),
60648
60734
  /** Ms epoch when the slice was last updated. */
60649
- lastFetchedAt: zod.z.number(),
60650
- /** Live display unit from the upstream source (e.g. HA
60651
- * `attributes.unit_of_measurement`). The UI prefers this over the
60652
- * role's canonical unit. Absent → fall back to the canonical unit. */
60653
- unit: zod.z.string().optional(),
60654
- /** Suggested decimal places for numeric display.
60655
- * Populated live from the upstream source when provided (e.g. HA
60656
- * `attributes.suggested_display_precision`). Falls back to
60657
- * auto-formatting when absent. */
60658
- precision: zod.z.number().int().min(0).max(10).optional()
60735
+ lastFetchedAt: zod.z.number()
60659
60736
  });
60660
60737
  var ambientLightSensorCapability = {
60661
60738
  name: "ambient-light-sensor",
@@ -60687,12 +60764,7 @@ var ambientLightSensorCapability = {
60687
60764
  } } },
60688
60765
  /** Clock fields: written, but excluded from the compare that decides
60689
60766
  * whether persisting is worth a SQLite commit. */
60690
- volatileStateFields: ["lastFetchedAt"],
60691
- customize: {
60692
- version: 1,
60693
- advanced: "derive",
60694
- targets: []
60695
- }
60767
+ volatileStateFields: ["lastFetchedAt"]
60696
60768
  };
60697
60769
  //#endregion
60698
60770
  //#region src/capabilities/audio-metrics.cap.ts
@@ -60934,10 +61006,7 @@ var BatteryStatusSchema = zod.z.object({
60934
61006
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
60935
61007
  * declines to band a non-finite reading, which is the correct shape.
60936
61008
  */
60937
- percentage: zod.z.number().min(0).max(100).nullable().meta({
60938
- unit: "%",
60939
- label: "Charge"
60940
- }),
61009
+ percentage: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage", { label: "Charge" })),
60941
61010
  /**
60942
61011
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
60943
61012
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -60964,7 +61033,14 @@ var BatteryStatusSchema = zod.z.object({
60964
61033
  */
60965
61034
  sleeping: zod.z.boolean().meta({ label: "Sleeping" }),
60966
61035
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
60967
- lastUpdated: zod.z.number(),
61036
+ lastUpdated: zod.z.number().meta(require_sleep.readingMeta("none")),
61037
+ /**
61038
+ * Battery voltage in volts, for a cell the firmware reports as voltage
61039
+ * rather than a percentage (Ecowitt capacitor packs). Absent on cameras
61040
+ * that only report charge. A second reading on this cap, not a second
61041
+ * `numeric-sensor` on the same device.
61042
+ */
61043
+ volts: zod.z.number().nullable().optional().meta(require_sleep.readingMeta("voltage", { label: "Voltage" })),
60968
61044
  /**
60969
61045
  * Ms epoch of the last time the device PROVED it was reachable — a
60970
61046
  * completed firmware round-trip, an observed wake, or an inbound push
@@ -61016,7 +61092,6 @@ var BATTERY_DEVICE_TYPES = [
61016
61092
  /** Generic Customize target for the percentage reading (D936). */
61017
61093
  var batteryCustomize = {
61018
61094
  version: 1,
61019
- advanced: "derive",
61020
61095
  targets: [{
61021
61096
  id: "percentage",
61022
61097
  cardinality: "scalar",
@@ -61025,7 +61100,7 @@ var batteryCustomize = {
61025
61100
  stateField: "percentage"
61026
61101
  },
61027
61102
  deviceTypes: BATTERY_DEVICE_TYPES,
61028
- apply: "add-or-replace",
61103
+ apply: "add",
61029
61104
  path: "percentage",
61030
61105
  sources: [{
61031
61106
  kind: "field",
@@ -61146,10 +61221,16 @@ onStatusChanged: { data: zod.z.object({
61146
61221
  /**
61147
61222
  * History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
61148
61223
  */
61149
- history: { fields: { percentage: {
61150
- kind: "gauge",
61151
- unit: "%"
61152
- } } },
61224
+ history: { fields: {
61225
+ percentage: {
61226
+ kind: "gauge",
61227
+ unit: "%"
61228
+ },
61229
+ volts: {
61230
+ kind: "gauge",
61231
+ unit: "V"
61232
+ }
61233
+ } },
61153
61234
  customize: batteryCustomize,
61154
61235
  /** Clock fields: written, but excluded from the compare that decides
61155
61236
  * whether persisting is worth a SQLite commit. */
@@ -61198,12 +61279,7 @@ var binaryCapability = {
61198
61279
  history: { fields: { on: { kind: "boolean" } } },
61199
61280
  /** Clock fields: written, but excluded from the compare that decides
61200
61281
  * whether persisting is worth a SQLite commit. */
61201
- volatileStateFields: ["lastChangedAt"],
61202
- customize: {
61203
- version: 1,
61204
- advanced: "derive",
61205
- targets: []
61206
- }
61282
+ volatileStateFields: ["lastChangedAt"]
61207
61283
  };
61208
61284
  //#endregion
61209
61285
  //#region src/capabilities/brightness.cap.ts
@@ -61221,7 +61297,7 @@ var binaryCapability = {
61221
61297
  */
61222
61298
  var BrightnessStatusSchema = zod.z.object({
61223
61299
  /** Current level as 0..100 inclusive. Firmware-reported. */
61224
- percentage: zod.z.number().min(0).max(100),
61300
+ percentage: zod.z.number().min(0).max(100).meta(require_sleep.readingMeta("percentage")),
61225
61301
  /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
61226
61302
  lastChangedAt: zod.z.number()
61227
61303
  });
@@ -61820,21 +61896,21 @@ var ClimateControlStatusSchema = zod.z.object({
61820
61896
  /** Single setpoint in Celsius. Used by single-target modes
61821
61897
  * (heat / cool / dry). Null when the device is in a range mode
61822
61898
  * (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
61823
- target: zod.z.number().nullable(),
61899
+ target: zod.z.number().nullable().meta(require_sleep.readingMeta("temperature")),
61824
61900
  /** Upper bound of the dual setpoint in Celsius. Populated only
61825
61901
  * in `heat_cool` mode. */
61826
- targetHigh: zod.z.number().nullable(),
61902
+ targetHigh: zod.z.number().nullable().meta(require_sleep.readingMeta("temperature")),
61827
61903
  /** Lower bound of the dual setpoint in Celsius. Populated only
61828
61904
  * in `heat_cool` mode. */
61829
- targetLow: zod.z.number().nullable(),
61905
+ targetLow: zod.z.number().nullable().meta(require_sleep.readingMeta("temperature")),
61830
61906
  /** Target relative humidity (0..100). Null when the device has no
61831
61907
  * humidity control. */
61832
- targetHumidity: zod.z.number().min(0).max(100).nullable(),
61908
+ targetHumidity: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("humidity")),
61833
61909
  /** Current measured temperature in Celsius. Read-only — pushed by
61834
61910
  * the device's internal sensor. */
61835
- currentTemp: zod.z.number().nullable(),
61911
+ currentTemp: zod.z.number().nullable().meta(require_sleep.readingMeta("temperature")),
61836
61912
  /** Current measured relative humidity (0..100). Read-only. */
61837
- currentHumidity: zod.z.number().min(0).max(100).nullable(),
61913
+ currentHumidity: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("humidity")),
61838
61914
  /** Vertical louver swing on/off. Null when the device has no vertical
61839
61915
  * swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
61840
61916
  swingVertical: zod.z.boolean().nullable(),
@@ -61842,11 +61918,11 @@ var ClimateControlStatusSchema = zod.z.object({
61842
61918
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
61843
61919
  swingHorizontal: zod.z.boolean().nullable(),
61844
61920
  /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
61845
- minTemp: zod.z.number().nullable().optional(),
61921
+ minTemp: zod.z.number().nullable().optional().meta(require_sleep.readingMeta("temperature")),
61846
61922
  /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
61847
- maxTemp: zod.z.number().nullable().optional(),
61923
+ maxTemp: zod.z.number().nullable().optional().meta(require_sleep.readingMeta("temperature")),
61848
61924
  /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
61849
- tempStep: zod.z.number().positive().nullable().optional(),
61925
+ tempStep: zod.z.number().positive().nullable().optional().meta(require_sleep.readingMeta("temperature")),
61850
61926
  /** Ms epoch when the slice was last updated (push or command). */
61851
61927
  lastFetchedAt: zod.z.number()
61852
61928
  });
@@ -61990,17 +62066,17 @@ var climateControlCapability = {
61990
62066
  * modes to expose in the picker without a slice fetch.
61991
62067
  */
61992
62068
  var RgbTripletSchema = zod.z.object({
61993
- r: zod.z.number().int().min(0).max(255),
61994
- g: zod.z.number().int().min(0).max(255),
61995
- b: zod.z.number().int().min(0).max(255)
62069
+ r: zod.z.number().int().min(0).max(255).meta(require_sleep.readingMeta("none")),
62070
+ g: zod.z.number().int().min(0).max(255).meta(require_sleep.readingMeta("none")),
62071
+ b: zod.z.number().int().min(0).max(255).meta(require_sleep.readingMeta("none"))
61996
62072
  });
61997
62073
  var HsvTripletSchema = zod.z.object({
61998
62074
  /** Hue in degrees, 0..360. */
61999
- h: zod.z.number().min(0).max(360),
62075
+ h: zod.z.number().min(0).max(360).meta(require_sleep.readingMeta("none")),
62000
62076
  /** Saturation as 0..100 inclusive. */
62001
- s: zod.z.number().min(0).max(100),
62077
+ s: zod.z.number().min(0).max(100).meta(require_sleep.readingMeta("none")),
62002
62078
  /** Value/brightness as 0..100 inclusive. */
62003
- v: zod.z.number().min(0).max(100)
62079
+ v: zod.z.number().min(0).max(100).meta(require_sleep.readingMeta("none"))
62004
62080
  });
62005
62081
  var ColorInputSchema = zod.z.discriminatedUnion("mode", [
62006
62082
  zod.z.object({
@@ -62029,7 +62105,7 @@ var ColorStatusSchema = zod.z.object({
62029
62105
  /** Populated when `mode === 'hsv'`. */
62030
62106
  hsv: HsvTripletSchema.optional(),
62031
62107
  /** Populated when `mode === 'mired'`. */
62032
- mireds: zod.z.number().int().optional(),
62108
+ mireds: zod.z.number().int().optional().meta(require_sleep.readingMeta("colour-temperature")),
62033
62109
  /** Ms epoch of the last operator-driven change. */
62034
62110
  lastChangedAt: zod.z.number()
62035
62111
  });
@@ -62311,18 +62387,12 @@ var FORM_OF_KIND = {
62311
62387
  function consumableFormOf(item) {
62312
62388
  return item.form ?? FORM_OF_KIND[consumableKindOf(item)];
62313
62389
  }
62314
- /** Units a remaining-life counter is reported in. Closed: the panel renders
62315
- * each. */
62316
- var ConsumableRemainingUnitSchema = zod.z.enum([
62317
- "days",
62318
- "hours",
62319
- "cycles"
62320
- ]);
62321
- /** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
62322
- * days). `value` null = unknown (D393). */
62390
+ /** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
62391
+ var ConsumableRemainingKindSchema = zod.z.enum(["duration", "count"]);
62392
+ /** Remaining life. `value` null = unknown (D393). Duration is seconds. */
62323
62393
  var ConsumableRemainingSchema = zod.z.object({
62324
- value: zod.z.number().min(0).nullable(),
62325
- unit: ConsumableRemainingUnitSchema
62394
+ value: zod.z.number().min(0).nullable().meta(require_sleep.readingMeta("device")),
62395
+ kind: ConsumableRemainingKindSchema
62326
62396
  });
62327
62397
  /** A single consumable item. Either a continuous `level` (remaining
62328
62398
  * life %, or fill % for a `supply` / `waste` item) or a discrete `status`
@@ -62335,7 +62405,7 @@ var ConsumableItemSchema = zod.z.object({
62335
62405
  /** Display name. */
62336
62406
  label: zod.z.string().min(1),
62337
62407
  /** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
62338
- level: zod.z.number().min(0).max(100).nullable(),
62408
+ level: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
62339
62409
  /** The provider's discrete verdict when known (binary mode). */
62340
62410
  status: ConsumableStatusSchema.nullable(),
62341
62411
  /** What the item is — decides whether low or full is bad. Absent = `wear`. */
@@ -62417,8 +62487,8 @@ var consumablesCustomize = {
62417
62487
  transforms: [{ kind: "identity" }]
62418
62488
  }],
62419
62489
  literals: [{
62420
- path: "remaining.unit",
62421
- values: [...ConsumableRemainingUnitSchema.options]
62490
+ path: "remaining.kind",
62491
+ values: [...ConsumableRemainingKindSchema.options]
62422
62492
  }]
62423
62493
  },
62424
62494
  {
@@ -62547,7 +62617,6 @@ reset: require_sleep.method(zod.z.object({
62547
62617
  },
62548
62618
  "remaining.value": {
62549
62619
  kind: "gauge",
62550
- unitFrom: "remaining.unit",
62551
62620
  deadband: 1,
62552
62621
  displayName: "Remaining"
62553
62622
  }
@@ -62650,49 +62719,26 @@ var ControlKindSchema = zod.z.enum([
62650
62719
  * (empty array for the other kinds). It lives in the slice because
62651
62720
  * HA `select.*` entities may update their options at runtime.
62652
62721
  *
62653
- * The `unit`/`min`/`max`/`step` fields are meaningful for
62654
- * `kind === 'numeric'` and are absent (undefined) for all other
62655
- * kinds. They are populated live from HA attributes on each state
62656
- * push — consistent with `options` and the sensor-unit approach.
62722
+ * Numeric `min` / `max` / `step` live in the slice and share the device
62723
+ * quantity (`SourceInfo.quantity`, D944). Display precision is a read-time
62724
+ * preference. A datetime control's shape is `SourceInfo.dateTimeShape`.
62657
62725
  */
62658
62726
  var ControlStatusSchema = zod.z.object({
62659
62727
  kind: ControlKindSchema,
62660
62728
  /** Current value. Numeric kind → number; select/text/datetime → string.
62661
62729
  * Datetime values are ISO 8601 strings (full timestamp, date-only,
62662
62730
  * or time-only — HA accepts all three forms). */
62663
- value: zod.z.union([zod.z.number(), zod.z.string()]),
62731
+ value: zod.z.union([zod.z.number().meta(require_sleep.readingMeta("device")), zod.z.string()]),
62664
62732
  /** Acceptable values when `kind === 'select'`. Empty for other kinds. */
62665
62733
  options: zod.z.array(zod.z.string()),
62666
62734
  /** Ms epoch when the slice was last updated. */
62667
62735
  lastChangedAt: zod.z.number(),
62668
- /** Display unit-of-measurement (e.g. '°C', '%', 'lx').
62669
- * Populated live from HA `attributes.unit_of_measurement` on each push.
62670
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
62671
- unit: zod.z.string().optional(),
62672
- /** Minimum allowed value. Populated live from HA `attributes.min`.
62673
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
62674
- min: zod.z.number().optional(),
62675
- /** Maximum allowed value. Populated live from HA `attributes.max`.
62676
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
62677
- max: zod.z.number().optional(),
62678
- /** Step increment. Populated live from HA `attributes.step`.
62679
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
62680
- step: zod.z.number().optional(),
62681
- /** Suggested decimal places for numeric display. Populated live from HA
62682
- * `attributes.suggested_display_precision`, or derived from `step` when the
62683
- * HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
62684
- * other kinds. Falls back to auto-formatting when absent. */
62685
- precision: zod.z.number().int().min(0).max(10).optional(),
62686
- /** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
62687
- * 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
62688
- * (UI defaults to 'datetime'). Derived live from the device's HA domain
62689
- * (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
62690
- * `has_date`/`has_time` attributes on each push. */
62691
- format: zod.z.enum([
62692
- "date",
62693
- "time",
62694
- "datetime"
62695
- ]).optional()
62736
+ /** Minimum allowed value, in the device quantity's base unit. */
62737
+ min: zod.z.number().optional().meta(require_sleep.readingMeta("device")),
62738
+ /** Maximum allowed value, in the device quantity's base unit. */
62739
+ max: zod.z.number().optional().meta(require_sleep.readingMeta("device")),
62740
+ /** Step increment, in the device quantity's base unit. */
62741
+ step: zod.z.number().optional().meta(require_sleep.readingMeta("device"))
62696
62742
  });
62697
62743
  var ControlSetValueInputSchema = zod.z.discriminatedUnion("kind", [
62698
62744
  zod.z.object({
@@ -62782,9 +62828,9 @@ var CoverStatusSchema = zod.z.object({
62782
62828
  state: CoverStateSchema,
62783
62829
  /** 0 = fully closed, 100 = fully open. Null when the device has
62784
62830
  * no intermediate position surface. */
62785
- position: zod.z.number().min(0).max(100).nullable(),
62831
+ position: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
62786
62832
  /** 0 = closed slats, 100 = open slats. Null when no tilt support. */
62787
- tiltPosition: zod.z.number().min(0).max(100).nullable(),
62833
+ tiltPosition: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
62788
62834
  /** Ms epoch when the slice was last updated. */
62789
62835
  lastChangedAt: zod.z.number()
62790
62836
  });
@@ -62889,9 +62935,9 @@ var NormalizedRangeSchema$1 = zod.z.object({
62889
62935
  var DayNightStatusSchema = zod.z.object({
62890
62936
  mode: DayNightModeSchema,
62891
62937
  /** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
62892
- sensitivity: zod.z.number().optional(),
62938
+ sensitivity: zod.z.number().optional().meta(require_sleep.readingMeta("none")),
62893
62939
  /** Delay before the IR-cut filter flips, in seconds. */
62894
- switchDelaySec: zod.z.number().optional(),
62940
+ switchDelaySec: zod.z.number().optional().meta(require_sleep.readingMeta("duration")),
62895
62941
  lastFetchedAt: zod.z.number()
62896
62942
  });
62897
62943
  /**
@@ -63124,11 +63170,11 @@ var DishwasherStatusSchema = zod.z.object({
63124
63170
  /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
63125
63171
  availablePrograms: zod.z.array(zod.z.string()),
63126
63172
  /** Seconds remaining on the active cycle, or null when not running / not known. */
63127
- remainingSeconds: zod.z.number().int().nonnegative().nullable(),
63173
+ remainingSeconds: zod.z.number().int().nonnegative().nullable().meta(require_sleep.readingMeta("duration")),
63128
63174
  /** Seconds the delayed start is set to (time until the cycle starts), or null. */
63129
- delayedStartSeconds: zod.z.number().int().nonnegative().nullable(),
63175
+ delayedStartSeconds: zod.z.number().int().nonnegative().nullable().meta(require_sleep.readingMeta("duration")),
63130
63176
  /** Cycle completion 0..100, or null when not applicable. */
63131
- progressPercent: zod.z.number().min(0).max(100).nullable(),
63177
+ progressPercent: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
63132
63178
  /** True when the door is reported open; null when the door state is not known. */
63133
63179
  doorOpen: zod.z.boolean().nullable(),
63134
63180
  /** True when the door is latched (locked) during a cycle; null when not reported. */
@@ -63152,7 +63198,7 @@ var DishwasherStatusSchema = zod.z.object({
63152
63198
  /** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
63153
63199
  machineCareDue: zod.z.boolean().nullable(),
63154
63200
  /** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
63155
- machineCareRemainingRuns: zod.z.number().int().nonnegative().nullable(),
63201
+ machineCareRemainingRuns: zod.z.number().int().nonnegative().nullable().meta(require_sleep.readingMeta("count")),
63156
63202
  /** Option states: null when the appliance does not expose the option or has not said. */
63157
63203
  halfLoad: zod.z.boolean().nullable(),
63158
63204
  hygienePlus: zod.z.boolean().nullable(),
@@ -63167,7 +63213,7 @@ var DishwasherStatusSchema = zod.z.object({
63167
63213
  /** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
63168
63214
  activeEvents: zod.z.array(zod.z.string()),
63169
63215
  /** Numeric error code when the appliance reports one; null otherwise. */
63170
- errorCode: zod.z.number().nullable(),
63216
+ errorCode: zod.z.number().nullable().meta(require_sleep.readingMeta("none")),
63171
63217
  /** Name of the raised alert-level event while the appliance is in error; null otherwise. */
63172
63218
  errorLabel: zod.z.string().nullable(),
63173
63219
  /** Ms epoch of the last value received from the appliance; 0 = never. */
@@ -63402,7 +63448,7 @@ var DoorbellStatusSchema = zod.z.object({
63402
63448
  /** Ms epoch of the last press. null = never observed since this provider started. */
63403
63449
  lastPressedAt: zod.z.number().nullable(),
63404
63450
  /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
63405
- pressCountSinceStart: zod.z.number()
63451
+ pressCountSinceStart: zod.z.number().meta(require_sleep.readingMeta("count"))
63406
63452
  });
63407
63453
  var DoorbellPressEventSchema = zod.z.object({
63408
63454
  deviceId: zod.z.number(),
@@ -63487,12 +63533,11 @@ var EnumSensorDateTimeFormatSchema = zod.z.enum([
63487
63533
  var EnumSensorStatusSchema = zod.z.object({
63488
63534
  value: zod.z.string(),
63489
63535
  /**
63490
- * Set for `DateTimeSensor`-role sensors so the UI renders the ISO `value`
63491
- * as a locale date/time/datetime; absent for plain enum sensors. HA
63492
- * `device_class=timestamp` → `'datetime'`, `device_class=date` → `'date'`,
63493
- * a time-only sensor → `'time'`.
63536
+ * Set when `value` is a timestamp rather than an enum state, so history
63537
+ * does not learn ISO strings as states. The display shape (date / time /
63538
+ * datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
63494
63539
  */
63495
- format: EnumSensorDateTimeFormatSchema.optional(),
63540
+ timestamp: zod.z.boolean().optional(),
63496
63541
  /** Ms epoch when the slice was last updated. */
63497
63542
  lastFetchedAt: zod.z.number()
63498
63543
  });
@@ -63519,11 +63564,11 @@ var enumSensorCapability = {
63519
63564
  * History (D850): the value as an `enum` series (an appliance's program or
63520
63565
  * operation state, an HVAC action). The option set is per device, so the
63521
63566
  * value list is LEARNED as values are first seen (bounded). A sensor with a
63522
- * `format` carries a timestamp, not a state, and is not recorded.
63567
+ * `timestamp` carries a clock, not a state, and is not recorded.
63523
63568
  */
63524
63569
  history: { fields: { value: {
63525
63570
  kind: "enum",
63526
- skipWhenSet: "format"
63571
+ skipWhenSet: "timestamp"
63527
63572
  } } },
63528
63573
  /** Clock fields: written, but excluded from the compare that decides
63529
63574
  * whether persisting is worth a SQLite commit. */
@@ -63545,13 +63590,13 @@ var EventFireSchema = zod.z.object({
63545
63590
  deviceId: zod.z.number(),
63546
63591
  eventType: zod.z.string(),
63547
63592
  data: zod.z.record(zod.z.string(), zod.z.unknown()).nullable(),
63548
- timestamp: zod.z.number(),
63549
- seq: zod.z.number()
63593
+ timestamp: zod.z.number().meta(require_sleep.readingMeta("none")),
63594
+ seq: zod.z.number().meta(require_sleep.readingMeta("count"))
63550
63595
  });
63551
63596
  var EventEmitterStatusSchema = zod.z.object({
63552
63597
  eventTypes: zod.z.array(zod.z.string()),
63553
63598
  lastEvent: EventFireSchema.nullable(),
63554
- eventCountSinceStart: zod.z.number()
63599
+ eventCountSinceStart: zod.z.number().meta(require_sleep.readingMeta("count"))
63555
63600
  });
63556
63601
  var eventEmitterCapability = {
63557
63602
  name: "event-emitter",
@@ -64008,10 +64053,10 @@ var FanDirectionSchema = zod.z.enum(["forward", "reverse"]);
64008
64053
  var FanControlStatusSchema = zod.z.object({
64009
64054
  /** Active speed as 0..100 inclusive. Null when the device has no
64010
64055
  * speed surface (single-speed fan). */
64011
- percentage: zod.z.number().min(0).max(100).nullable(),
64056
+ percentage: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
64012
64057
  /** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
64013
64058
  * a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
64014
- percentageStep: zod.z.number().positive().optional(),
64059
+ percentageStep: zod.z.number().positive().optional().meta(require_sleep.readingMeta("percentage")),
64015
64060
  /** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
64016
64061
  * — empty string when no preset is active or supported. */
64017
64062
  preset: zod.z.string(),
@@ -64134,7 +64179,7 @@ var FeatureProbeStatusSchema = zod.z.object({
64134
64179
  /** Camera/firmware model string. `null` when the firmware doesn't expose it. */
64135
64180
  model: zod.z.string().nullable(),
64136
64181
  /** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
64137
- channelCount: zod.z.number().nullable(),
64182
+ channelCount: zod.z.number().nullable().meta(require_sleep.readingMeta("count")),
64138
64183
  /**
64139
64184
  * Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
64140
64185
  * completes — drivers' `getAccessoryChildren()` should treat zero as
@@ -64265,7 +64310,7 @@ var gasCapability = {
64265
64310
  //#region src/capabilities/heating-valve.cap.ts
64266
64311
  var HeatingValveStatusSchema = zod.z.object({
64267
64312
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
64268
- position: zod.z.number().min(0).max(100).nullable(),
64313
+ position: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
64269
64314
  /** Ms epoch when the slice was last updated (push or poll). */
64270
64315
  lastFetchedAt: zod.z.number()
64271
64316
  });
@@ -64310,9 +64355,9 @@ var HumidifierStatusSchema = zod.z.object({
64310
64355
  /** Whether the humidifier is currently on. */
64311
64356
  on: zod.z.boolean(),
64312
64357
  /** Current measured relative humidity (0..100). Null when not reported. */
64313
- currentHumidity: zod.z.number().min(0).max(100).nullable(),
64358
+ currentHumidity: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("humidity")),
64314
64359
  /** Target relative humidity (0..100). Null when no setpoint surface. */
64315
- targetHumidity: zod.z.number().min(0).max(100).nullable(),
64360
+ targetHumidity: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("humidity")),
64316
64361
  /** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
64317
64362
  * device has no mode surface. */
64318
64363
  mode: zod.z.string().nullable(),
@@ -64322,9 +64367,9 @@ var HumidifierStatusSchema = zod.z.object({
64322
64367
  * `idle` / `off`). Null when the device doesn't report it. */
64323
64368
  action: zod.z.string().nullable(),
64324
64369
  /** HA `min_humidity` attribute. Null → UI uses 0. */
64325
- minHumidity: zod.z.number().nullable(),
64370
+ minHumidity: zod.z.number().nullable().meta(require_sleep.readingMeta("humidity")),
64326
64371
  /** HA `max_humidity` attribute. Null → UI uses 100. */
64327
- maxHumidity: zod.z.number().nullable(),
64372
+ maxHumidity: zod.z.number().nullable().meta(require_sleep.readingMeta("humidity")),
64328
64373
  /** Ms epoch when the slice was last updated. */
64329
64374
  lastChangedAt: zod.z.number()
64330
64375
  });
@@ -64396,22 +64441,13 @@ var humidifierCapability = {
64396
64441
  * `sourceInfo`.
64397
64442
  */
64398
64443
  var HumiditySensorStatusSchema = zod.z.object({
64399
- /** Current relative humidity, 0..100. */
64400
- percent: zod.z.number().min(0).max(100).meta({
64401
- unit: "%",
64444
+ /** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
64445
+ percent: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("humidity", {
64446
+ precision: 0,
64402
64447
  label: "Humidity"
64403
- }),
64448
+ })),
64404
64449
  /** Ms epoch when the slice was last updated. */
64405
- lastFetchedAt: zod.z.number(),
64406
- /** Live display unit from the upstream source (e.g. HA
64407
- * `attributes.unit_of_measurement`). The UI prefers this over the
64408
- * role's canonical unit. Absent → fall back to the canonical unit. */
64409
- unit: zod.z.string().optional(),
64410
- /** Suggested decimal places for numeric display.
64411
- * Populated live from the upstream source when provided (e.g. HA
64412
- * `attributes.suggested_display_precision`). Falls back to
64413
- * auto-formatting when absent. */
64414
- precision: zod.z.number().int().min(0).max(10).optional()
64450
+ lastFetchedAt: zod.z.number()
64415
64451
  });
64416
64452
  var humiditySensorCapability = {
64417
64453
  name: "humidity-sensor",
@@ -64442,12 +64478,7 @@ var humiditySensorCapability = {
64442
64478
  } } },
64443
64479
  /** Clock fields: written, but excluded from the compare that decides
64444
64480
  * whether persisting is worth a SQLite commit. */
64445
- volatileStateFields: ["lastFetchedAt"],
64446
- customize: {
64447
- version: 1,
64448
- advanced: "derive",
64449
- targets: []
64450
- }
64481
+ volatileStateFields: ["lastFetchedAt"]
64451
64482
  };
64452
64483
  //#endregion
64453
64484
  //#region src/capabilities/image.cap.ts
@@ -64470,7 +64501,7 @@ var ImageStatusSchema = zod.z.object({
64470
64501
  * entity exposes no `entity_picture` (yet). */
64471
64502
  url: zod.z.string().nullable(),
64472
64503
  /** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
64473
- lastUpdated: zod.z.number().nullable()
64504
+ lastUpdated: zod.z.number().nullable().meta(require_sleep.readingMeta("none"))
64474
64505
  });
64475
64506
  var imageCapability = {
64476
64507
  name: "image",
@@ -64560,19 +64591,19 @@ var NormalizedRangeSchema = zod.z.object({
64560
64591
  */
64561
64592
  var ImageSettingsStatusSchema = zod.z.object({
64562
64593
  /** Normalized 0–100. */
64563
- brightness: zod.z.number().optional(),
64594
+ brightness: zod.z.number().optional().meta(require_sleep.readingMeta("none")),
64564
64595
  /** Normalized 0–100. */
64565
- contrast: zod.z.number().optional(),
64596
+ contrast: zod.z.number().optional().meta(require_sleep.readingMeta("none")),
64566
64597
  /** Normalized 0–100. */
64567
- saturation: zod.z.number().optional(),
64598
+ saturation: zod.z.number().optional().meta(require_sleep.readingMeta("none")),
64568
64599
  /** Normalized 0–100. */
64569
- sharpness: zod.z.number().optional(),
64600
+ sharpness: zod.z.number().optional().meta(require_sleep.readingMeta("none")),
64570
64601
  mirror: zod.z.boolean().optional(),
64571
64602
  flip: zod.z.boolean().optional(),
64572
64603
  rotate: ImageRotateSchema.optional(),
64573
64604
  whiteBalance: WhiteBalanceModeSchema.optional(),
64574
64605
  /** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
64575
- warmth: zod.z.number().optional(),
64606
+ warmth: zod.z.number().optional().meta(require_sleep.readingMeta("none")),
64576
64607
  exposureMode: ExposureModeSchema.optional(),
64577
64608
  backlightMode: BacklightModeSchema.optional(),
64578
64609
  lastFetchedAt: zod.z.number()
@@ -64873,10 +64904,10 @@ var IntercomAbilitySchema = zod.z.object({
64873
64904
  /** Codecs accepted by firmware, in preference order. Driver-authored strings. */
64874
64905
  codecs: zod.z.array(zod.z.string()),
64875
64906
  /** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
64876
- sampleRate: zod.z.number(),
64907
+ sampleRate: zod.z.number().meta(require_sleep.readingMeta("none")),
64877
64908
  duplex: zod.z.enum(["half", "full"]),
64878
64909
  /** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
64879
- maxBacklogMs: zod.z.number()
64910
+ maxBacklogMs: zod.z.number().meta(require_sleep.readingMeta("duration-ms"))
64880
64911
  });
64881
64912
  var IntercomStatusSchema = zod.z.object({
64882
64913
  /** True while a talk session is open. */
@@ -65105,13 +65136,13 @@ var LawnMowerControlStatusSchema = zod.z.object({
65105
65136
  activity: LawnMowerActivitySchema,
65106
65137
  /** 0..100 battery percentage. Null when the device has no battery
65107
65138
  * reading. */
65108
- batteryLevel: zod.z.number().min(0).max(100).nullable(),
65139
+ batteryLevel: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
65109
65140
  /** 0..100 mowing-completion percentage of the current task, or null when no
65110
65141
  * task is active / progress is unavailable. */
65111
- progressPercent: zod.z.number().min(0).max(100).nullable(),
65142
+ progressPercent: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
65112
65143
  /** Current device/event code (dynamic — mostly status, sometimes an error),
65113
65144
  * or null when unknown. */
65114
- currentCode: zod.z.number().nullable(),
65145
+ currentCode: zod.z.number().nullable().meta(require_sleep.readingMeta("none")),
65115
65146
  /** Human label for {@link currentCode}, or null when undecodable. */
65116
65147
  currentCodeLabel: zod.z.string().nullable(),
65117
65148
  /** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
@@ -65841,7 +65872,7 @@ var MediaPlayerStatusSchema = zod.z.object({
65841
65872
  state: MediaPlayerStateSchema,
65842
65873
  /** Volume as 0..100 inclusive. Null when the device has no volume
65843
65874
  * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
65844
- volumeLevel: zod.z.number().min(0).max(100).nullable(),
65875
+ volumeLevel: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
65845
65876
  /** Mute toggle distinct from volume=0. Null when no mute surface.
65846
65877
  * Pair with `DeviceFeature.MediaPlayerMute`. */
65847
65878
  isMuted: zod.z.boolean().nullable(),
@@ -65854,10 +65885,10 @@ var MediaPlayerStatusSchema = zod.z.object({
65854
65885
  currentMedia: MediaInfoSchema.nullable(),
65855
65886
  /** Current playback position in ms. Null when not seekable or
65856
65887
  * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
65857
- positionMs: zod.z.number().int().nonnegative().nullable(),
65888
+ positionMs: zod.z.number().int().nonnegative().nullable().meta(require_sleep.readingMeta("duration-ms")),
65858
65889
  /** Total duration of the current media in ms. Null when unknown
65859
65890
  * (live stream) or nothing is playing. */
65860
- durationMs: zod.z.number().int().nonnegative().nullable(),
65891
+ durationMs: zod.z.number().int().nonnegative().nullable().meta(require_sleep.readingMeta("duration-ms")),
65861
65892
  /** Shuffle toggle. Null when no shuffle surface.
65862
65893
  * Pair with `DeviceFeature.MediaPlayerShuffle`. */
65863
65894
  shuffle: zod.z.boolean().nullable(),
@@ -66237,7 +66268,7 @@ var motionCustomize = {
66237
66268
  eventName: "onMotionChanged"
66238
66269
  },
66239
66270
  deviceTypes: [require_err_msg.DeviceType.Camera],
66240
- apply: "replace",
66271
+ apply: "add",
66241
66272
  applyAuthority: {
66242
66273
  kind: "camera-sensors",
66243
66274
  timeoutField: "motionCooldownMs"
@@ -66334,7 +66365,7 @@ var MotionStatusSchema = zod.z.object({
66334
66365
  * it, every provider seeded it at boot, and every session left it
66335
66366
  * `detected: true` after its detach.
66336
66367
  */
66337
- autoClearAfterMs: zod.z.number().nullable()
66368
+ autoClearAfterMs: zod.z.number().nullable().meta(require_sleep.readingMeta("duration-ms"))
66338
66369
  });
66339
66370
  /**
66340
66371
  * Payload of `motion.onMotionChanged` event + the corresponding bus
@@ -66520,7 +66551,7 @@ var MotionZoneRegionSchema = zod.z.object({
66520
66551
  * the grid region(s). */
66521
66552
  var MotionZoneStatusSchema = zod.z.object({
66522
66553
  enabled: zod.z.boolean(),
66523
- sensitivity: zod.z.number(),
66554
+ sensitivity: zod.z.number().meta(require_sleep.readingMeta("none")),
66524
66555
  /** Grid region(s). Today exactly one `grid` shape. */
66525
66556
  regions: zod.z.array(MotionZoneRegionSchema),
66526
66557
  lastFetchedAt: zod.z.number()
@@ -67036,7 +67067,7 @@ var NavigationStatusSchema = zod.z.object({
67036
67067
  following: zod.z.boolean(),
67037
67068
  flash: zod.z.boolean(),
67038
67069
  lightMode: NavigationLightModeSchema,
67039
- lightLevel: zod.z.number().min(40).max(100),
67070
+ lightLevel: zod.z.number().min(40).max(100).meta(require_sleep.readingMeta("none")),
67040
67071
  /** Ms epoch when the slice was last updated. */
67041
67072
  lastChangedAt: zod.z.number()
67042
67073
  });
@@ -67187,13 +67218,13 @@ var NetworkLinkStatusSchema = zod.z.object({
67187
67218
  * one whose reading has not landed must not be drawn at 0 %. Consumers
67188
67219
  * SKIP a null rather than coerce it.
67189
67220
  */
67190
- signalPercent: zod.z.number().min(0).max(100).nullable(),
67221
+ signalPercent: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
67191
67222
  /** Raw received signal strength in dBm, when the firmware reports one. */
67192
- rssiDbm: zod.z.number().optional(),
67223
+ rssiDbm: zod.z.number().optional().meta(require_sleep.readingMeta("signal-level")),
67193
67224
  /** Network name of a wireless link, when the firmware reports it. */
67194
67225
  ssid: zod.z.string().optional(),
67195
67226
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
67196
- lastUpdated: zod.z.number()
67227
+ lastUpdated: zod.z.number().meta(require_sleep.readingMeta("none"))
67197
67228
  });
67198
67229
  /** The slice a provider seeds before its first read: nothing is known yet. */
67199
67230
  var NETWORK_LINK_UNKNOWN = {
@@ -67669,7 +67700,7 @@ var NotifierStatusSchema = zod.z.object({
67669
67700
  lastError: zod.z.string().nullable(),
67670
67701
  /** Number of deliveries currently buffered server-side (rate-limited
67671
67702
  * notifiers may queue). 0 when the provider sends synchronously. */
67672
- queueDepth: zod.z.number().int().nonnegative(),
67703
+ queueDepth: zod.z.number().int().nonnegative().meta(require_sleep.readingMeta("count")),
67673
67704
  /** Per-feature capability matrix — authoritative source for the
67674
67705
  * compose-form field gating. */
67675
67706
  supports: NotifierSupportsSchema
@@ -67763,11 +67794,9 @@ onSent: { data: zod.z.object({
67763
67794
  * cap fits (HA `sensor` whose `device_class` we don't have a typed cap
67764
67795
  * for: water flow, distance, weight, frequency, signal strength, …).
67765
67796
  *
67766
- * The `unit` and `precision` fields carry the live display metadata that
67767
- * the upstream source provides with each state push (HA
67768
- * `attributes.unit_of_measurement` / `attributes.suggested_display_precision`).
67769
- * The UI reads them directly from the slice — they are the single source
67770
- * of truth for generic numeric rendering and do NOT live on `sourceInfo`.
67797
+ * The quantity is not on this slice. It is chosen when the device is
67798
+ * adopted and stored once on `SourceInfo.quantity` (D944). `value` is in
67799
+ * that quantity's base unit. `null` is a refused sample (D393).
67771
67800
  *
67772
67801
  * Prefer the typed alternatives (`temperature-sensor`, `humidity-sensor`,
67773
67802
  * `ambient-light-sensor`, `pressure-sensor`, `power-meter`,
@@ -67776,15 +67805,8 @@ onSent: { data: zod.z.object({
67776
67805
  * numeric values land here.
67777
67806
  */
67778
67807
  var NumericSensorStatusSchema = zod.z.object({
67779
- value: zod.z.number(),
67780
- /** Display unit-of-measurement (e.g. 'dBm', 's', 'rpm', 'steps').
67781
- * Populated live from the upstream source on each state push.
67782
- * Absent for unitless sensors. */
67783
- unit: zod.z.string().optional(),
67784
- /** Suggested decimal places for numeric display.
67785
- * Populated live from the upstream source when provided.
67786
- * Falls back to auto-formatting when absent. */
67787
- precision: zod.z.number().int().min(0).max(10).optional(),
67808
+ /** Reading in the device quantity's base unit. `null` when refused (D393). */
67809
+ value: zod.z.number().nullable().meta(require_sleep.readingMeta("device")),
67788
67810
  /** Ms epoch when the slice was last updated. */
67789
67811
  lastFetchedAt: zod.z.number()
67790
67812
  });
@@ -67808,11 +67830,11 @@ var numericSensorCapability = {
67808
67830
  */
67809
67831
  durability: "restored",
67810
67832
  /**
67811
- * History (D850): recorded series. The unit is per device, read from the sibling `unit` field.
67833
+ * History (D850): recorded series. The unit is the device quantity's base,
67834
+ * resolved when the series is read — the cap has no single unit.
67812
67835
  */
67813
67836
  history: { fields: { value: {
67814
67837
  kind: "gauge",
67815
- unitFrom: "unit",
67816
67838
  minIntervalMs: 3e4
67817
67839
  } } },
67818
67840
  /** Clock fields: written, but excluded from the compare that decides
@@ -68269,23 +68291,26 @@ var PetFeederStatusSchema = zod.z.object({
68269
68291
  /** Food currently in the bowl (grams). Null when the device has not
68270
68292
  * reported a reading yet. On dual-hopper models this is the combined
68271
68293
  * bowl reading; per-hopper levels live in `food1`/`food2`. */
68272
- foodLevel: zod.z.number().nullable(),
68294
+ foodLevel: zod.z.number().nullable().meta(require_sleep.readingMeta("none")),
68273
68295
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
68274
68296
  * single-hopper models. */
68275
- food1: zod.z.number().nullable(),
68297
+ food1: zod.z.number().nullable().meta(require_sleep.readingMeta("none")),
68276
68298
  /** Hopper-2 food level (grams) on dual-hopper feeders; null on
68277
68299
  * single-hopper models. */
68278
- food2: zod.z.number().nullable(),
68300
+ food2: zod.z.number().nullable().meta(require_sleep.readingMeta("none")),
68279
68301
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
68280
68302
  * (`device_class: problem`, on = low). True when the bowl is empty /
68281
68303
  * below the feeder's low threshold. */
68282
68304
  lowFood: zod.z.boolean(),
68283
68305
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
68284
68306
  * device has no battery reading. */
68285
- batteryPower: zod.z.number().min(0).max(100).nullable(),
68286
- /** Days of desiccant life remaining. Null when the model has no
68287
- * desiccant sensor. */
68288
- desiccantLeftDays: zod.z.number().nullable(),
68307
+ batteryPower: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
68308
+ /**
68309
+ * Desiccant life remaining, stored in seconds (duration base, D944).
68310
+ * Providers convert from the days the feeder reports. Null when the
68311
+ * model has no desiccant sensor.
68312
+ */
68313
+ desiccantLeftDays: zod.z.number().nullable().meta(require_sleep.readingMeta("duration")),
68289
68314
  /** True while a feed is in progress. */
68290
68315
  feeding: zod.z.boolean(),
68291
68316
  /** Decoded connectivity / power status (HA petkit device-status enum).
@@ -68296,7 +68321,7 @@ var PetFeederStatusSchema = zod.z.object({
68296
68321
  * with `errorCode` for consumers that want the raw integer. */
68297
68322
  error: zod.z.string().nullable(),
68298
68323
  /** Raw device error code (0 / null = no error). */
68299
- errorCode: zod.z.number().nullable(),
68324
+ errorCode: zod.z.number().nullable().meta(require_sleep.readingMeta("none")),
68300
68325
  /** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
68301
68326
  isDualHopper: zod.z.boolean(),
68302
68327
  /** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
@@ -68306,7 +68331,7 @@ var PetFeederStatusSchema = zod.z.object({
68306
68331
  /** Play a chime when dispensing. */
68307
68332
  feedSound: zod.z.boolean(),
68308
68333
  /** Speaker / prompt volume level (device-scaled integer). */
68309
- volume: zod.z.number(),
68334
+ volume: zod.z.number().meta(require_sleep.readingMeta("none")),
68310
68335
  /** Ms epoch when the slice was last refreshed from the cloud. */
68311
68336
  lastFetchedAt: zod.z.number()
68312
68337
  });
@@ -68425,7 +68450,7 @@ var petFeederCapability = {
68425
68450
  },
68426
68451
  desiccantLeftDays: {
68427
68452
  kind: "gauge",
68428
- unit: "days"
68453
+ unit: "s"
68429
68454
  }
68430
68455
  } }
68431
68456
  };
@@ -69631,36 +69656,19 @@ var platformProbeCapability = {
69631
69656
  */
69632
69657
  var PowerMeterStatusSchema = zod.z.object({
69633
69658
  /** Instantaneous power draw in watts. */
69634
- watts: zod.z.number().optional().meta({
69635
- unit: "W",
69636
- label: "Power"
69637
- }),
69659
+ watts: zod.z.number().optional().meta(require_sleep.readingMeta("power", { label: "Power" })),
69638
69660
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
69639
- kwhTotal: zod.z.number().optional().meta({
69640
- unit: "kWh",
69641
- label: "Energy"
69642
- }),
69661
+ kwhTotal: zod.z.number().optional().meta(require_sleep.readingMeta("energy", { label: "Energy" })),
69643
69662
  /** Voltage in volts. */
69644
- volts: zod.z.number().optional(),
69663
+ volts: zod.z.number().optional().meta(require_sleep.readingMeta("voltage")),
69645
69664
  /** Current in amperes. */
69646
- amps: zod.z.number().optional(),
69647
- /** Apparent power in volt-amperes (W × power-factor reciprocal). */
69648
- va: zod.z.number().optional(),
69665
+ amps: zod.z.number().optional().meta(require_sleep.readingMeta("current")),
69666
+ /** Apparent power in volt-amperes. */
69667
+ va: zod.z.number().optional().meta(require_sleep.readingMeta("apparent-power")),
69649
69668
  /** Power factor (0..1) if reported. */
69650
- powerFactor: zod.z.number().min(0).max(1).optional(),
69669
+ powerFactor: zod.z.number().min(0).max(1).optional().meta(require_sleep.readingMeta("ratio")),
69651
69670
  /** Ms epoch when the slice was last updated. */
69652
- lastFetchedAt: zod.z.number(),
69653
- /** Live display unit of the single metric this slice carries (e.g. HA
69654
- * `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
69655
- * Each upstream `sensor.*` entity surfaces ONE device_class, so one
69656
- * unit per slice is unambiguous. The UI prefers this over the role's
69657
- * canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
69658
- unit: zod.z.string().optional(),
69659
- /** Suggested decimal places for numeric display.
69660
- * Populated live from the upstream source when provided (e.g. HA
69661
- * `attributes.suggested_display_precision`). Falls back to
69662
- * auto-formatting when absent. */
69663
- precision: zod.z.number().int().min(0).max(10).optional()
69671
+ lastFetchedAt: zod.z.number()
69664
69672
  });
69665
69673
  var powerMeterCapability = {
69666
69674
  name: "power-meter",
@@ -69724,12 +69732,7 @@ var powerMeterCapability = {
69724
69732
  } },
69725
69733
  /** Clock fields: written, but excluded from the compare that decides
69726
69734
  * whether persisting is worth a SQLite commit. */
69727
- volatileStateFields: ["lastFetchedAt"],
69728
- customize: {
69729
- version: 1,
69730
- advanced: "derive",
69731
- targets: []
69732
- }
69735
+ volatileStateFields: ["lastFetchedAt"]
69733
69736
  };
69734
69737
  //#endregion
69735
69738
  //#region src/capabilities/presence.cap.ts
@@ -69751,11 +69754,11 @@ var powerMeterCapability = {
69751
69754
  */
69752
69755
  var GpsLocationSchema = zod.z.object({
69753
69756
  /** Latitude in decimal degrees, -90..90. */
69754
- latitude: zod.z.number().min(-90).max(90),
69757
+ latitude: zod.z.number().min(-90).max(90).meta(require_sleep.readingMeta("angle")),
69755
69758
  /** Longitude in decimal degrees, -180..180. */
69756
- longitude: zod.z.number().min(-180).max(180),
69759
+ longitude: zod.z.number().min(-180).max(180).meta(require_sleep.readingMeta("angle")),
69757
69760
  /** Reported accuracy in meters (lower = better). */
69758
- accuracyMeters: zod.z.number().nonnegative()
69761
+ accuracyMeters: zod.z.number().nonnegative().meta(require_sleep.readingMeta("length"))
69759
69762
  });
69760
69763
  var PresenceStatusSchema = zod.z.object({
69761
69764
  /** `home` / `not_home` / any user-defined zone name. */
@@ -69767,7 +69770,7 @@ var PresenceStatusSchema = zod.z.object({
69767
69770
  gps: GpsLocationSchema.nullable(),
69768
69771
  /** Optional battery level of the tracking device (0..100). Null
69769
69772
  * when not reported. */
69770
- batteryPercent: zod.z.number().min(0).max(100).nullable(),
69773
+ batteryPercent: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
69771
69774
  /** Ms epoch when the slice was last updated. */
69772
69775
  lastChangedAt: zod.z.number()
69773
69776
  });
@@ -69819,19 +69822,10 @@ var presenceCapability = {
69819
69822
  * `sourceInfo`.
69820
69823
  */
69821
69824
  var PressureSensorStatusSchema = zod.z.object({
69822
- /** Current pressure in hPa. */
69823
- hpa: zod.z.number(),
69825
+ /** Current pressure in hPa. `null` when the sample was refused (D393). */
69826
+ hpa: zod.z.number().nullable().meta(require_sleep.readingMeta("pressure", { precision: 0 })),
69824
69827
  /** Ms epoch when the slice was last updated. */
69825
- lastFetchedAt: zod.z.number(),
69826
- /** Live display unit from the upstream source (e.g. HA
69827
- * `attributes.unit_of_measurement`). The UI prefers this over the
69828
- * role's canonical unit. Absent → fall back to the canonical unit. */
69829
- unit: zod.z.string().optional(),
69830
- /** Suggested decimal places for numeric display.
69831
- * Populated live from the upstream source when provided (e.g. HA
69832
- * `attributes.suggested_display_precision`). Falls back to
69833
- * auto-formatting when absent. */
69834
- precision: zod.z.number().int().min(0).max(10).optional()
69828
+ lastFetchedAt: zod.z.number()
69835
69829
  });
69836
69830
  var pressureSensorCapability = {
69837
69831
  name: "pressure-sensor",
@@ -70398,8 +70392,8 @@ var ptzCapability = {
70398
70392
  var AutotrackTargetTypeSchema = zod.z.string().describe("Vendor target string (people/vehicle/pet); empty = camera default");
70399
70393
  var PtzAutotrackSettingsSchema = zod.z.object({
70400
70394
  targetType: AutotrackTargetTypeSchema,
70401
- stopDelaySeconds: zod.z.number().int().min(0).max(300),
70402
- disappearDelaySeconds: zod.z.number().int().min(0).max(300)
70395
+ stopDelaySeconds: zod.z.number().int().min(0).max(300).meta(require_sleep.readingMeta("duration")),
70396
+ disappearDelaySeconds: zod.z.number().int().min(0).max(300).meta(require_sleep.readingMeta("duration"))
70403
70397
  });
70404
70398
  /**
70405
70399
  * One option the camera advertises as a valid `targetType`. Drivers
@@ -70566,7 +70560,7 @@ var RunningStateStatusSchema = zod.z.object({
70566
70560
  /** null = unknown — never coerced to false (D393). */
70567
70561
  running: zod.z.boolean().nullable(),
70568
70562
  /** Epoch ms the current boolean began; null while `running` is null. */
70569
- since: zod.z.number().nullable(),
70563
+ since: zod.z.number().nullable().meta(require_sleep.readingMeta("none")),
70570
70564
  source: RunningStateSourceSchema.meta({ label: "Source" }),
70571
70565
  phase: zod.z.string().nullable(),
70572
70566
  /** null until a reading has been published (D393). */
@@ -70581,7 +70575,6 @@ var RUNNING_DEVICE_TYPES = Object.values(require_err_msg.DeviceType);
70581
70575
  /** Generic Customize target for the running boolean (D936). */
70582
70576
  var runningCustomize = {
70583
70577
  version: 1,
70584
- advanced: "derive",
70585
70578
  targets: [{
70586
70579
  id: "running",
70587
70580
  cardinality: "scalar",
@@ -70601,7 +70594,7 @@ var runningCustomize = {
70601
70594
  require_err_msg.DeviceType.Thermostat,
70602
70595
  require_err_msg.DeviceType.PetFeeder
70603
70596
  ],
70604
- apply: "add-or-replace",
70597
+ apply: "add",
70605
70598
  path: "running",
70606
70599
  sources: [
70607
70600
  {
@@ -72449,9 +72442,9 @@ var RecordTriggerSchema = zod.z.enum([
72449
72442
  */
72450
72443
  var RecordWindowSchema = zod.z.object({
72451
72444
  trigger: RecordTriggerSchema,
72452
- day: zod.z.number().int().min(0).max(6),
72453
- startMinute: zod.z.number().int().min(0).max(1439),
72454
- endMinute: zod.z.number().int().min(1).max(1440)
72445
+ day: zod.z.number().int().min(0).max(6).meta(require_sleep.readingMeta("none")),
72446
+ startMinute: zod.z.number().int().min(0).max(1439).meta(require_sleep.readingMeta("none")),
72447
+ endMinute: zod.z.number().int().min(1).max(1440).meta(require_sleep.readingMeta("none"))
72455
72448
  });
72456
72449
  /** Status of one physical volume, as the camera itself describes it. */
72457
72450
  var OnboardStorageVolumeSchema = zod.z.object({
@@ -72550,10 +72543,10 @@ var RecordingOnboardStatusSchema = zod.z.object({
72550
72543
  /** Master "record to the card at all" switch. */
72551
72544
  enabled: zod.z.boolean().nullable(),
72552
72545
  overwriteWhenFull: zod.z.boolean().nullable(),
72553
- preRecordSec: zod.z.number().nullable(),
72554
- postRecordSec: zod.z.number().nullable(),
72546
+ preRecordSec: zod.z.number().nullable().meta(require_sleep.readingMeta("duration")),
72547
+ postRecordSec: zod.z.number().nullable().meta(require_sleep.readingMeta("duration")),
72555
72548
  /** Length of one recorded file, in minutes. */
72556
- segmentMinutes: zod.z.number().nullable(),
72549
+ segmentMinutes: zod.z.number().nullable().meta(require_sleep.readingMeta("none")),
72557
72550
  /** The primary track's weekly windows, flattened. */
72558
72551
  windows: zod.z.array(RecordWindowSchema),
72559
72552
  /**
@@ -72569,7 +72562,7 @@ var RecordingOnboardStatusSchema = zod.z.object({
72569
72562
  * provider reporting a non-zero count also reports the schedule as not
72570
72563
  * writable.
72571
72564
  */
72572
- unreadableWindows: zod.z.number(),
72565
+ unreadableWindows: zod.z.number().meta(require_sleep.readingMeta("count")),
72573
72566
  /**
72574
72567
  * The camera is scheduled to record and has NO usable storage.
72575
72568
  *
@@ -73741,7 +73734,7 @@ var CamStreamDescriptorSchema = zod.z.object({
73741
73734
  url: zod.z.string().optional(),
73742
73735
  codec: zod.z.string().optional(),
73743
73736
  resolution: require_sleep.CamStreamResolutionSchema.optional(),
73744
- fps: zod.z.number().positive().optional(),
73737
+ fps: zod.z.number().positive().optional().meta(require_sleep.readingMeta("rate")),
73745
73738
  label: zod.z.string().optional(),
73746
73739
  /** Device-level features (e.g. `battery-operated`) — drives broker policy. */
73747
73740
  deviceFeatures: zod.z.array(zod.z.string()).optional(),
@@ -73830,18 +73823,18 @@ var StreamProfileSchema = zod.z.enum([
73830
73823
  ]);
73831
73824
  /** The current encoding config of one stream profile. */
73832
73825
  var StreamProfileConfigSchema = zod.z.object({
73833
- width: zod.z.number(),
73834
- height: zod.z.number(),
73826
+ width: zod.z.number().meta(require_sleep.readingMeta("none")),
73827
+ height: zod.z.number().meta(require_sleep.readingMeta("none")),
73835
73828
  codec: zod.z.enum(["h264", "h265"]),
73836
- framerate: zod.z.number(),
73837
- bitrate: zod.z.number(),
73829
+ framerate: zod.z.number().meta(require_sleep.readingMeta("rate")),
73830
+ bitrate: zod.z.number().meta(require_sleep.readingMeta("bitrate")),
73838
73831
  bitrateMode: zod.z.enum(["vbr", "cbr"]).optional(),
73839
73832
  encoderProfile: zod.z.enum([
73840
73833
  "high",
73841
73834
  "main",
73842
73835
  "baseline"
73843
73836
  ]).optional(),
73844
- gop: zod.z.number().optional(),
73837
+ gop: zod.z.number().optional().meta(require_sleep.readingMeta("none")),
73845
73838
  /**
73846
73839
  * Whether THIS profile currently carries an audio track. READ-ONLY here.
73847
73840
  *
@@ -74323,12 +74316,7 @@ var switchCapability = {
74323
74316
  label: "On",
74324
74317
  kind: "boolean"
74325
74318
  }
74326
- }] },
74327
- customize: {
74328
- version: 1,
74329
- advanced: "derive",
74330
- targets: []
74331
- }
74319
+ }] }
74332
74320
  };
74333
74321
  //#endregion
74334
74322
  //#region src/system/server-name.ts
@@ -75076,35 +75064,21 @@ var tamperCapability = {
75076
75064
  * entries with `device_class: temperature` and any future native
75077
75065
  * thermometer.
75078
75066
  *
75079
- * Unit normalisation: providers convert to Celsius before storing.
75080
- * The slice value is always Celsius so cross-cap aggregators
75081
- * (climate-control's `currentTemp`, energy analytics) can compose
75082
- * without per-source unit fixups. The canonical display unit (`°C`) is
75083
- * a descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
75084
- * `sourceInfo`.
75067
+ * The number is Celsius (D944). Providers convert on ingest. A failed
75068
+ * conversion is `null`, never a guess. Display unit is a read-time preference.
75085
75069
  *
75086
75070
  * Status `lastFetchedAt` lets staleness-aware consumers detect a
75087
75071
  * frozen feed (provider hung) distinct from a "temperature hasn't
75088
75072
  * changed" steady state.
75089
75073
  */
75090
75074
  var TemperatureSensorStatusSchema = zod.z.object({
75091
- /** Current temperature in Celsius. */
75092
- celsius: zod.z.number().meta({
75093
- unit: "°C",
75075
+ /** Current temperature in Celsius. `null` when the sample was refused (D393). */
75076
+ celsius: zod.z.number().nullable().meta(require_sleep.readingMeta("temperature", {
75077
+ precision: 1,
75094
75078
  label: "Temperature"
75095
- }),
75079
+ })),
75096
75080
  /** Ms epoch when the slice was last updated (push or poll). */
75097
- lastFetchedAt: zod.z.number(),
75098
- /** Live display unit from the upstream source (e.g. HA
75099
- * `attributes.unit_of_measurement`). The UI prefers this over the
75100
- * role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
75101
- * Absent → the UI falls back to the role's canonical unit. */
75102
- unit: zod.z.string().optional(),
75103
- /** Suggested decimal places for numeric display.
75104
- * Populated live from the upstream source when provided (e.g. HA
75105
- * `attributes.suggested_display_precision`). Falls back to
75106
- * auto-formatting when absent. */
75107
- precision: zod.z.number().int().min(0).max(10).optional()
75081
+ lastFetchedAt: zod.z.number()
75108
75082
  });
75109
75083
  var temperatureSensorCapability = {
75110
75084
  name: "temperature-sensor",
@@ -75135,12 +75109,7 @@ var temperatureSensorCapability = {
75135
75109
  } } },
75136
75110
  /** Clock fields: written, but excluded from the compare that decides
75137
75111
  * whether persisting is worth a SQLite commit. */
75138
- volatileStateFields: ["lastFetchedAt"],
75139
- customize: {
75140
- version: 1,
75141
- advanced: "derive",
75142
- targets: []
75143
- }
75112
+ volatileStateFields: ["lastFetchedAt"]
75144
75113
  };
75145
75114
  //#endregion
75146
75115
  //#region src/capabilities/toast.cap.ts
@@ -75494,16 +75463,16 @@ var VacuumControlStatusSchema = zod.z.object({
75494
75463
  state: VacuumStateSchema,
75495
75464
  /** 0..100 battery percentage. Null when the device has no battery
75496
75465
  * reading. */
75497
- batteryLevel: zod.z.number().min(0).max(100).nullable(),
75466
+ batteryLevel: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
75498
75467
  /** Current fan-speed token (provider-verbatim). Null when unknown or
75499
75468
  * the vacuum has no speed control. */
75500
75469
  fanSpeed: zod.z.string().nullable(),
75501
75470
  /** Speed tokens the hardware accepts — drives the UI selector. */
75502
75471
  availableFanSpeeds: zod.z.array(zod.z.string()),
75503
75472
  /** 0..100 cleaning-completion percentage of the current task, or null. */
75504
- progressPercent: zod.z.number().min(0).max(100).nullable(),
75473
+ progressPercent: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
75505
75474
  /** Current error code (0 / null = no error). */
75506
- errorCode: zod.z.number().nullable(),
75475
+ errorCode: zod.z.number().nullable().meta(require_sleep.readingMeta("none")),
75507
75476
  /** Human label for {@link errorCode}, or null when none / undecodable. */
75508
75477
  errorLabel: zod.z.string().nullable(),
75509
75478
  /** Ms epoch when the slice was last updated. */
@@ -75601,7 +75570,7 @@ var ValveStatusSchema = zod.z.object({
75601
75570
  state: ValveStateSchema,
75602
75571
  /** 0 = fully closed, 100 = fully open. Null when the device has no
75603
75572
  * intermediate position surface. */
75604
- position: zod.z.number().min(0).max(100).nullable(),
75573
+ position: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("percentage")),
75605
75574
  /** Ms epoch when the slice was last updated. */
75606
75575
  lastChangedAt: zod.z.number()
75607
75576
  });
@@ -75713,9 +75682,9 @@ var vibrationCapability = {
75713
75682
  */
75714
75683
  var WaterHeaterStatusSchema = zod.z.object({
75715
75684
  /** Current measured temperature. Null when not reported. */
75716
- currentTemp: zod.z.number().nullable(),
75685
+ currentTemp: zod.z.number().nullable().meta(require_sleep.readingMeta("temperature")),
75717
75686
  /** Target temperature setpoint. Null when no setpoint surface. */
75718
- targetTemp: zod.z.number().nullable(),
75687
+ targetTemp: zod.z.number().nullable().meta(require_sleep.readingMeta("temperature")),
75719
75688
  /** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
75720
75689
  * `heat_pump` / `high_demand` / `performance` / `off`). Null when the
75721
75690
  * device reports an unknown state. */
@@ -75726,9 +75695,9 @@ var WaterHeaterStatusSchema = zod.z.object({
75726
75695
  * has no away surface. */
75727
75696
  away: zod.z.boolean().nullable(),
75728
75697
  /** HA `min_temp` attribute. Null when not reported. */
75729
- minTemp: zod.z.number().nullable(),
75698
+ minTemp: zod.z.number().nullable().meta(require_sleep.readingMeta("temperature")),
75730
75699
  /** HA `max_temp` attribute. Null when not reported. */
75731
- maxTemp: zod.z.number().nullable(),
75700
+ maxTemp: zod.z.number().nullable().meta(require_sleep.readingMeta("temperature")),
75732
75701
  /** Ms epoch when the slice was last updated. */
75733
75702
  lastChangedAt: zod.z.number()
75734
75703
  });
@@ -75815,25 +75784,18 @@ var waterHeaterCapability = {
75815
75784
  * Forecast deferred — current conditions only for v1.
75816
75785
  */
75817
75786
  var WeatherStatusSchema = zod.z.object({
75818
- /** Verbatim HA condition state (`sunny`, `cloudy`, `rainy`, …). Null
75819
- * when no condition has been reported yet. */
75787
+ /** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
75820
75788
  condition: zod.z.string().nullable(),
75821
- /** Current temperature in the reported unit. Null when not provided. */
75822
- temperature: zod.z.number().nullable(),
75823
- /** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
75824
- temperatureUnit: zod.z.string().nullable(),
75789
+ /** Current temperature in Celsius. Null when not provided or refused. */
75790
+ temperature: zod.z.number().nullable().meta(require_sleep.readingMeta("temperature", { precision: 1 })),
75825
75791
  /** Relative humidity (0..100). Null when not provided. */
75826
- humidity: zod.z.number().min(0).max(100).nullable(),
75827
- /** Barometric pressure in the reported unit. Null when not provided. */
75828
- pressure: zod.z.number().nullable(),
75829
- /** Pressure unit string (e.g. `hPa` / `inHg`). Null when not provided. */
75830
- pressureUnit: zod.z.string().nullable(),
75831
- /** Wind speed in the reported unit. Null when not provided. */
75832
- windSpeed: zod.z.number().nullable(),
75833
- /** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
75834
- windSpeedUnit: zod.z.string().nullable(),
75792
+ humidity: zod.z.number().min(0).max(100).nullable().meta(require_sleep.readingMeta("humidity")),
75793
+ /** Barometric pressure in hPa. Null when not provided or refused. */
75794
+ pressure: zod.z.number().nullable().meta(require_sleep.readingMeta("pressure")),
75795
+ /** Wind speed in metres per second. Null when not provided or refused. */
75796
+ windSpeed: zod.z.number().nullable().meta(require_sleep.readingMeta("speed")),
75835
75797
  /** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
75836
- windBearing: zod.z.number().nullable(),
75798
+ windBearing: zod.z.number().nullable().meta(require_sleep.readingMeta("angle")),
75837
75799
  /** Ms epoch when the slice was last updated. */
75838
75800
  lastFetchedAt: zod.z.number()
75839
75801
  });
@@ -75861,12 +75823,12 @@ var weatherCapability = {
75861
75823
  */
75862
75824
  durability: "session",
75863
75825
  /**
75864
- * History (D850): recorded series. Units follow the sibling `*Unit` fields.
75826
+ * History (D850): recorded series. Numbers are the quantity base (D944).
75865
75827
  */
75866
75828
  history: { fields: {
75867
75829
  temperature: {
75868
75830
  kind: "gauge",
75869
- unitFrom: "temperatureUnit",
75831
+ unit: "°C",
75870
75832
  deadband: .2
75871
75833
  },
75872
75834
  humidity: {
@@ -75876,12 +75838,12 @@ var weatherCapability = {
75876
75838
  },
75877
75839
  pressure: {
75878
75840
  kind: "gauge",
75879
- unitFrom: "pressureUnit",
75841
+ unit: "hPa",
75880
75842
  deadband: .5
75881
75843
  },
75882
75844
  windSpeed: {
75883
75845
  kind: "gauge",
75884
- unitFrom: "windSpeedUnit",
75846
+ unit: "m/s",
75885
75847
  deadband: .5
75886
75848
  }
75887
75849
  } }
@@ -77320,6 +77282,11 @@ var UNIT_TABLE = {
77320
77282
  factor: 1609.344,
77321
77283
  offset: 0
77322
77284
  },
77285
+ "m²": {
77286
+ dimension: "area",
77287
+ factor: 1,
77288
+ offset: 0
77289
+ },
77323
77290
  lx: {
77324
77291
  dimension: "illuminance",
77325
77292
  factor: 1,
@@ -77424,6 +77391,121 @@ var UNIT_TABLE = {
77424
77391
  dimension: "percentage",
77425
77392
  factor: 1,
77426
77393
  offset: 0
77394
+ },
77395
+ L: {
77396
+ dimension: "volume",
77397
+ factor: 1,
77398
+ offset: 0
77399
+ },
77400
+ mL: {
77401
+ dimension: "volume",
77402
+ factor: .001,
77403
+ offset: 0
77404
+ },
77405
+ gal: {
77406
+ dimension: "volume",
77407
+ factor: 3.785411784,
77408
+ offset: 0
77409
+ },
77410
+ s: {
77411
+ dimension: "duration",
77412
+ factor: 1,
77413
+ offset: 0
77414
+ },
77415
+ ms: {
77416
+ dimension: "duration",
77417
+ factor: .001,
77418
+ offset: 0
77419
+ },
77420
+ min: {
77421
+ dimension: "duration",
77422
+ factor: 60,
77423
+ offset: 0
77424
+ },
77425
+ h: {
77426
+ dimension: "duration",
77427
+ factor: 3600,
77428
+ offset: 0
77429
+ },
77430
+ d: {
77431
+ dimension: "duration",
77432
+ factor: 86400,
77433
+ offset: 0
77434
+ },
77435
+ B: {
77436
+ dimension: "information",
77437
+ factor: 1,
77438
+ offset: 0
77439
+ },
77440
+ KiB: {
77441
+ dimension: "information",
77442
+ factor: 1024,
77443
+ offset: 0
77444
+ },
77445
+ MiB: {
77446
+ dimension: "information",
77447
+ factor: 1024 ** 2,
77448
+ offset: 0
77449
+ },
77450
+ GiB: {
77451
+ dimension: "information",
77452
+ factor: 1024 ** 3,
77453
+ offset: 0
77454
+ },
77455
+ TiB: {
77456
+ dimension: "information",
77457
+ factor: 1024 ** 4,
77458
+ offset: 0
77459
+ },
77460
+ kB: {
77461
+ dimension: "information",
77462
+ factor: 1e3,
77463
+ offset: 0
77464
+ },
77465
+ MB: {
77466
+ dimension: "information",
77467
+ factor: 1e6,
77468
+ offset: 0
77469
+ },
77470
+ GB: {
77471
+ dimension: "information",
77472
+ factor: 1e9,
77473
+ offset: 0
77474
+ },
77475
+ "°": {
77476
+ dimension: "angle",
77477
+ factor: 1,
77478
+ offset: 0
77479
+ },
77480
+ dBFS: {
77481
+ dimension: "sound-level",
77482
+ factor: 1,
77483
+ offset: 0
77484
+ },
77485
+ dBm: {
77486
+ dimension: "signal-level",
77487
+ factor: 1,
77488
+ offset: 0
77489
+ },
77490
+ mired: {
77491
+ dimension: "colour-temperature",
77492
+ factor: 1,
77493
+ offset: 0
77494
+ },
77495
+ kbps: {
77496
+ dimension: "bitrate",
77497
+ factor: 1,
77498
+ offset: 0
77499
+ },
77500
+ Mbps: {
77501
+ dimension: "bitrate",
77502
+ factor: 1e3,
77503
+ offset: 0
77504
+ },
77505
+ fps: {
77506
+ dimension: "rate",
77507
+ factor: 1,
77508
+ offset: 0
77427
77509
  }
77428
77510
  };
77429
77511
  //#endregion
@@ -77514,6 +77596,17 @@ function canConvertUnit(from, to) {
77514
77596
  * Every known unit spelling in a dimension (table insertion order — canonical
77515
77597
  * unit first). Powers the UI's same-dimension unit picker.
77516
77598
  */
77599
+ /**
77600
+ * Convert a difference, not a reading. Affine units (°F) shift an absolute
77601
+ * value by their offset; a step of 1°F is 5/9°C, so the offset is cancelled.
77602
+ */
77603
+ function tryConvertDelta(value, from, to) {
77604
+ if (from === to) return value;
77605
+ const origin = tryConvertUnit(0, from, to);
77606
+ const shifted = tryConvertUnit(value, from, to);
77607
+ if (origin === void 0 || shifted === void 0) return void 0;
77608
+ return shifted - origin;
77609
+ }
77517
77610
  function unitsForDimension(dimension) {
77518
77611
  const out = [];
77519
77612
  for (const [unit, spec] of Object.entries(UNIT_TABLE)) if (spec.dimension === dimension) out.push(unit);
@@ -77817,9 +77910,9 @@ function feederCustomizationExample(sources) {
77817
77910
  expr: "number(d)",
77818
77911
  bindings: days
77819
77912
  },
77820
- "remaining.unit": {
77913
+ "remaining.kind": {
77821
77914
  kind: "expression",
77822
- expr: "\"days\"",
77915
+ expr: "\"duration\"",
77823
77916
  bindings: {}
77824
77917
  },
77825
77918
  status: {
@@ -78085,19 +78178,9 @@ function convertUnitTargets(from) {
78085
78178
  //#region src/generated/customize-catalog.ts
78086
78179
  /**
78087
78180
  * Customize catalog, projected from every `*.cap.ts` `customize` block.
78088
- * 11 caps. Field keys are `runtimeState` paths. No Zod.
78181
+ * 5 caps. Field keys are `runtimeState` paths. No Zod.
78089
78182
  */
78090
78183
  var CAP_CUSTOMIZE_CATALOG = {
78091
- "ambient-light-sensor": {
78092
- version: 1,
78093
- targets: [],
78094
- advancedParameters: [{
78095
- path: "lux",
78096
- kind: "number",
78097
- label: "Illuminance",
78098
- unit: "lx"
78099
- }]
78100
- },
78101
78184
  "battery": {
78102
78185
  version: 1,
78103
78186
  targets: [{
@@ -78112,7 +78195,7 @@ var CAP_CUSTOMIZE_CATALOG = {
78112
78195
  require_err_msg.DeviceType.Container,
78113
78196
  require_err_msg.DeviceType.Thermostat
78114
78197
  ],
78115
- apply: "add-or-replace",
78198
+ apply: "add",
78116
78199
  vocabularyCap: "battery",
78117
78200
  vocabularyStateField: "percentage",
78118
78201
  sources: [{
@@ -78151,38 +78234,6 @@ var CAP_CUSTOMIZE_CATALOG = {
78151
78234
  when: "add"
78152
78235
  }
78153
78236
  ]
78154
- }],
78155
- advancedParameters: [
78156
- {
78157
- path: "binary",
78158
- kind: "boolean",
78159
- label: "Binary indicator"
78160
- },
78161
- {
78162
- path: "charging",
78163
- kind: "enum",
78164
- label: "Charging source",
78165
- enumValues: [
78166
- "dc",
78167
- "solar",
78168
- "none",
78169
- "unknown"
78170
- ]
78171
- },
78172
- {
78173
- path: "sleeping",
78174
- kind: "boolean",
78175
- label: "Sleeping"
78176
- }
78177
- ]
78178
- },
78179
- "binary": {
78180
- version: 1,
78181
- targets: [],
78182
- advancedParameters: [{
78183
- path: "on",
78184
- kind: "boolean",
78185
- label: "On"
78186
78237
  }]
78187
78238
  },
78188
78239
  "consumables": {
@@ -78260,12 +78311,8 @@ var CAP_CUSTOMIZE_CATALOG = {
78260
78311
  transforms: [{ kind: "identity" }]
78261
78312
  }],
78262
78313
  literals: [{
78263
- path: "remaining.unit",
78264
- values: [
78265
- "days",
78266
- "hours",
78267
- "cycles"
78268
- ]
78314
+ path: "remaining.kind",
78315
+ values: ["duration", "count"]
78269
78316
  }]
78270
78317
  },
78271
78318
  {
@@ -78323,8 +78370,7 @@ var CAP_CUSTOMIZE_CATALOG = {
78323
78370
  path: "lastFetchedAt",
78324
78371
  when: "add"
78325
78372
  }]
78326
- }],
78327
- advancedParameters: []
78373
+ }]
78328
78374
  },
78329
78375
  "doorbell": {
78330
78376
  version: 1,
@@ -78356,17 +78402,6 @@ var CAP_CUSTOMIZE_CATALOG = {
78356
78402
  counterPath: "pressCountSinceStart",
78357
78403
  highWater: "composer-advance",
78358
78404
  debounceMs: 2e3
78359
- }],
78360
- advancedParameters: []
78361
- },
78362
- "humidity-sensor": {
78363
- version: 1,
78364
- targets: [],
78365
- advancedParameters: [{
78366
- path: "percent",
78367
- kind: "number",
78368
- label: "Humidity",
78369
- unit: "%"
78370
78405
  }]
78371
78406
  },
78372
78407
  "motion": {
@@ -78375,7 +78410,7 @@ var CAP_CUSTOMIZE_CATALOG = {
78375
78410
  id: "detected",
78376
78411
  cardinality: "event",
78377
78412
  deviceTypes: [require_err_msg.DeviceType.Camera],
78378
- apply: "replace",
78413
+ apply: "add",
78379
78414
  vocabularyCap: "motion",
78380
78415
  vocabularyEvent: "onMotionChanged",
78381
78416
  sources: [{
@@ -78404,22 +78439,6 @@ var CAP_CUSTOMIZE_CATALOG = {
78404
78439
  "confirm",
78405
78440
  "replace"
78406
78441
  ]
78407
- }],
78408
- advancedParameters: []
78409
- },
78410
- "power-meter": {
78411
- version: 1,
78412
- targets: [],
78413
- advancedParameters: [{
78414
- path: "kwhTotal",
78415
- kind: "number",
78416
- label: "Energy",
78417
- unit: "kWh"
78418
- }, {
78419
- path: "watts",
78420
- kind: "number",
78421
- label: "Power",
78422
- unit: "W"
78423
78442
  }]
78424
78443
  },
78425
78444
  "running-state": {
@@ -78439,7 +78458,7 @@ var CAP_CUSTOMIZE_CATALOG = {
78439
78458
  require_err_msg.DeviceType.Thermostat,
78440
78459
  require_err_msg.DeviceType.PetFeeder
78441
78460
  ],
78442
- apply: "add-or-replace",
78461
+ apply: "add",
78443
78462
  vocabularyCap: "running-state",
78444
78463
  vocabularyStateField: "running",
78445
78464
  sources: [
@@ -78502,29 +78521,26 @@ var CAP_CUSTOMIZE_CATALOG = {
78502
78521
  sourceChangedAt: "prefer"
78503
78522
  }
78504
78523
  ]
78505
- }],
78506
- advancedParameters: []
78507
- },
78508
- "switch": {
78509
- version: 1,
78510
- targets: [],
78511
- advancedParameters: [{
78512
- path: "on",
78513
- kind: "boolean",
78514
- label: "On"
78515
- }]
78516
- },
78517
- "temperature-sensor": {
78518
- version: 1,
78519
- targets: [],
78520
- advancedParameters: [{
78521
- path: "celsius",
78522
- kind: "number",
78523
- label: "Temperature",
78524
- unit: "°C"
78525
78524
  }]
78526
78525
  }
78527
78526
  };
78527
+ /**
78528
+ * Unit meta on runtime-state fields. The composed-device row reads this;
78529
+ * it is not an override list (D944).
78530
+ */
78531
+ var CAP_RUNTIME_FIELD_UNITS = {
78532
+ "ambient-light-sensor": { lux: "lx" },
78533
+ "battery": {
78534
+ percentage: "%",
78535
+ volts: "V"
78536
+ },
78537
+ "humidity-sensor": { percent: "%" },
78538
+ "power-meter": {
78539
+ watts: "W",
78540
+ kwhTotal: "kWh"
78541
+ },
78542
+ "temperature-sensor": { celsius: "°C" }
78543
+ };
78528
78544
  //#endregion
78529
78545
  //#region src/composition/customize/field-source.ts
78530
78546
  function fromRead(source, cap, fieldPath) {
@@ -96145,7 +96161,6 @@ var SENSOR_CAP_VOCABULARY = {
96145
96161
  field: "value",
96146
96162
  quantity: t("the reading", "il valore"),
96147
96163
  label: t("Reading", "Valore"),
96148
- unitField: "unit",
96149
96164
  icon: "gauge"
96150
96165
  }]
96151
96166
  },
@@ -96281,7 +96296,7 @@ var SENSOR_CAP_VOCABULARY = {
96281
96296
  field: "temperature",
96282
96297
  quantity: t("the outdoor temperature", "la temperatura esterna"),
96283
96298
  label: t("Temperature", "Temperatura"),
96284
- unitField: "temperatureUnit",
96299
+ unit: "°C",
96285
96300
  icon: "thermometer"
96286
96301
  },
96287
96302
  {
@@ -96297,7 +96312,7 @@ var SENSOR_CAP_VOCABULARY = {
96297
96312
  field: "windSpeed",
96298
96313
  quantity: t("the wind speed", "la velocità del vento"),
96299
96314
  label: t("Wind", "Vento"),
96300
- unitField: "windSpeedUnit",
96315
+ unit: "m/s",
96301
96316
  icon: "wind"
96302
96317
  },
96303
96318
  {
@@ -96305,7 +96320,7 @@ var SENSOR_CAP_VOCABULARY = {
96305
96320
  field: "pressure",
96306
96321
  quantity: t("the air pressure", "la pressione atmosferica"),
96307
96322
  label: t("Pressure", "Pressione"),
96308
- unitField: "pressureUnit",
96323
+ unit: "hPa",
96309
96324
  icon: "gauge"
96310
96325
  }
96311
96326
  ]
@@ -102838,7 +102853,7 @@ var BucketAccumulator = class {
102838
102853
  //#region src/generated/history-catalog.ts
102839
102854
  /**
102840
102855
  * Cap history catalog, projected from every `*.cap.ts` `history` block.
102841
- * 41 caps with series (74 series), 30 caps answered `'none'`.
102856
+ * 41 caps with series (75 series), 30 caps answered `'none'`.
102842
102857
  * Field keys are `runtimeState` paths (`a.b` into fixed objects only).
102843
102858
  */
102844
102859
  var HISTORY_CATALOG = {
@@ -102855,12 +102870,12 @@ var HISTORY_CATALOG = {
102855
102870
  },
102856
102871
  "pm25": {
102857
102872
  kind: "gauge",
102858
- unit: "ug/m3",
102873
+ unit: "µg/m³",
102859
102874
  deadband: 1
102860
102875
  },
102861
102876
  "pm10": {
102862
102877
  kind: "gauge",
102863
- unit: "ug/m3",
102878
+ unit: "µg/m³",
102864
102879
  deadband: 1
102865
102880
  },
102866
102881
  "aqi": {
@@ -102890,10 +102905,16 @@ var HISTORY_CATALOG = {
102890
102905
  deadbandPct: 10,
102891
102906
  minIntervalMs: 6e4
102892
102907
  } },
102893
- "battery": { "percentage": {
102894
- kind: "gauge",
102895
- unit: "%"
102896
- } },
102908
+ "battery": {
102909
+ "percentage": {
102910
+ kind: "gauge",
102911
+ unit: "%"
102912
+ },
102913
+ "volts": {
102914
+ kind: "gauge",
102915
+ unit: "V"
102916
+ }
102917
+ },
102897
102918
  "binary": { "on": { kind: "boolean" } },
102898
102919
  "brightness": { "percentage": {
102899
102920
  kind: "gauge",
@@ -102977,7 +102998,7 @@ var HISTORY_CATALOG = {
102977
102998
  },
102978
102999
  "enum-sensor": { "value": {
102979
103000
  kind: "enum",
102980
- skipWhenSet: "format"
103001
+ skipWhenSet: "timestamp"
102981
103002
  } },
102982
103003
  "fan-control": {
102983
103004
  "percentage": {
@@ -103050,7 +103071,6 @@ var HISTORY_CATALOG = {
103050
103071
  },
103051
103072
  "numeric-sensor": { "value": {
103052
103073
  kind: "gauge",
103053
- unitFrom: "unit",
103054
103074
  minIntervalMs: 3e4
103055
103075
  } },
103056
103076
  "pet-feeder": {
@@ -103064,7 +103084,7 @@ var HISTORY_CATALOG = {
103064
103084
  },
103065
103085
  "desiccantLeftDays": {
103066
103086
  kind: "gauge",
103067
- unit: "days"
103087
+ unit: "s"
103068
103088
  }
103069
103089
  },
103070
103090
  "power-meter": {
@@ -103168,7 +103188,7 @@ var HISTORY_CATALOG = {
103168
103188
  "weather": {
103169
103189
  "temperature": {
103170
103190
  kind: "gauge",
103171
- unitFrom: "temperatureUnit",
103191
+ unit: "°C",
103172
103192
  deadband: .2
103173
103193
  },
103174
103194
  "humidity": {
@@ -103178,12 +103198,12 @@ var HISTORY_CATALOG = {
103178
103198
  },
103179
103199
  "pressure": {
103180
103200
  kind: "gauge",
103181
- unitFrom: "pressureUnit",
103201
+ unit: "hPa",
103182
103202
  deadband: .5
103183
103203
  },
103184
103204
  "windSpeed": {
103185
103205
  kind: "gauge",
103186
- unitFrom: "windSpeedUnit",
103206
+ unit: "m/s",
103187
103207
  deadband: .5
103188
103208
  }
103189
103209
  }
@@ -103217,7 +103237,6 @@ var HISTORY_ITEM_CATALOG = { "consumables": {
103217
103237
  },
103218
103238
  "remaining.value": {
103219
103239
  kind: "gauge",
103220
- unitFrom: "remaining.unit",
103221
103240
  deadband: 1,
103222
103241
  displayName: "Remaining"
103223
103242
  }
@@ -103261,6 +103280,14 @@ function historyFieldsFor(capName) {
103261
103280
  return HISTORY_CATALOG[capName] ?? {};
103262
103281
  }
103263
103282
  //#endregion
103283
+ //#region src/capabilities/runtime-field-unit.ts
103284
+ /**
103285
+ * The unit a composed-device field row offers convert-unit from (D941, D944).
103286
+ */
103287
+ function runtimeFieldUnit(cap, path) {
103288
+ return CAP_RUNTIME_FIELD_UNITS[cap]?.[path];
103289
+ }
103290
+ //#endregion
103264
103291
  //#region src/composition/customize/override-draft.ts
103265
103292
  /**
103266
103293
  * A saved Customize assignment (D936). The schema is the type: callers use
@@ -112817,6 +112844,227 @@ var DEVICE_TYPE_INFO = { ["camera"]: {
112817
112844
  icon: "camera"
112818
112845
  } };
112819
112846
  //#endregion
112847
+ //#region src/units/threshold.ts
112848
+ /**
112849
+ * Move a threshold between the unit the operator sees and the stored base.
112850
+ * A failed conversion leaves the number unchanged — the recipe stays serialisable.
112851
+ */
112852
+ function thresholdToBase(value, displayUnit, baseUnit) {
112853
+ if (!Number.isFinite(value) || displayUnit === baseUnit) return value;
112854
+ return tryConvertUnit(value, displayUnit, baseUnit) ?? value;
112855
+ }
112856
+ function thresholdToDisplay(value, baseUnit, displayUnit) {
112857
+ if (!Number.isFinite(value) || baseUnit === displayUnit) return value;
112858
+ return tryConvertUnit(value, baseUnit, displayUnit) ?? value;
112859
+ }
112860
+ //#endregion
112861
+ //#region src/units/format-reading.ts
112862
+ /**
112863
+ * Render a stored base-unit reading (D944 §4). The preference defaults to
112864
+ * metric. An operator override is applied by the caller via `convertUnit`
112865
+ * before this, or by passing the already-chosen display unit through
112866
+ * `resolveSensorDisplay` — this function is the quantity → string step.
112867
+ */
112868
+ var DURATION_STEPS = [
112869
+ {
112870
+ value: 86400,
112871
+ unit: "d"
112872
+ },
112873
+ {
112874
+ value: 3600,
112875
+ unit: "h"
112876
+ },
112877
+ {
112878
+ value: 60,
112879
+ unit: "min"
112880
+ }
112881
+ ];
112882
+ var INFO_STEPS = [
112883
+ {
112884
+ factor: 1024 ** 4,
112885
+ unit: "TiB"
112886
+ },
112887
+ {
112888
+ factor: 1024 ** 3,
112889
+ unit: "GiB"
112890
+ },
112891
+ {
112892
+ factor: 1024 ** 2,
112893
+ unit: "MiB"
112894
+ },
112895
+ {
112896
+ factor: 1024,
112897
+ unit: "KiB"
112898
+ }
112899
+ ];
112900
+ function formatMagnitude(value, precision) {
112901
+ if (!Number.isFinite(value)) return "";
112902
+ if (precision !== void 0) return value.toFixed(precision);
112903
+ return Number.isInteger(value) ? String(value) : value.toFixed(1);
112904
+ }
112905
+ function scaleDuration(seconds) {
112906
+ const abs = Math.abs(seconds);
112907
+ for (const step of DURATION_STEPS) if (abs >= step.value) return {
112908
+ value: seconds / step.value,
112909
+ unit: step.unit
112910
+ };
112911
+ return {
112912
+ value: seconds,
112913
+ unit: "s"
112914
+ };
112915
+ }
112916
+ function scaleInformation(bytes) {
112917
+ const abs = Math.abs(bytes);
112918
+ for (const step of INFO_STEPS) if (abs >= step.factor) return {
112919
+ value: bytes / step.factor,
112920
+ unit: step.unit
112921
+ };
112922
+ return {
112923
+ value: bytes,
112924
+ unit: "B"
112925
+ };
112926
+ }
112927
+ function resolvedQuantity(input) {
112928
+ if (input.quantity === "device") return input.deviceQuantity ?? "none";
112929
+ return input.quantity;
112930
+ }
112931
+ function formatReading(input) {
112932
+ const quantity = resolvedQuantity(input);
112933
+ const precision = input.precision;
112934
+ if (!Number.isFinite(input.value)) return {
112935
+ text: "",
112936
+ unit: "",
112937
+ value: input.value
112938
+ };
112939
+ if (quantity === "duration" || quantity === "duration-ms") {
112940
+ const scaled = scaleDuration(quantity === "duration-ms" ? input.value / 1e3 : input.value);
112941
+ return {
112942
+ text: formatMagnitude(scaled.value, precision),
112943
+ unit: scaled.unit,
112944
+ value: scaled.value
112945
+ };
112946
+ }
112947
+ if (quantity === "information") {
112948
+ const scaled = scaleInformation(input.value);
112949
+ return {
112950
+ text: formatMagnitude(scaled.value, precision),
112951
+ unit: scaled.unit,
112952
+ value: scaled.value
112953
+ };
112954
+ }
112955
+ const preference = input.preference ?? "metric";
112956
+ const base = require_sleep.baseUnitOf(quantity);
112957
+ const display = require_sleep.displayUnitFor(quantity, preference);
112958
+ if (base === void 0 || display === void 0) return {
112959
+ text: formatMagnitude(input.value, precision),
112960
+ unit: "",
112961
+ value: input.value
112962
+ };
112963
+ const converted = tryConvertUnit(input.value, base, display);
112964
+ if (converted === void 0) return {
112965
+ text: formatMagnitude(input.value, precision),
112966
+ unit: base,
112967
+ value: input.value
112968
+ };
112969
+ return {
112970
+ text: formatMagnitude(converted, precision),
112971
+ unit: display,
112972
+ value: converted
112973
+ };
112974
+ }
112975
+ //#endregion
112976
+ //#region src/units/ingest-reading.ts
112977
+ /**
112978
+ * Convert an upstream number into the stored base of its quantity (D944 §4).
112979
+ * An unknown unit or a unit from another dimension is a refusal: the caller
112980
+ * logs it and publishes null (D393). This function never guesses.
112981
+ */
112982
+ var DIMENSION_QUANTITY = {
112983
+ temperature: "temperature",
112984
+ pressure: "pressure",
112985
+ speed: "speed",
112986
+ "precipitation-rate": "precipitation-rate",
112987
+ length: "length",
112988
+ area: "area",
112989
+ illuminance: "illuminance",
112990
+ irradiance: "irradiance",
112991
+ power: "power",
112992
+ "apparent-power": "apparent-power",
112993
+ energy: "energy",
112994
+ voltage: "voltage",
112995
+ current: "current",
112996
+ "concentration-volume": "concentration-volume",
112997
+ "concentration-mass": "concentration-mass",
112998
+ mass: "mass",
112999
+ percentage: "percentage",
113000
+ volume: "volume",
113001
+ duration: "duration",
113002
+ information: "information",
113003
+ angle: "angle",
113004
+ "sound-level": "sound-level",
113005
+ "signal-level": "signal-level",
113006
+ "colour-temperature": "colour-temperature",
113007
+ bitrate: "bitrate",
113008
+ rate: "rate"
113009
+ };
113010
+ /**
113011
+ * Quantity implied by a unit symbol. `ppb` and `ms` are the two symbols
113012
+ * whose stored base is not the dimension's canonical unit. `mm` is length;
113013
+ * precipitation is chosen by the caller (a device class), not by the symbol.
113014
+ */
113015
+ function quantityFromUnit(rawUnit) {
113016
+ const unit = normalizeUnit(rawUnit);
113017
+ if (unit === void 0) return void 0;
113018
+ if (unit === "ppb") return "concentration-ppb";
113019
+ if (unit === "ms") return "duration-ms";
113020
+ const dimension = unitDimension(unit);
113021
+ if (dimension === void 0) return void 0;
113022
+ return DIMENSION_QUANTITY[dimension];
113023
+ }
113024
+ function ingestReading(input) {
113025
+ if (!Number.isFinite(input.value)) return {
113026
+ ok: false,
113027
+ reason: "non-finite"
113028
+ };
113029
+ if (input.quantity === "device") return {
113030
+ ok: false,
113031
+ reason: "unknown-unit"
113032
+ };
113033
+ const raw = input.rawUnit?.trim();
113034
+ if (raw === void 0 || raw.length === 0) return {
113035
+ ok: true,
113036
+ value: input.value
113037
+ };
113038
+ const unit = normalizeUnit(raw);
113039
+ if (unit === void 0) return {
113040
+ ok: false,
113041
+ reason: "unknown-unit"
113042
+ };
113043
+ const base = require_sleep.baseUnitOf(input.quantity);
113044
+ if (base === void 0) {
113045
+ if (unitDimension(unit) !== void 0) return {
113046
+ ok: false,
113047
+ reason: "cross-dimension"
113048
+ };
113049
+ return {
113050
+ ok: false,
113051
+ reason: "unknown-unit"
113052
+ };
113053
+ }
113054
+ const converted = tryConvertUnit(input.value, unit, base);
113055
+ if (converted === void 0) return unitDimension(unit) === void 0 ? {
113056
+ ok: false,
113057
+ reason: "unknown-unit"
113058
+ } : {
113059
+ ok: false,
113060
+ reason: "cross-dimension"
113061
+ };
113062
+ return {
113063
+ ok: true,
113064
+ value: converted
113065
+ };
113066
+ }
113067
+ //#endregion
112820
113068
  //#region src/util/location-match.ts
112821
113069
  /**
112822
113070
  * Pure fuzzy matcher for adoption location import. Normalized
@@ -114443,6 +114691,7 @@ exports.CAP_CUSTOMIZE_CATALOG = CAP_CUSTOMIZE_CATALOG;
114443
114691
  exports.CAP_NAMES_WITH_STATUS = CAP_NAMES_WITH_STATUS;
114444
114692
  exports.CAP_NODE_PIN_CONTEXT_KEY = require_sleep.CAP_NODE_PIN_CONTEXT_KEY;
114445
114693
  exports.CAP_PROVIDER_KIND_MAP = CAP_PROVIDER_KIND_MAP;
114694
+ exports.CAP_RUNTIME_FIELD_UNITS = CAP_RUNTIME_FIELD_UNITS;
114446
114695
  exports.CAP_VOCABULARY = CAP_VOCABULARY;
114447
114696
  exports.CAP_VOCABULARY_BY_NAME = CAP_VOCABULARY_BY_NAME;
114448
114697
  exports.CATALOG_LANGS = CATALOG_LANGS;
@@ -114641,8 +114890,8 @@ exports.ConsumableFormSchema = ConsumableFormSchema;
114641
114890
  exports.ConsumableItemSchema = ConsumableItemSchema;
114642
114891
  exports.ConsumableKindSchema = ConsumableKindSchema;
114643
114892
  exports.ConsumableLevelSchema = ConsumableLevelSchema;
114893
+ exports.ConsumableRemainingKindSchema = ConsumableRemainingKindSchema;
114644
114894
  exports.ConsumableRemainingSchema = ConsumableRemainingSchema;
114645
- exports.ConsumableRemainingUnitSchema = ConsumableRemainingUnitSchema;
114646
114895
  exports.ConsumableStatusSchema = ConsumableStatusSchema;
114647
114896
  exports.ConsumablesStatusSchema = ConsumablesStatusSchema;
114648
114897
  exports.ContactStatusSchema = ContactStatusSchema;
@@ -114688,6 +114937,7 @@ exports.DEFAULT_CLUSTER_STEP_SETTINGS = DEFAULT_CLUSTER_STEP_SETTINGS;
114688
114937
  exports.DEFAULT_CORE_BLOCK_ENTRY = DEFAULT_CORE_BLOCK_ENTRY;
114689
114938
  exports.DEFAULT_DECODER_HWACCEL_CONFIG = DEFAULT_DECODER_HWACCEL_CONFIG;
114690
114939
  exports.DEFAULT_DETAIL_CROP_CONVENTION = DEFAULT_DETAIL_CROP_CONVENTION;
114940
+ exports.DEFAULT_DISPLAY_PREFERENCE = require_sleep.DEFAULT_DISPLAY_PREFERENCE;
114691
114941
  exports.DEFAULT_EVENTS_BAND_BUFFER_SEC = DEFAULT_EVENTS_BAND_BUFFER_SEC;
114692
114942
  exports.DEFAULT_EVENT_COLOR = DEFAULT_EVENT_COLOR;
114693
114943
  exports.DEFAULT_FEATURES = DEFAULT_FEATURES;
@@ -114746,6 +114996,7 @@ exports.DEVICE_TOMBSTONE_RETENTION_MS = DEVICE_TOMBSTONE_RETENTION_MS;
114746
114996
  exports.DEVICE_TYPE_CONTROL_KIND = DEVICE_TYPE_CONTROL_KIND;
114747
114997
  exports.DEVICE_TYPE_DO_CAPS = DEVICE_TYPE_DO_CAPS;
114748
114998
  exports.DEVICE_TYPE_INFO = DEVICE_TYPE_INFO;
114999
+ exports.DISPLAY_PREFERENCES = require_sleep.DISPLAY_PREFERENCES;
114749
115000
  exports.DISPLAY_PRESETS = DISPLAY_PRESETS;
114750
115001
  exports.DOORBELL_RISE_DEBOUNCE_MS = DOORBELL_RISE_DEBOUNCE_MS;
114751
115002
  exports.DataStoreDatabaseSchema = DataStoreDatabaseSchema;
@@ -115676,6 +115927,7 @@ exports.PtzPositionSchema = PtzPositionSchema;
115676
115927
  exports.PtzPresetFunctionSchema = PtzPresetFunctionSchema;
115677
115928
  exports.PtzPresetSchema = PtzPresetSchema;
115678
115929
  exports.PtzStatusSchema = PtzStatusSchema;
115930
+ exports.QUANTITY_BASE = require_sleep.QUANTITY_BASE;
115679
115931
  exports.QueryFilterSchema = QueryFilterSchema;
115680
115932
  exports.RATE_CONTROL_RELAXED = RATE_CONTROL_RELAXED;
115681
115933
  exports.RATE_CONTROL_TIGHT = RATE_CONTROL_TIGHT;
@@ -115754,6 +116006,7 @@ exports.ReadSegmentBytesResultSchema = ReadSegmentBytesResultSchema;
115754
116006
  exports.ReadWindowBytesResultSchema = ReadWindowBytesResultSchema;
115755
116007
  exports.ReadinessRegistry = require_sleep.ReadinessRegistry;
115756
116008
  exports.ReadinessTimeoutError = require_sleep.ReadinessTimeoutError;
116009
+ exports.ReadingQuantitySchema = require_sleep.ReadingQuantitySchema;
115757
116010
  exports.RebuildObjectEmbeddingsRefusalSchema = RebuildObjectEmbeddingsRefusalSchema;
115758
116011
  exports.RecentTracksPageSchema = RecentTracksPageSchema;
115759
116012
  exports.RecentTracksQueryInput = RecentTracksQueryInput;
@@ -116320,6 +116573,7 @@ exports.autoAssignProfiles = autoAssignProfiles;
116320
116573
  exports.automationControlCapability = automationControlCapability;
116321
116574
  exports.backupCapability = backupCapability;
116322
116575
  exports.bareAddonId = bareAddonId;
116576
+ exports.baseUnitOf = require_sleep.baseUnitOf;
116323
116577
  exports.batteryCapability = batteryCapability;
116324
116578
  exports.bestLocationMatch = bestLocationMatch;
116325
116579
  exports.binaryCapability = binaryCapability;
@@ -116539,6 +116793,7 @@ exports.diffStepSettings = diffStepSettings;
116539
116793
  exports.disarmRoseMemory = require_composition.disarmRoseMemory;
116540
116794
  exports.dishwasherCapability = dishwasherCapability;
116541
116795
  exports.displayPaletteCapability = displayPaletteCapability;
116796
+ exports.displayUnitFor = require_sleep.displayUnitFor;
116542
116797
  exports.doActionSelectValue = doActionSelectValue;
116543
116798
  exports.dominantClipLabels = dominantClipLabels;
116544
116799
  exports.doorbellCapability = doorbellCapability;
@@ -116625,6 +116880,7 @@ exports.formatMediaOwnerKey = formatMediaOwnerKey;
116625
116880
  exports.formatMinutes = formatMinutes;
116626
116881
  exports.formatPaletteViolations = formatPaletteViolations;
116627
116882
  exports.formatParkedFor = formatParkedFor;
116883
+ exports.formatReading = formatReading;
116628
116884
  exports.formatSecretScope = require_sleep.formatSecretScope;
116629
116885
  exports.formulaProblem = formulaProblem;
116630
116886
  exports.freshResults = freshResults;
@@ -116649,6 +116905,7 @@ exports.heatingValveCapability = heatingValveCapability;
116649
116905
  exports.hfModelUrl = require_sleep.hfModelUrl;
116650
116906
  exports.historyFieldsFor = historyFieldsFor;
116651
116907
  exports.historyItemFieldPath = require_sleep.historyItemFieldPath;
116908
+ exports.historyUnitFor = require_sleep.historyUnitFor;
116652
116909
  exports.htmlToText = htmlToText;
116653
116910
  exports.hubAddressHostname = hubAddressHostname;
116654
116911
  exports.hubMotionNativeProvider = hubMotionNativeProvider;
@@ -116663,6 +116920,7 @@ exports.imageModesForKind = imageModesForKind;
116663
116920
  exports.imageSettingsCapability = imageSettingsCapability;
116664
116921
  exports.inferExpressionKinds = inferExpressionKinds;
116665
116922
  exports.inferModelProvider = inferModelProvider;
116923
+ exports.ingestReading = ingestReading;
116666
116924
  exports.initialPoolMemoryState = initialPoolMemoryState;
116667
116925
  exports.integrationsCapability = integrationsCapability;
116668
116926
  exports.intercomCapability = intercomCapability;
@@ -116717,6 +116975,7 @@ exports.isOauthLandingReason = isOauthLandingReason;
116717
116975
  exports.isObjectInput = isObjectInput;
116718
116976
  exports.isOccupancyRule = isOccupancyRule;
116719
116977
  exports.isOwnerOnlyEventCategory = require_event_category.isOwnerOnlyEventCategory;
116978
+ exports.isReadingQuantity = require_sleep.isReadingQuantity;
116720
116979
  exports.isRemovedModelId = isRemovedModelId;
116721
116980
  exports.isRestoredCap = isRestoredCap;
116722
116981
  exports.isRetiredClearKey = isRetiredClearKey;
@@ -116947,6 +117206,7 @@ exports.ptzAutotrackCapability = ptzAutotrackCapability;
116947
117206
  exports.ptzCapability = ptzCapability;
116948
117207
  exports.publicEventCategories = require_event_category.publicEventCategories;
116949
117208
  exports.pythonScriptForBackend = pythonScriptForBackend;
117209
+ exports.quantityFromUnit = quantityFromUnit;
116950
117210
  exports.rankStreams = rankStreams;
116951
117211
  exports.readClusterStepModels = readClusterStepModels;
116952
117212
  exports.readClusterStepSettings = readClusterStepSettings;
@@ -116965,6 +117225,7 @@ exports.readRecoveryRecipeForSteps = readRecoveryRecipeForSteps;
116965
117225
  exports.readTimelapseGeneratedAt = readTimelapseGeneratedAt;
116966
117226
  exports.readUseRootModel = readUseRootModel;
116967
117227
  exports.readinessKey = require_sleep.readinessKey;
117228
+ exports.readingMeta = require_sleep.readingMeta;
116968
117229
  exports.rebootCapability = rebootCapability;
116969
117230
  exports.recommendNode = recommendNode;
116970
117231
  exports.reconcileBlockDevices = reconcileBlockDevices;
@@ -117065,6 +117326,7 @@ exports.runningFromMediaPlayer = runningFromMediaPlayer;
117065
117326
  exports.runningFromVacuumState = runningFromVacuumState;
117066
117327
  exports.runningStateCapability = runningStateCapability;
117067
117328
  exports.runtimeDevices = runtimeDevices;
117329
+ exports.runtimeFieldUnit = runtimeFieldUnit;
117068
117330
  exports.runtimeStatePolicyFor = runtimeStatePolicyFor;
117069
117331
  exports.sameDoFamily = sameDoFamily;
117070
117332
  exports.sameRead = sameRead;
@@ -117184,6 +117446,8 @@ exports.templateVarSystemEventApplies = templateVarSystemEventApplies;
117184
117446
  exports.templateVarsFor = templateVarsFor;
117185
117447
  exports.terminalSessionCapability = terminalSessionCapability;
117186
117448
  exports.textToHtml = textToHtml;
117449
+ exports.thresholdToBase = thresholdToBase;
117450
+ exports.thresholdToDisplay = thresholdToDisplay;
117187
117451
  exports.timeOfDayToMinutes = timeOfDayToMinutes;
117188
117452
  exports.toAppLinkHubOrigin = toAppLinkHubOrigin;
117189
117453
  exports.toDeviceSummary = toDeviceSummary;
@@ -117199,6 +117463,7 @@ exports.trainingCapability = trainingCapability;
117199
117463
  exports.trainingProviderCapability = trainingProviderCapability;
117200
117464
  exports.transcodeBody = transcodeBody;
117201
117465
  exports.transformSecretWrite = require_sleep.transformSecretWrite;
117466
+ exports.tryConvertDelta = tryConvertDelta;
117202
117467
  exports.tryConvertUnit = tryConvertUnit;
117203
117468
  exports.turnProviderCapability = turnProviderCapability;
117204
117469
  exports.unitCompanionField = unitCompanionField;