@camstack/addon-osd-manager 0.1.270 → 0.1.272

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 (18) hide show
  1. package/dist/{MotionZonesSettings-CVom9abm.mjs → MotionZonesSettings-DO_rCcth.mjs} +26 -26
  2. package/dist/{PrivacyMaskSettings-Bo-mhwY3.mjs → PrivacyMaskSettings-CayY5BmJ.mjs} +34 -34
  3. package/dist/{SceneMonitorEditor-jI0DFCps.mjs → SceneMonitorEditor-L-q8wwb4.mjs} +37 -37
  4. package/dist/_stub.js +4136 -4150
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-yQC--34-.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-D-aTY-A7.mjs} +4 -4
  6. package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BBm95C74.mjs +26 -0
  7. package/dist/{hostInit-Dlnxz_Xy.mjs → hostInit-NSw9ILTo.mjs} +3 -3
  8. package/dist/index.js +427 -422
  9. package/dist/index.mjs +427 -422
  10. package/dist/{player-overlays-CVErPzOI.mjs → player-overlays-C7qgUCpt.mjs} +1 -1
  11. package/dist/remoteEntry.js +1 -1
  12. package/dist/{responsive-DvKLz6tZ.mjs → responsive-PTiwxSHa.mjs} +13 -10
  13. package/dist/{scene-monitor-copy-CFMKYWvN.mjs → scene-monitor-copy-BOMlNMKs.mjs} +1 -1
  14. package/dist/{square-7EBGU9cd.mjs → square-bnP3Db28.mjs} +1 -1
  15. package/dist/{trash-2-CL68AGsL.mjs → trash-2-DJvcr3Yh.mjs} +1 -1
  16. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-CVDDDDPu.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-CySnUJzK.mjs} +1 -1
  17. package/package.json +7 -2
  18. package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-eQtDkbeP.mjs +0 -26
package/dist/index.mjs CHANGED
@@ -5433,7 +5433,7 @@ var ZodIssueCode = {
5433
5433
  var ZodFirstPartyTypeKind;
5434
5434
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5435
5435
  //#endregion
5436
- //#region ../types/dist/sleep-BErAxhul.mjs
5436
+ //#region ../types/dist/sleep-CARd6wZF.mjs
5437
5437
  /**
5438
5438
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5439
5439
  * window to float samples (D455).
@@ -7920,6 +7920,52 @@ var RecordKindDeclarationSchema = object({
7920
7920
  }).strict();
7921
7921
  /** The kind names: lower-case, dash separated, short enough for an index name. */
7922
7922
  var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
7923
+ var READING_QUANTITIES = [
7924
+ "temperature",
7925
+ "pressure",
7926
+ "speed",
7927
+ "precipitation",
7928
+ "precipitation-rate",
7929
+ "length",
7930
+ "area",
7931
+ "illuminance",
7932
+ "irradiance",
7933
+ "power",
7934
+ "energy",
7935
+ "voltage",
7936
+ "current",
7937
+ "apparent-power",
7938
+ "humidity",
7939
+ "percentage",
7940
+ "concentration-volume",
7941
+ "concentration-mass",
7942
+ "concentration-ppb",
7943
+ "volume",
7944
+ "duration",
7945
+ "duration-ms",
7946
+ "information",
7947
+ "sound-level",
7948
+ "signal-level",
7949
+ "colour-temperature",
7950
+ "angle",
7951
+ "bitrate",
7952
+ "rate",
7953
+ "mass",
7954
+ "index",
7955
+ "ratio",
7956
+ "count",
7957
+ "none",
7958
+ "device"
7959
+ ];
7960
+ var ReadingQuantitySchema = _enum(READING_QUANTITIES);
7961
+ new Set(READING_QUANTITIES);
7962
+ function readingMeta(quantity, extras) {
7963
+ return {
7964
+ quantity,
7965
+ ...extras?.precision !== void 0 ? { precision: extras.precision } : {},
7966
+ ...extras?.label !== void 0 ? { label: extras.label } : {}
7967
+ };
7968
+ }
7923
7969
  /** Shared Zod schemas used across streaming capabilities. */
7924
7970
  var CamProfileSchema = _enum([
7925
7971
  "high",
@@ -7947,8 +7993,8 @@ var CamStreamKindSchema = _enum([
7947
7993
  "derived"
7948
7994
  ]);
7949
7995
  var CamStreamResolutionSchema = object({
7950
- width: number().int().positive(),
7951
- height: number().int().positive()
7996
+ width: number().int().positive().meta(readingMeta("none")),
7997
+ height: number().int().positive().meta(readingMeta("none"))
7952
7998
  });
7953
7999
  var CameraStreamSchema = object({
7954
8000
  /** Stable, provider-assigned id unique within the (deviceId) scope. */
@@ -8592,14 +8638,6 @@ var CAP_STATE_LEAVES = Object.freeze({
8592
8638
  {
8593
8639
  "path": "aqi",
8594
8640
  "valueType": "number"
8595
- },
8596
- {
8597
- "path": "unit",
8598
- "valueType": "string"
8599
- },
8600
- {
8601
- "path": "precision",
8602
- "valueType": "number"
8603
8641
  }
8604
8642
  ],
8605
8643
  "alarm-panel": [
@@ -8635,20 +8673,10 @@ var CAP_STATE_LEAVES = Object.freeze({
8635
8673
  "valueType": "boolean"
8636
8674
  }
8637
8675
  ],
8638
- "ambient-light-sensor": [
8639
- {
8640
- "path": "lux",
8641
- "valueType": "number"
8642
- },
8643
- {
8644
- "path": "unit",
8645
- "valueType": "string"
8646
- },
8647
- {
8648
- "path": "precision",
8649
- "valueType": "number"
8650
- }
8651
- ],
8676
+ "ambient-light-sensor": [{
8677
+ "path": "lux",
8678
+ "valueType": "number"
8679
+ }],
8652
8680
  "audio-metrics": [
8653
8681
  {
8654
8682
  "path": "ts",
@@ -8728,6 +8756,10 @@ var CAP_STATE_LEAVES = Object.freeze({
8728
8756
  "path": "lastUpdated",
8729
8757
  "valueType": "number"
8730
8758
  },
8759
+ {
8760
+ "path": "volts",
8761
+ "valueType": "number"
8762
+ },
8731
8763
  {
8732
8764
  "path": "lastContactAt",
8733
8765
  "valueType": "number"
@@ -8970,10 +9002,6 @@ var CAP_STATE_LEAVES = Object.freeze({
8970
9002
  "datetime"
8971
9003
  ]
8972
9004
  },
8973
- {
8974
- "path": "unit",
8975
- "valueType": "string"
8976
- },
8977
9005
  {
8978
9006
  "path": "min",
8979
9007
  "valueType": "number"
@@ -8985,19 +9013,6 @@ var CAP_STATE_LEAVES = Object.freeze({
8985
9013
  {
8986
9014
  "path": "step",
8987
9015
  "valueType": "number"
8988
- },
8989
- {
8990
- "path": "precision",
8991
- "valueType": "number"
8992
- },
8993
- {
8994
- "path": "format",
8995
- "valueType": "enum",
8996
- "enumValues": [
8997
- "date",
8998
- "time",
8999
- "datetime"
9000
- ]
9001
9016
  }
9002
9017
  ],
9003
9018
  "cover": [
@@ -9238,13 +9253,8 @@ var CAP_STATE_LEAVES = Object.freeze({
9238
9253
  "path": "value",
9239
9254
  "valueType": "string"
9240
9255
  }, {
9241
- "path": "format",
9242
- "valueType": "enum",
9243
- "enumValues": [
9244
- "date",
9245
- "time",
9246
- "datetime"
9247
- ]
9256
+ "path": "timestamp",
9257
+ "valueType": "boolean"
9248
9258
  }],
9249
9259
  "event-emitter": [
9250
9260
  {
@@ -9351,20 +9361,10 @@ var CAP_STATE_LEAVES = Object.freeze({
9351
9361
  "valueType": "number"
9352
9362
  }
9353
9363
  ],
9354
- "humidity-sensor": [
9355
- {
9356
- "path": "percent",
9357
- "valueType": "number"
9358
- },
9359
- {
9360
- "path": "unit",
9361
- "valueType": "string"
9362
- },
9363
- {
9364
- "path": "precision",
9365
- "valueType": "number"
9366
- }
9367
- ],
9364
+ "humidity-sensor": [{
9365
+ "path": "percent",
9366
+ "valueType": "number"
9367
+ }],
9368
9368
  "image": [{
9369
9369
  "path": "url",
9370
9370
  "valueType": "string"
@@ -9703,20 +9703,10 @@ var CAP_STATE_LEAVES = Object.freeze({
9703
9703
  "valueType": "boolean"
9704
9704
  }
9705
9705
  ],
9706
- "numeric-sensor": [
9707
- {
9708
- "path": "value",
9709
- "valueType": "number"
9710
- },
9711
- {
9712
- "path": "unit",
9713
- "valueType": "string"
9714
- },
9715
- {
9716
- "path": "precision",
9717
- "valueType": "number"
9718
- }
9719
- ],
9706
+ "numeric-sensor": [{
9707
+ "path": "value",
9708
+ "valueType": "number"
9709
+ }],
9720
9710
  "pet-feeder": [
9721
9711
  {
9722
9712
  "path": "foodLevel",
@@ -9808,14 +9798,6 @@ var CAP_STATE_LEAVES = Object.freeze({
9808
9798
  {
9809
9799
  "path": "powerFactor",
9810
9800
  "valueType": "number"
9811
- },
9812
- {
9813
- "path": "unit",
9814
- "valueType": "string"
9815
- },
9816
- {
9817
- "path": "precision",
9818
- "valueType": "number"
9819
9801
  }
9820
9802
  ],
9821
9803
  "presence": [
@@ -9844,20 +9826,10 @@ var CAP_STATE_LEAVES = Object.freeze({
9844
9826
  "valueType": "number"
9845
9827
  }
9846
9828
  ],
9847
- "pressure-sensor": [
9848
- {
9849
- "path": "hpa",
9850
- "valueType": "number"
9851
- },
9852
- {
9853
- "path": "unit",
9854
- "valueType": "string"
9855
- },
9856
- {
9857
- "path": "precision",
9858
- "valueType": "number"
9859
- }
9860
- ],
9829
+ "pressure-sensor": [{
9830
+ "path": "hpa",
9831
+ "valueType": "number"
9832
+ }],
9861
9833
  "privacy-mask": [{
9862
9834
  "path": "enabled",
9863
9835
  "valueType": "boolean"
@@ -10168,20 +10140,10 @@ var CAP_STATE_LEAVES = Object.freeze({
10168
10140
  "path": "tampered",
10169
10141
  "valueType": "boolean"
10170
10142
  }],
10171
- "temperature-sensor": [
10172
- {
10173
- "path": "celsius",
10174
- "valueType": "number"
10175
- },
10176
- {
10177
- "path": "unit",
10178
- "valueType": "string"
10179
- },
10180
- {
10181
- "path": "precision",
10182
- "valueType": "number"
10183
- }
10184
- ],
10143
+ "temperature-sensor": [{
10144
+ "path": "celsius",
10145
+ "valueType": "number"
10146
+ }],
10185
10147
  "update": [
10186
10148
  {
10187
10149
  "path": "currentVersion",
@@ -10292,10 +10254,6 @@ var CAP_STATE_LEAVES = Object.freeze({
10292
10254
  "path": "temperature",
10293
10255
  "valueType": "number"
10294
10256
  },
10295
- {
10296
- "path": "temperatureUnit",
10297
- "valueType": "string"
10298
- },
10299
10257
  {
10300
10258
  "path": "humidity",
10301
10259
  "valueType": "number"
@@ -10304,18 +10262,10 @@ var CAP_STATE_LEAVES = Object.freeze({
10304
10262
  "path": "pressure",
10305
10263
  "valueType": "number"
10306
10264
  },
10307
- {
10308
- "path": "pressureUnit",
10309
- "valueType": "string"
10310
- },
10311
10265
  {
10312
10266
  "path": "windSpeed",
10313
10267
  "valueType": "number"
10314
10268
  },
10315
- {
10316
- "path": "windSpeedUnit",
10317
- "valueType": "string"
10318
- },
10319
10269
  {
10320
10270
  "path": "windBearing",
10321
10271
  "valueType": "number"
@@ -17680,7 +17630,8 @@ var CompositionProblemSchema = object({
17680
17630
  "item-field-reserved",
17681
17631
  "target-missing",
17682
17632
  "target-unreadable",
17683
- "not-replaceable"
17633
+ "not-replaceable",
17634
+ "native-capability-replace"
17684
17635
  ]),
17685
17636
  path: string(),
17686
17637
  message: string()
@@ -17738,9 +17689,11 @@ var CompositionFieldPlanSchema = object({
17738
17689
  reads: array(CompositionFieldReadSchema)
17739
17690
  });
17740
17691
  /**
17741
- * What a feature does to its target (D663): `new` builds a capability on a new
17742
- * device; on an existing device it `add`s a capability the device lacks, or
17743
- * `replace`s named fields of one it has natively.
17692
+ * What a feature does to its target (D663, D944): `new` builds a capability on a
17693
+ * new device; on an existing device it `add`s a capability the device lacks.
17694
+ * Replacing a capability the device already serves is refused
17695
+ * (`native-capability-replace`), not planned. `replace` remains on the claim
17696
+ * wire for a claim the hub already holds.
17744
17697
  */
17745
17698
  var CompositionFeatureModeSchema = _enum([
17746
17699
  "new",
@@ -19626,12 +19579,12 @@ method(object({ deviceId: number().int().nonnegative() }), DeviceAdminLinkSchema
19626
19579
  * providers detect renames by matching `uniqueId` and patch `id`
19627
19580
  * in-place so dispatch keeps working.
19628
19581
  *
19629
- * **Rendering metadata is NOT stored here.** Display fields (`unit`,
19630
- * `precision`) live in the cap STATUS SLICE so they flow live with every
19631
- * state push and are the single source of truth. The `device_class` tag is
19632
- * an HA-specific input that is consumed at the discovery boundary
19633
- * (`ha-domain-mapping`) to derive the CamStack `DeviceRole`; it is never
19634
- * stored after that point.
19582
+ * **Display unit and precision are NOT stored here.** They are a read-time
19583
+ * preference (D944). A polymorphic reading stores its `quantity` here once,
19584
+ * at adopt, and a date/time value stores `dateTimeShape` the same way —
19585
+ * neither is repeated on each sample. The `device_class` tag is an
19586
+ * HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
19587
+ * to derive the CamStack `DeviceRole`; it is never stored after that point.
19635
19588
  *
19636
19589
  * The `raw` escape hatch carries provider-specific attributes that don't
19637
19590
  * fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
@@ -19645,6 +19598,14 @@ var SourceInfoSchema = object({
19645
19598
  /** Immutable upstream identifier when available (HA `unique_id`, … ).
19646
19599
  * Used to detect rename when `id` changes. */
19647
19600
  uniqueId: string().optional(),
19601
+ /** Quantity of a polymorphic reading, written once at adopt (D944). */
19602
+ quantity: ReadingQuantitySchema.optional(),
19603
+ /** Date / time / datetime shape of a string reading, written once at adopt. */
19604
+ dateTimeShape: _enum([
19605
+ "date",
19606
+ "time",
19607
+ "datetime"
19608
+ ]).optional(),
19648
19609
  /** Provider-specific extras that don't fit the structured slots.
19649
19610
  * Whitelist what goes here per provider — do NOT dump entire HA
19650
19611
  * attribute blobs (would explode the meta JSON column). */
@@ -19690,9 +19651,38 @@ function normalizeUnit(raw) {
19690
19651
  if (lower === "mph") return "mph";
19691
19652
  if (lower === "m/s") return "m/s";
19692
19653
  if (lower === "m") return "m";
19654
+ if (lower === "m²" || lower === "m2") return "m²";
19655
+ if (lower === "mm") return "mm";
19656
+ if (lower === "cm") return "cm";
19693
19657
  if (lower === "km") return "km";
19658
+ if (lower === "in" || lower === "inch" || lower === "inches") return "in";
19659
+ if (lower === "ft" || lower === "foot" || lower === "feet") return "ft";
19694
19660
  if (lower === "kg") return "kg";
19695
19661
  if (lower === "g") return "g";
19662
+ if (lower === "l" || lower === "liter" || lower === "litre" || lower === "liters" || lower === "litres") return "L";
19663
+ if (lower === "ml") return "mL";
19664
+ if (lower === "gal" || lower === "gallon" || lower === "gallons") return "gal";
19665
+ if (lower === "s" || lower === "sec" || lower === "second" || lower === "seconds") return "s";
19666
+ if (lower === "ms") return "ms";
19667
+ if (lower === "min" || lower === "minute" || lower === "minutes") return "min";
19668
+ if (lower === "h" || lower === "hr" || lower === "hour" || lower === "hours") return "h";
19669
+ if (lower === "d" || lower === "day" || lower === "days") return "d";
19670
+ if (lower === "b" || lower === "byte" || lower === "bytes") return "B";
19671
+ if (lower === "kib") return "KiB";
19672
+ if (lower === "mib") return "MiB";
19673
+ if (lower === "gib") return "GiB";
19674
+ if (lower === "tib") return "TiB";
19675
+ if (lower === "kb") return "kB";
19676
+ if (lower === "mb") return "MB";
19677
+ if (lower === "gb") return "GB";
19678
+ if (lower === "dbm") return "dBm";
19679
+ if (lower === "dbfs") return "dBFS";
19680
+ if (lower === "inhg") return "inHg";
19681
+ if (lower === "mm/h") return "mm/h";
19682
+ if (lower === "w/m²" || lower === "w/m2") return "W/m²";
19683
+ if (lower === "°" || lower === "deg" || lower === "degree" || lower === "degrees") return "°";
19684
+ if (lower === "kbps") return "kbps";
19685
+ if (lower === "fps") return "fps";
19696
19686
  return trimmed;
19697
19687
  }
19698
19688
  /**
@@ -40602,7 +40592,8 @@ method(object({
40602
40592
  "enum"
40603
40593
  ]),
40604
40594
  enumValues: array(string()).optional(),
40605
- item: boolean().optional()
40595
+ item: boolean().optional(),
40596
+ quantity: ReadingQuantitySchema.optional()
40606
40597
  })).readonly(),
40607
40598
  itemArray: object({
40608
40599
  path: string(),
@@ -44402,8 +44393,8 @@ var MaskPolygonShapeSchema = object({
44402
44393
  /** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
44403
44394
  var MaskGridShapeSchema = object({
44404
44395
  kind: literal("grid"),
44405
- gridWidth: number(),
44406
- gridHeight: number(),
44396
+ gridWidth: number().meta(readingMeta("none")),
44397
+ gridHeight: number().meta(readingMeta("none")),
44407
44398
  cells: array(boolean())
44408
44399
  });
44409
44400
  discriminatedUnion("kind", [
@@ -56389,46 +56380,27 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
56389
56380
  }), LogStreamEntrySchema, { kind: "subscription" });
56390
56381
  object({
56391
56382
  /** Carbon dioxide concentration in ppm. */
56392
- co2Ppm: number().min(0).optional(),
56383
+ co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
56393
56384
  /** Total volatile organic compounds in ppb. */
56394
- vocPpb: number().min(0).optional(),
56385
+ vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
56395
56386
  /** Particulate matter ≤ 2.5 μm in µg/m³. */
56396
- pm25: number().min(0).optional(),
56387
+ pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
56397
56388
  /** Particulate matter ≤ 10 μm in µg/m³. */
56398
- pm10: number().min(0).optional(),
56389
+ pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
56399
56390
  /** Composite AQI value (typically 0..500). */
56400
- aqi: number().optional(),
56391
+ aqi: number().optional().meta(readingMeta("index")),
56401
56392
  /** Ms epoch when the slice was last updated. */
56402
- lastFetchedAt: number(),
56403
- /** Live display unit of the single metric this slice carries (e.g. HA
56404
- * `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
56405
- * upstream `sensor.*` entity surfaces ONE device_class, so one unit
56406
- * per slice is unambiguous. */
56407
- unit: string().optional(),
56408
- /** Suggested decimal places for numeric display.
56409
- * Populated live from the upstream source when provided (e.g. HA
56410
- * `attributes.suggested_display_precision`). Falls back to
56411
- * auto-formatting when absent. */
56412
- precision: number().int().min(0).max(10).optional()
56393
+ lastFetchedAt: number()
56413
56394
  });
56414
56395
  DeviceType.Sensor;
56415
56396
  object({
56416
- /** Current illuminance in lux (lx). */
56417
- lux: number().min(0).meta({
56418
- unit: "lx",
56397
+ /** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
56398
+ lux: number().min(0).nullable().meta(readingMeta("illuminance", {
56399
+ precision: 0,
56419
56400
  label: "Illuminance"
56420
- }),
56401
+ })),
56421
56402
  /** Ms epoch when the slice was last updated. */
56422
- lastFetchedAt: number(),
56423
- /** Live display unit from the upstream source (e.g. HA
56424
- * `attributes.unit_of_measurement`). The UI prefers this over the
56425
- * role's canonical unit. Absent → fall back to the canonical unit. */
56426
- unit: string().optional(),
56427
- /** Suggested decimal places for numeric display.
56428
- * Populated live from the upstream source when provided (e.g. HA
56429
- * `attributes.suggested_display_precision`). Falls back to
56430
- * auto-formatting when absent. */
56431
- precision: number().int().min(0).max(10).optional()
56403
+ lastFetchedAt: number()
56432
56404
  });
56433
56405
  DeviceType.Sensor;
56434
56406
  /**
@@ -56573,10 +56545,7 @@ var BatteryStatusSchema = object({
56573
56545
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
56574
56546
  * declines to band a non-finite reading, which is the correct shape.
56575
56547
  */
56576
- percentage: number().min(0).max(100).nullable().meta({
56577
- unit: "%",
56578
- label: "Charge"
56579
- }),
56548
+ percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
56580
56549
  /**
56581
56550
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
56582
56551
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -56603,7 +56572,14 @@ var BatteryStatusSchema = object({
56603
56572
  */
56604
56573
  sleeping: boolean().meta({ label: "Sleeping" }),
56605
56574
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
56606
- lastUpdated: number(),
56575
+ lastUpdated: number().meta(readingMeta("none")),
56576
+ /**
56577
+ * Battery voltage in volts, for a cell the firmware reports as voltage
56578
+ * rather than a percentage (Ecowitt capacitor packs). Absent on cameras
56579
+ * that only report charge. A second reading on this cap, not a second
56580
+ * `numeric-sensor` on the same device.
56581
+ */
56582
+ volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
56607
56583
  /**
56608
56584
  * Ms epoch of the last time the device PROVED it was reachable — a
56609
56585
  * completed firmware round-trip, an observed wake, or an inbound push
@@ -56663,7 +56639,7 @@ object({
56663
56639
  DeviceType.Sensor;
56664
56640
  object({
56665
56641
  /** Current level as 0..100 inclusive. Firmware-reported. */
56666
- percentage: number().min(0).max(100),
56642
+ percentage: number().min(0).max(100).meta(readingMeta("percentage")),
56667
56643
  /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
56668
56644
  lastChangedAt: number()
56669
56645
  });
@@ -57048,21 +57024,21 @@ object({
57048
57024
  /** Single setpoint in Celsius. Used by single-target modes
57049
57025
  * (heat / cool / dry). Null when the device is in a range mode
57050
57026
  * (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
57051
- target: number().nullable(),
57027
+ target: number().nullable().meta(readingMeta("temperature")),
57052
57028
  /** Upper bound of the dual setpoint in Celsius. Populated only
57053
57029
  * in `heat_cool` mode. */
57054
- targetHigh: number().nullable(),
57030
+ targetHigh: number().nullable().meta(readingMeta("temperature")),
57055
57031
  /** Lower bound of the dual setpoint in Celsius. Populated only
57056
57032
  * in `heat_cool` mode. */
57057
- targetLow: number().nullable(),
57033
+ targetLow: number().nullable().meta(readingMeta("temperature")),
57058
57034
  /** Target relative humidity (0..100). Null when the device has no
57059
57035
  * humidity control. */
57060
- targetHumidity: number().min(0).max(100).nullable(),
57036
+ targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
57061
57037
  /** Current measured temperature in Celsius. Read-only — pushed by
57062
57038
  * the device's internal sensor. */
57063
- currentTemp: number().nullable(),
57039
+ currentTemp: number().nullable().meta(readingMeta("temperature")),
57064
57040
  /** Current measured relative humidity (0..100). Read-only. */
57065
- currentHumidity: number().min(0).max(100).nullable(),
57041
+ currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
57066
57042
  /** Vertical louver swing on/off. Null when the device has no vertical
57067
57043
  * swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
57068
57044
  swingVertical: boolean().nullable(),
@@ -57070,11 +57046,11 @@ object({
57070
57046
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
57071
57047
  swingHorizontal: boolean().nullable(),
57072
57048
  /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
57073
- minTemp: number().nullable().optional(),
57049
+ minTemp: number().nullable().optional().meta(readingMeta("temperature")),
57074
57050
  /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
57075
- maxTemp: number().nullable().optional(),
57051
+ maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
57076
57052
  /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
57077
- tempStep: number().positive().nullable().optional(),
57053
+ tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
57078
57054
  /** Ms epoch when the slice was last updated (push or command). */
57079
57055
  lastFetchedAt: number()
57080
57056
  });
@@ -57153,17 +57129,17 @@ DeviceType.Thermostat, DeviceType.Climate, method(object({
57153
57129
  * modes to expose in the picker without a slice fetch.
57154
57130
  */
57155
57131
  var RgbTripletSchema = object({
57156
- r: number().int().min(0).max(255),
57157
- g: number().int().min(0).max(255),
57158
- b: number().int().min(0).max(255)
57132
+ r: number().int().min(0).max(255).meta(readingMeta("none")),
57133
+ g: number().int().min(0).max(255).meta(readingMeta("none")),
57134
+ b: number().int().min(0).max(255).meta(readingMeta("none"))
57159
57135
  });
57160
57136
  var HsvTripletSchema = object({
57161
57137
  /** Hue in degrees, 0..360. */
57162
- h: number().min(0).max(360),
57138
+ h: number().min(0).max(360).meta(readingMeta("none")),
57163
57139
  /** Saturation as 0..100 inclusive. */
57164
- s: number().min(0).max(100),
57140
+ s: number().min(0).max(100).meta(readingMeta("none")),
57165
57141
  /** Value/brightness as 0..100 inclusive. */
57166
- v: number().min(0).max(100)
57142
+ v: number().min(0).max(100).meta(readingMeta("none"))
57167
57143
  });
57168
57144
  var ColorInputSchema = discriminatedUnion("mode", [
57169
57145
  object({
@@ -57192,7 +57168,7 @@ object({
57192
57168
  /** Populated when `mode === 'hsv'`. */
57193
57169
  hsv: HsvTripletSchema.optional(),
57194
57170
  /** Populated when `mode === 'mired'`. */
57195
- mireds: number().int().optional(),
57171
+ mireds: number().int().optional().meta(readingMeta("colour-temperature")),
57196
57172
  /** Ms epoch of the last operator-driven change. */
57197
57173
  lastChangedAt: number()
57198
57174
  });
@@ -57316,18 +57292,12 @@ _enum([
57316
57292
  "empty",
57317
57293
  "full"
57318
57294
  ]);
57319
- /** Units a remaining-life counter is reported in. Closed: the panel renders
57320
- * each. */
57321
- var ConsumableRemainingUnitSchema = _enum([
57322
- "days",
57323
- "hours",
57324
- "cycles"
57325
- ]);
57326
- /** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
57327
- * days). `value` null = unknown (D393). */
57295
+ /** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
57296
+ var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
57297
+ /** Remaining life. `value` null = unknown (D393). Duration is seconds. */
57328
57298
  var ConsumableRemainingSchema = object({
57329
- value: number().min(0).nullable(),
57330
- unit: ConsumableRemainingUnitSchema
57299
+ value: number().min(0).nullable().meta(readingMeta("device")),
57300
+ kind: ConsumableRemainingKindSchema
57331
57301
  });
57332
57302
  var ConsumablesStatusSchema = object({
57333
57303
  items: array(object({
@@ -57336,7 +57306,7 @@ var ConsumablesStatusSchema = object({
57336
57306
  /** Display name. */
57337
57307
  label: string().min(1),
57338
57308
  /** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
57339
- level: number().min(0).max(100).nullable(),
57309
+ level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57340
57310
  /** The provider's discrete verdict when known (binary mode). */
57341
57311
  status: ConsumableStatusSchema.nullable(),
57342
57312
  /** What the item is — decides whether low or full is bad. Absent = `wear`. */
@@ -57353,7 +57323,7 @@ var ConsumablesStatusSchema = object({
57353
57323
  })),
57354
57324
  lastChangedAt: number()
57355
57325
  });
57356
- Object.values(DeviceType), [...ConsumableRemainingUnitSchema.options], [...ConsumableStatusSchema.options], [...ConsumableKindSchema.options], [...ConsumableFormSchema.options];
57326
+ Object.values(DeviceType), [...ConsumableRemainingKindSchema.options], [...ConsumableStatusSchema.options], [...ConsumableKindSchema.options], [...ConsumableFormSchema.options];
57357
57327
  Object.values(DeviceType), method(object({
57358
57328
  deviceId: number().int().nonnegative(),
57359
57329
  key: string().min(1)
@@ -57378,39 +57348,17 @@ object({
57378
57348
  /** Current value. Numeric kind → number; select/text/datetime → string.
57379
57349
  * Datetime values are ISO 8601 strings (full timestamp, date-only,
57380
57350
  * or time-only — HA accepts all three forms). */
57381
- value: union([number(), string()]),
57351
+ value: union([number().meta(readingMeta("device")), string()]),
57382
57352
  /** Acceptable values when `kind === 'select'`. Empty for other kinds. */
57383
57353
  options: array(string()),
57384
57354
  /** Ms epoch when the slice was last updated. */
57385
57355
  lastChangedAt: number(),
57386
- /** Display unit-of-measurement (e.g. '°C', '%', 'lx').
57387
- * Populated live from HA `attributes.unit_of_measurement` on each push.
57388
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57389
- unit: string().optional(),
57390
- /** Minimum allowed value. Populated live from HA `attributes.min`.
57391
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57392
- min: number().optional(),
57393
- /** Maximum allowed value. Populated live from HA `attributes.max`.
57394
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57395
- max: number().optional(),
57396
- /** Step increment. Populated live from HA `attributes.step`.
57397
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57398
- step: number().optional(),
57399
- /** Suggested decimal places for numeric display. Populated live from HA
57400
- * `attributes.suggested_display_precision`, or derived from `step` when the
57401
- * HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
57402
- * other kinds. Falls back to auto-formatting when absent. */
57403
- precision: number().int().min(0).max(10).optional(),
57404
- /** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
57405
- * 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
57406
- * (UI defaults to 'datetime'). Derived live from the device's HA domain
57407
- * (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
57408
- * `has_date`/`has_time` attributes on each push. */
57409
- format: _enum([
57410
- "date",
57411
- "time",
57412
- "datetime"
57413
- ]).optional()
57356
+ /** Minimum allowed value, in the device quantity's base unit. */
57357
+ min: number().optional().meta(readingMeta("device")),
57358
+ /** Maximum allowed value, in the device quantity's base unit. */
57359
+ max: number().optional().meta(readingMeta("device")),
57360
+ /** Step increment, in the device quantity's base unit. */
57361
+ step: number().optional().meta(readingMeta("device"))
57414
57362
  });
57415
57363
  var ControlSetValueInputSchema = discriminatedUnion("kind", [
57416
57364
  object({
@@ -57448,9 +57396,9 @@ object({
57448
57396
  ]),
57449
57397
  /** 0 = fully closed, 100 = fully open. Null when the device has
57450
57398
  * no intermediate position surface. */
57451
- position: number().min(0).max(100).nullable(),
57399
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57452
57400
  /** 0 = closed slats, 100 = open slats. Null when no tilt support. */
57453
- tiltPosition: number().min(0).max(100).nullable(),
57401
+ tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57454
57402
  /** Ms epoch when the slice was last updated. */
57455
57403
  lastChangedAt: number()
57456
57404
  });
@@ -57508,9 +57456,9 @@ var NormalizedRangeSchema$1 = object({
57508
57456
  object({
57509
57457
  mode: DayNightModeSchema,
57510
57458
  /** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
57511
- sensitivity: number().optional(),
57459
+ sensitivity: number().optional().meta(readingMeta("none")),
57512
57460
  /** Delay before the IR-cut filter flips, in seconds. */
57513
- switchDelaySec: number().optional(),
57461
+ switchDelaySec: number().optional().meta(readingMeta("duration")),
57514
57462
  lastFetchedAt: number()
57515
57463
  });
57516
57464
  /**
@@ -57665,11 +57613,11 @@ object({
57665
57613
  /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
57666
57614
  availablePrograms: array(string()),
57667
57615
  /** Seconds remaining on the active cycle, or null when not running / not known. */
57668
- remainingSeconds: number().int().nonnegative().nullable(),
57616
+ remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
57669
57617
  /** Seconds the delayed start is set to (time until the cycle starts), or null. */
57670
- delayedStartSeconds: number().int().nonnegative().nullable(),
57618
+ delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
57671
57619
  /** Cycle completion 0..100, or null when not applicable. */
57672
- progressPercent: number().min(0).max(100).nullable(),
57620
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57673
57621
  /** True when the door is reported open; null when the door state is not known. */
57674
57622
  doorOpen: boolean().nullable(),
57675
57623
  /** True when the door is latched (locked) during a cycle; null when not reported. */
@@ -57693,7 +57641,7 @@ object({
57693
57641
  /** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
57694
57642
  machineCareDue: boolean().nullable(),
57695
57643
  /** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
57696
- machineCareRemainingRuns: number().int().nonnegative().nullable(),
57644
+ machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
57697
57645
  /** Option states: null when the appliance does not expose the option or has not said. */
57698
57646
  halfLoad: boolean().nullable(),
57699
57647
  hygienePlus: boolean().nullable(),
@@ -57708,7 +57656,7 @@ object({
57708
57656
  /** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
57709
57657
  activeEvents: array(string()),
57710
57658
  /** Numeric error code when the appliance reports one; null otherwise. */
57711
- errorCode: number().nullable(),
57659
+ errorCode: number().nullable().meta(readingMeta("none")),
57712
57660
  /** Name of the raised alert-level event while the appliance is in error; null otherwise. */
57713
57661
  errorLabel: string().nullable(),
57714
57662
  /** Ms epoch of the last value received from the appliance; 0 = never. */
@@ -57854,24 +57802,14 @@ object({
57854
57802
  /** Ms epoch of the last press. null = never observed since this provider started. */
57855
57803
  lastPressedAt: number().nullable(),
57856
57804
  /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
57857
- pressCountSinceStart: number()
57805
+ pressCountSinceStart: number().meta(readingMeta("count"))
57858
57806
  });
57859
57807
  object({
57860
57808
  deviceId: number(),
57861
57809
  timestamp: number()
57862
57810
  });
57863
57811
  DeviceType.Button, DeviceType.Camera;
57864
- /**
57865
- * Enum-state sensor — a string value picked from a finite option set.
57866
- * Drives HA `sensor` entries with `state_class: enum` (HVAC action
57867
- * states, weather conditions, contact-source channels, mode strings).
57868
- * The option set is stable across observations; it is persisted in the
57869
- * device config blob (via the control slice at adoption) and is NOT
57870
- * stored in `sourceInfo`.
57871
- */
57872
- /** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
57873
- * enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
57874
- var EnumSensorDateTimeFormatSchema = _enum([
57812
+ _enum([
57875
57813
  "date",
57876
57814
  "time",
57877
57815
  "datetime"
@@ -57879,12 +57817,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
57879
57817
  object({
57880
57818
  value: string(),
57881
57819
  /**
57882
- * Set for `DateTimeSensor`-role sensors so the UI renders the ISO `value`
57883
- * as a locale date/time/datetime; absent for plain enum sensors. HA
57884
- * `device_class=timestamp` → `'datetime'`, `device_class=date` → `'date'`,
57885
- * a time-only sensor → `'time'`.
57820
+ * Set when `value` is a timestamp rather than an enum state, so history
57821
+ * does not learn ISO strings as states. The display shape (date / time /
57822
+ * datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
57886
57823
  */
57887
- format: EnumSensorDateTimeFormatSchema.optional(),
57824
+ timestamp: boolean().optional(),
57888
57825
  /** Ms epoch when the slice was last updated. */
57889
57826
  lastFetchedAt: number()
57890
57827
  });
@@ -57903,13 +57840,13 @@ var EventFireSchema = object({
57903
57840
  deviceId: number(),
57904
57841
  eventType: string(),
57905
57842
  data: record(string(), unknown()).nullable(),
57906
- timestamp: number(),
57907
- seq: number()
57843
+ timestamp: number().meta(readingMeta("none")),
57844
+ seq: number().meta(readingMeta("count"))
57908
57845
  });
57909
57846
  object({
57910
57847
  eventTypes: array(string()),
57911
57848
  lastEvent: EventFireSchema.nullable(),
57912
- eventCountSinceStart: number()
57849
+ eventCountSinceStart: number().meta(readingMeta("count"))
57913
57850
  });
57914
57851
  DeviceType.EventEmitter;
57915
57852
  var IdentitySchema = object({
@@ -58298,10 +58235,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
58298
58235
  object({
58299
58236
  /** Active speed as 0..100 inclusive. Null when the device has no
58300
58237
  * speed surface (single-speed fan). */
58301
- percentage: number().min(0).max(100).nullable(),
58238
+ percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58302
58239
  /** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
58303
58240
  * a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
58304
- percentageStep: number().positive().optional(),
58241
+ percentageStep: number().positive().optional().meta(readingMeta("percentage")),
58305
58242
  /** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
58306
58243
  * — empty string when no preset is active or supported. */
58307
58244
  preset: string(),
@@ -58383,7 +58320,7 @@ var FeatureProbeStatusSchema = object({
58383
58320
  /** Camera/firmware model string. `null` when the firmware doesn't expose it. */
58384
58321
  model: string().nullable(),
58385
58322
  /** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
58386
- channelCount: number().nullable(),
58323
+ channelCount: number().nullable().meta(readingMeta("count")),
58387
58324
  /**
58388
58325
  * Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
58389
58326
  * completes — drivers' `getAccessoryChildren()` should treat zero as
@@ -58417,7 +58354,7 @@ object({
58417
58354
  DeviceType.Sensor;
58418
58355
  object({
58419
58356
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
58420
- position: number().min(0).max(100).nullable(),
58357
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58421
58358
  /** Ms epoch when the slice was last updated (push or poll). */
58422
58359
  lastFetchedAt: number()
58423
58360
  });
@@ -58426,9 +58363,9 @@ object({
58426
58363
  /** Whether the humidifier is currently on. */
58427
58364
  on: boolean(),
58428
58365
  /** Current measured relative humidity (0..100). Null when not reported. */
58429
- currentHumidity: number().min(0).max(100).nullable(),
58366
+ currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
58430
58367
  /** Target relative humidity (0..100). Null when no setpoint surface. */
58431
- targetHumidity: number().min(0).max(100).nullable(),
58368
+ targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
58432
58369
  /** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
58433
58370
  * device has no mode surface. */
58434
58371
  mode: string().nullable(),
@@ -58438,9 +58375,9 @@ object({
58438
58375
  * `idle` / `off`). Null when the device doesn't report it. */
58439
58376
  action: string().nullable(),
58440
58377
  /** HA `min_humidity` attribute. Null → UI uses 0. */
58441
- minHumidity: number().nullable(),
58378
+ minHumidity: number().nullable().meta(readingMeta("humidity")),
58442
58379
  /** HA `max_humidity` attribute. Null → UI uses 100. */
58443
- maxHumidity: number().nullable(),
58380
+ maxHumidity: number().nullable().meta(readingMeta("humidity")),
58444
58381
  /** Ms epoch when the slice was last updated. */
58445
58382
  lastChangedAt: number()
58446
58383
  });
@@ -58464,22 +58401,13 @@ DeviceType.Humidifier, method(object({
58464
58401
  auth: "protected"
58465
58402
  });
58466
58403
  object({
58467
- /** Current relative humidity, 0..100. */
58468
- percent: number().min(0).max(100).meta({
58469
- unit: "%",
58404
+ /** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
58405
+ percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
58406
+ precision: 0,
58470
58407
  label: "Humidity"
58471
- }),
58408
+ })),
58472
58409
  /** Ms epoch when the slice was last updated. */
58473
- lastFetchedAt: number(),
58474
- /** Live display unit from the upstream source (e.g. HA
58475
- * `attributes.unit_of_measurement`). The UI prefers this over the
58476
- * role's canonical unit. Absent → fall back to the canonical unit. */
58477
- unit: string().optional(),
58478
- /** Suggested decimal places for numeric display.
58479
- * Populated live from the upstream source when provided (e.g. HA
58480
- * `attributes.suggested_display_precision`). Falls back to
58481
- * auto-formatting when absent. */
58482
- precision: number().int().min(0).max(10).optional()
58410
+ lastFetchedAt: number()
58483
58411
  });
58484
58412
  DeviceType.Sensor, DeviceType.Thermostat;
58485
58413
  object({
@@ -58487,7 +58415,7 @@ object({
58487
58415
  * entity exposes no `entity_picture` (yet). */
58488
58416
  url: string().nullable(),
58489
58417
  /** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
58490
- lastUpdated: number().nullable()
58418
+ lastUpdated: number().nullable().meta(readingMeta("none"))
58491
58419
  });
58492
58420
  DeviceType.Image;
58493
58421
  /**
@@ -58543,19 +58471,19 @@ var NormalizedRangeSchema = object({
58543
58471
  });
58544
58472
  object({
58545
58473
  /** Normalized 0–100. */
58546
- brightness: number().optional(),
58474
+ brightness: number().optional().meta(readingMeta("none")),
58547
58475
  /** Normalized 0–100. */
58548
- contrast: number().optional(),
58476
+ contrast: number().optional().meta(readingMeta("none")),
58549
58477
  /** Normalized 0–100. */
58550
- saturation: number().optional(),
58478
+ saturation: number().optional().meta(readingMeta("none")),
58551
58479
  /** Normalized 0–100. */
58552
- sharpness: number().optional(),
58480
+ sharpness: number().optional().meta(readingMeta("none")),
58553
58481
  mirror: boolean().optional(),
58554
58482
  flip: boolean().optional(),
58555
58483
  rotate: ImageRotateSchema.optional(),
58556
58484
  whiteBalance: WhiteBalanceModeSchema.optional(),
58557
58485
  /** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
58558
- warmth: number().optional(),
58486
+ warmth: number().optional().meta(readingMeta("none")),
58559
58487
  exposureMode: ExposureModeSchema.optional(),
58560
58488
  backlightMode: BacklightModeSchema.optional(),
58561
58489
  lastFetchedAt: number()
@@ -58800,10 +58728,10 @@ var IntercomAbilitySchema = object({
58800
58728
  /** Codecs accepted by firmware, in preference order. Driver-authored strings. */
58801
58729
  codecs: array(string()),
58802
58730
  /** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
58803
- sampleRate: number(),
58731
+ sampleRate: number().meta(readingMeta("none")),
58804
58732
  duplex: _enum(["half", "full"]),
58805
58733
  /** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
58806
- maxBacklogMs: number()
58734
+ maxBacklogMs: number().meta(readingMeta("duration-ms"))
58807
58735
  });
58808
58736
  var IntercomStatusSchema = object({
58809
58737
  /** True while a talk session is open. */
@@ -58914,13 +58842,13 @@ object({
58914
58842
  activity: LawnMowerActivitySchema,
58915
58843
  /** 0..100 battery percentage. Null when the device has no battery
58916
58844
  * reading. */
58917
- batteryLevel: number().min(0).max(100).nullable(),
58845
+ batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58918
58846
  /** 0..100 mowing-completion percentage of the current task, or null when no
58919
58847
  * task is active / progress is unavailable. */
58920
- progressPercent: number().min(0).max(100).nullable(),
58848
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58921
58849
  /** Current device/event code (dynamic — mostly status, sometimes an error),
58922
58850
  * or null when unknown. */
58923
- currentCode: number().nullable(),
58851
+ currentCode: number().nullable().meta(readingMeta("none")),
58924
58852
  /** Human label for {@link currentCode}, or null when undecodable. */
58925
58853
  currentCodeLabel: string().nullable(),
58926
58854
  /** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
@@ -59382,7 +59310,7 @@ object({
59382
59310
  state: MediaPlayerStateSchema,
59383
59311
  /** Volume as 0..100 inclusive. Null when the device has no volume
59384
59312
  * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
59385
- volumeLevel: number().min(0).max(100).nullable(),
59313
+ volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59386
59314
  /** Mute toggle distinct from volume=0. Null when no mute surface.
59387
59315
  * Pair with `DeviceFeature.MediaPlayerMute`. */
59388
59316
  isMuted: boolean().nullable(),
@@ -59395,10 +59323,10 @@ object({
59395
59323
  currentMedia: MediaInfoSchema.nullable(),
59396
59324
  /** Current playback position in ms. Null when not seekable or
59397
59325
  * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
59398
- positionMs: number().int().nonnegative().nullable(),
59326
+ positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
59399
59327
  /** Total duration of the current media in ms. Null when unknown
59400
59328
  * (live stream) or nothing is playing. */
59401
- durationMs: number().int().nonnegative().nullable(),
59329
+ durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
59402
59330
  /** Shuffle toggle. Null when no shuffle surface.
59403
59331
  * Pair with `DeviceFeature.MediaPlayerShuffle`. */
59404
59332
  shuffle: boolean().nullable(),
@@ -59698,7 +59626,7 @@ object({
59698
59626
  * it, every provider seeded it at boot, and every session left it
59699
59627
  * `detected: true` after its detach.
59700
59628
  */
59701
- autoClearAfterMs: number().nullable()
59629
+ autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
59702
59630
  });
59703
59631
  /**
59704
59632
  * Payload of `motion.onMotionChanged` event + the corresponding bus
@@ -59748,7 +59676,7 @@ var MotionZoneRegionSchema = object({
59748
59676
  });
59749
59677
  object({
59750
59678
  enabled: boolean(),
59751
- sensitivity: number(),
59679
+ sensitivity: number().meta(readingMeta("none")),
59752
59680
  /** Grid region(s). Today exactly one `grid` shape. */
59753
59681
  regions: array(MotionZoneRegionSchema),
59754
59682
  lastFetchedAt: number()
@@ -60052,7 +59980,7 @@ var NavigationStatusSchema = object({
60052
59980
  following: boolean(),
60053
59981
  flash: boolean(),
60054
59982
  lightMode: NavigationLightModeSchema,
60055
- lightLevel: number().min(40).max(100),
59983
+ lightLevel: number().min(40).max(100).meta(readingMeta("none")),
60056
59984
  /** Ms epoch when the slice was last updated. */
60057
59985
  lastChangedAt: number()
60058
59986
  });
@@ -60098,13 +60026,13 @@ var NetworkLinkStatusSchema = object({
60098
60026
  * one whose reading has not landed must not be drawn at 0 %. Consumers
60099
60027
  * SKIP a null rather than coerce it.
60100
60028
  */
60101
- signalPercent: number().min(0).max(100).nullable(),
60029
+ signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60102
60030
  /** Raw received signal strength in dBm, when the firmware reports one. */
60103
- rssiDbm: number().optional(),
60031
+ rssiDbm: number().optional().meta(readingMeta("signal-level")),
60104
60032
  /** Network name of a wireless link, when the firmware reports it. */
60105
60033
  ssid: string().optional(),
60106
60034
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
60107
- lastUpdated: number()
60035
+ lastUpdated: number().meta(readingMeta("none"))
60108
60036
  });
60109
60037
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.Light, DeviceType.Lock, DeviceType.Siren, object({
60110
60038
  deviceId: number(),
@@ -60423,7 +60351,7 @@ object({
60423
60351
  lastError: string().nullable(),
60424
60352
  /** Number of deliveries currently buffered server-side (rate-limited
60425
60353
  * notifiers may queue). 0 when the provider sends synchronously. */
60426
- queueDepth: number().int().nonnegative(),
60354
+ queueDepth: number().int().nonnegative().meta(readingMeta("count")),
60427
60355
  /** Per-feature capability matrix — authoritative source for the
60428
60356
  * compose-form field gating. */
60429
60357
  supports: NotifierSupportsSchema
@@ -60473,15 +60401,8 @@ DeviceType.Notifier, method(NotifierSendInputSchema, NotifierSendResultSchema, {
60473
60401
  acceptedAt: number()
60474
60402
  });
60475
60403
  object({
60476
- value: number(),
60477
- /** Display unit-of-measurement (e.g. 'dBm', 's', 'rpm', 'steps').
60478
- * Populated live from the upstream source on each state push.
60479
- * Absent for unitless sensors. */
60480
- unit: string().optional(),
60481
- /** Suggested decimal places for numeric display.
60482
- * Populated live from the upstream source when provided.
60483
- * Falls back to auto-formatting when absent. */
60484
- precision: number().int().min(0).max(10).optional(),
60404
+ /** Reading in the device quantity's base unit. `null` when refused (D393). */
60405
+ value: number().nullable().meta(readingMeta("device")),
60485
60406
  /** Ms epoch when the slice was last updated. */
60486
60407
  lastFetchedAt: number()
60487
60408
  });
@@ -60833,23 +60754,26 @@ object({
60833
60754
  /** Food currently in the bowl (grams). Null when the device has not
60834
60755
  * reported a reading yet. On dual-hopper models this is the combined
60835
60756
  * bowl reading; per-hopper levels live in `food1`/`food2`. */
60836
- foodLevel: number().nullable(),
60757
+ foodLevel: number().nullable().meta(readingMeta("none")),
60837
60758
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
60838
60759
  * single-hopper models. */
60839
- food1: number().nullable(),
60760
+ food1: number().nullable().meta(readingMeta("none")),
60840
60761
  /** Hopper-2 food level (grams) on dual-hopper feeders; null on
60841
60762
  * single-hopper models. */
60842
- food2: number().nullable(),
60763
+ food2: number().nullable().meta(readingMeta("none")),
60843
60764
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
60844
60765
  * (`device_class: problem`, on = low). True when the bowl is empty /
60845
60766
  * below the feeder's low threshold. */
60846
60767
  lowFood: boolean(),
60847
60768
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
60848
60769
  * device has no battery reading. */
60849
- batteryPower: number().min(0).max(100).nullable(),
60850
- /** Days of desiccant life remaining. Null when the model has no
60851
- * desiccant sensor. */
60852
- desiccantLeftDays: number().nullable(),
60770
+ batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60771
+ /**
60772
+ * Desiccant life remaining, stored in seconds (duration base, D944).
60773
+ * Providers convert from the days the feeder reports. Null when the
60774
+ * model has no desiccant sensor.
60775
+ */
60776
+ desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
60853
60777
  /** True while a feed is in progress. */
60854
60778
  feeding: boolean(),
60855
60779
  /** Decoded connectivity / power status (HA petkit device-status enum).
@@ -60860,7 +60784,7 @@ object({
60860
60784
  * with `errorCode` for consumers that want the raw integer. */
60861
60785
  error: string().nullable(),
60862
60786
  /** Raw device error code (0 / null = no error). */
60863
- errorCode: number().nullable(),
60787
+ errorCode: number().nullable().meta(readingMeta("none")),
60864
60788
  /** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
60865
60789
  isDualHopper: boolean(),
60866
60790
  /** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
@@ -60870,7 +60794,7 @@ object({
60870
60794
  /** Play a chime when dispensing. */
60871
60795
  feedSound: boolean(),
60872
60796
  /** Speaker / prompt volume level (device-scaled integer). */
60873
- volume: number(),
60797
+ volume: number().meta(readingMeta("none")),
60874
60798
  /** Ms epoch when the slice was last refreshed from the cloud. */
60875
60799
  lastFetchedAt: number()
60876
60800
  });
@@ -61307,36 +61231,19 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
61307
61231
  });
61308
61232
  object({
61309
61233
  /** Instantaneous power draw in watts. */
61310
- watts: number().optional().meta({
61311
- unit: "W",
61312
- label: "Power"
61313
- }),
61234
+ watts: number().optional().meta(readingMeta("power", { label: "Power" })),
61314
61235
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
61315
- kwhTotal: number().optional().meta({
61316
- unit: "kWh",
61317
- label: "Energy"
61318
- }),
61236
+ kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
61319
61237
  /** Voltage in volts. */
61320
- volts: number().optional(),
61238
+ volts: number().optional().meta(readingMeta("voltage")),
61321
61239
  /** Current in amperes. */
61322
- amps: number().optional(),
61323
- /** Apparent power in volt-amperes (W × power-factor reciprocal). */
61324
- va: number().optional(),
61240
+ amps: number().optional().meta(readingMeta("current")),
61241
+ /** Apparent power in volt-amperes. */
61242
+ va: number().optional().meta(readingMeta("apparent-power")),
61325
61243
  /** Power factor (0..1) if reported. */
61326
- powerFactor: number().min(0).max(1).optional(),
61244
+ powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
61327
61245
  /** Ms epoch when the slice was last updated. */
61328
- lastFetchedAt: number(),
61329
- /** Live display unit of the single metric this slice carries (e.g. HA
61330
- * `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
61331
- * Each upstream `sensor.*` entity surfaces ONE device_class, so one
61332
- * unit per slice is unambiguous. The UI prefers this over the role's
61333
- * canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
61334
- unit: string().optional(),
61335
- /** Suggested decimal places for numeric display.
61336
- * Populated live from the upstream source when provided (e.g. HA
61337
- * `attributes.suggested_display_precision`). Falls back to
61338
- * auto-formatting when absent. */
61339
- precision: number().int().min(0).max(10).optional()
61246
+ lastFetchedAt: number()
61340
61247
  });
61341
61248
  DeviceType.Sensor;
61342
61249
  /**
@@ -61357,11 +61264,11 @@ DeviceType.Sensor;
61357
61264
  */
61358
61265
  var GpsLocationSchema = object({
61359
61266
  /** Latitude in decimal degrees, -90..90. */
61360
- latitude: number().min(-90).max(90),
61267
+ latitude: number().min(-90).max(90).meta(readingMeta("angle")),
61361
61268
  /** Longitude in decimal degrees, -180..180. */
61362
- longitude: number().min(-180).max(180),
61269
+ longitude: number().min(-180).max(180).meta(readingMeta("angle")),
61363
61270
  /** Reported accuracy in meters (lower = better). */
61364
- accuracyMeters: number().nonnegative()
61271
+ accuracyMeters: number().nonnegative().meta(readingMeta("length"))
61365
61272
  });
61366
61273
  object({
61367
61274
  /** `home` / `not_home` / any user-defined zone name. */
@@ -61373,25 +61280,16 @@ object({
61373
61280
  gps: GpsLocationSchema.nullable(),
61374
61281
  /** Optional battery level of the tracking device (0..100). Null
61375
61282
  * when not reported. */
61376
- batteryPercent: number().min(0).max(100).nullable(),
61283
+ batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61377
61284
  /** Ms epoch when the slice was last updated. */
61378
61285
  lastChangedAt: number()
61379
61286
  });
61380
61287
  DeviceType.Presence;
61381
61288
  object({
61382
- /** Current pressure in hPa. */
61383
- hpa: number(),
61289
+ /** Current pressure in hPa. `null` when the sample was refused (D393). */
61290
+ hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
61384
61291
  /** Ms epoch when the slice was last updated. */
61385
- lastFetchedAt: number(),
61386
- /** Live display unit from the upstream source (e.g. HA
61387
- * `attributes.unit_of_measurement`). The UI prefers this over the
61388
- * role's canonical unit. Absent → fall back to the canonical unit. */
61389
- unit: string().optional(),
61390
- /** Suggested decimal places for numeric display.
61391
- * Populated live from the upstream source when provided (e.g. HA
61392
- * `attributes.suggested_display_precision`). Falls back to
61393
- * auto-formatting when absent. */
61394
- precision: number().int().min(0).max(10).optional()
61292
+ lastFetchedAt: number()
61395
61293
  });
61396
61294
  DeviceType.Sensor;
61397
61295
  /**
@@ -61724,8 +61622,8 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
61724
61622
  }), _void(), { kind: "mutation" });
61725
61623
  var PtzAutotrackSettingsSchema = object({
61726
61624
  targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
61727
- stopDelaySeconds: number().int().min(0).max(300),
61728
- disappearDelaySeconds: number().int().min(0).max(300)
61625
+ stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
61626
+ disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
61729
61627
  });
61730
61628
  /**
61731
61629
  * One option the camera advertises as a valid `targetType`. Drivers
@@ -61782,7 +61680,7 @@ object({
61782
61680
  /** null = unknown — never coerced to false (D393). */
61783
61681
  running: boolean().nullable(),
61784
61682
  /** Epoch ms the current boolean began; null while `running` is null. */
61785
- since: number().nullable(),
61683
+ since: number().nullable().meta(readingMeta("none")),
61786
61684
  source: RunningStateSourceSchema.meta({ label: "Source" }),
61787
61685
  phase: string().nullable(),
61788
61686
  /** null until a reading has been published (D393). */
@@ -62980,9 +62878,9 @@ var RecordTriggerSchema = _enum([
62980
62878
  */
62981
62879
  var RecordWindowSchema = object({
62982
62880
  trigger: RecordTriggerSchema,
62983
- day: number().int().min(0).max(6),
62984
- startMinute: number().int().min(0).max(1439),
62985
- endMinute: number().int().min(1).max(1440)
62881
+ day: number().int().min(0).max(6).meta(readingMeta("none")),
62882
+ startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
62883
+ endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
62986
62884
  });
62987
62885
  /** Status of one physical volume, as the camera itself describes it. */
62988
62886
  var OnboardStorageVolumeSchema = object({
@@ -63051,10 +62949,10 @@ object({
63051
62949
  /** Master "record to the card at all" switch. */
63052
62950
  enabled: boolean().nullable(),
63053
62951
  overwriteWhenFull: boolean().nullable(),
63054
- preRecordSec: number().nullable(),
63055
- postRecordSec: number().nullable(),
62952
+ preRecordSec: number().nullable().meta(readingMeta("duration")),
62953
+ postRecordSec: number().nullable().meta(readingMeta("duration")),
63056
62954
  /** Length of one recorded file, in minutes. */
63057
- segmentMinutes: number().nullable(),
62955
+ segmentMinutes: number().nullable().meta(readingMeta("none")),
63058
62956
  /** The primary track's weekly windows, flattened. */
63059
62957
  windows: array(RecordWindowSchema),
63060
62958
  /**
@@ -63070,7 +62968,7 @@ object({
63070
62968
  * provider reporting a non-zero count also reports the schedule as not
63071
62969
  * writable.
63072
62970
  */
63073
- unreadableWindows: number(),
62971
+ unreadableWindows: number().meta(readingMeta("count")),
63074
62972
  /**
63075
62973
  * The camera is scheduled to record and has NO usable storage.
63076
62974
  *
@@ -63707,7 +63605,7 @@ var CamStreamDescriptorSchema = object({
63707
63605
  url: string().optional(),
63708
63606
  codec: string().optional(),
63709
63607
  resolution: CamStreamResolutionSchema.optional(),
63710
- fps: number().positive().optional(),
63608
+ fps: number().positive().optional().meta(readingMeta("rate")),
63711
63609
  label: string().optional(),
63712
63610
  /** Device-level features (e.g. `battery-operated`) — drives broker policy. */
63713
63611
  deviceFeatures: array(string()).optional(),
@@ -63737,18 +63635,18 @@ var StreamProfileSchema = _enum([
63737
63635
  ]);
63738
63636
  /** The current encoding config of one stream profile. */
63739
63637
  var StreamProfileConfigSchema = object({
63740
- width: number(),
63741
- height: number(),
63638
+ width: number().meta(readingMeta("none")),
63639
+ height: number().meta(readingMeta("none")),
63742
63640
  codec: _enum(["h264", "h265"]),
63743
- framerate: number(),
63744
- bitrate: number(),
63641
+ framerate: number().meta(readingMeta("rate")),
63642
+ bitrate: number().meta(readingMeta("bitrate")),
63745
63643
  bitrateMode: _enum(["vbr", "cbr"]).optional(),
63746
63644
  encoderProfile: _enum([
63747
63645
  "high",
63748
63646
  "main",
63749
63647
  "baseline"
63750
63648
  ]).optional(),
63751
- gop: number().optional(),
63649
+ gop: number().optional().meta(readingMeta("none")),
63752
63650
  /**
63753
63651
  * Whether THIS profile currently carries an audio track. READ-ONLY here.
63754
63652
  *
@@ -64391,23 +64289,13 @@ object({
64391
64289
  });
64392
64290
  DeviceType.Sensor;
64393
64291
  object({
64394
- /** Current temperature in Celsius. */
64395
- celsius: number().meta({
64396
- unit: "°C",
64292
+ /** Current temperature in Celsius. `null` when the sample was refused (D393). */
64293
+ celsius: number().nullable().meta(readingMeta("temperature", {
64294
+ precision: 1,
64397
64295
  label: "Temperature"
64398
- }),
64296
+ })),
64399
64297
  /** Ms epoch when the slice was last updated (push or poll). */
64400
- lastFetchedAt: number(),
64401
- /** Live display unit from the upstream source (e.g. HA
64402
- * `attributes.unit_of_measurement`). The UI prefers this over the
64403
- * role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
64404
- * Absent → the UI falls back to the role's canonical unit. */
64405
- unit: string().optional(),
64406
- /** Suggested decimal places for numeric display.
64407
- * Populated live from the upstream source when provided (e.g. HA
64408
- * `attributes.suggested_display_precision`). Falls back to
64409
- * auto-formatting when absent. */
64410
- precision: number().int().min(0).max(10).optional()
64298
+ lastFetchedAt: number()
64411
64299
  });
64412
64300
  DeviceType.Sensor, DeviceType.Thermostat;
64413
64301
  /**
@@ -64659,16 +64547,16 @@ object({
64659
64547
  ]),
64660
64548
  /** 0..100 battery percentage. Null when the device has no battery
64661
64549
  * reading. */
64662
- batteryLevel: number().min(0).max(100).nullable(),
64550
+ batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64663
64551
  /** Current fan-speed token (provider-verbatim). Null when unknown or
64664
64552
  * the vacuum has no speed control. */
64665
64553
  fanSpeed: string().nullable(),
64666
64554
  /** Speed tokens the hardware accepts — drives the UI selector. */
64667
64555
  availableFanSpeeds: array(string()),
64668
64556
  /** 0..100 cleaning-completion percentage of the current task, or null. */
64669
- progressPercent: number().min(0).max(100).nullable(),
64557
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64670
64558
  /** Current error code (0 / null = no error). */
64671
- errorCode: number().nullable(),
64559
+ errorCode: number().nullable().meta(readingMeta("none")),
64672
64560
  /** Human label for {@link errorCode}, or null when none / undecodable. */
64673
64561
  errorLabel: string().nullable(),
64674
64562
  /** Ms epoch when the slice was last updated. */
@@ -64707,7 +64595,7 @@ object({
64707
64595
  ]),
64708
64596
  /** 0 = fully closed, 100 = fully open. Null when the device has no
64709
64597
  * intermediate position surface. */
64710
- position: number().min(0).max(100).nullable(),
64598
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64711
64599
  /** Ms epoch when the slice was last updated. */
64712
64600
  lastChangedAt: number()
64713
64601
  });
@@ -64735,9 +64623,9 @@ object({
64735
64623
  DeviceType.Sensor;
64736
64624
  object({
64737
64625
  /** Current measured temperature. Null when not reported. */
64738
- currentTemp: number().nullable(),
64626
+ currentTemp: number().nullable().meta(readingMeta("temperature")),
64739
64627
  /** Target temperature setpoint. Null when no setpoint surface. */
64740
- targetTemp: number().nullable(),
64628
+ targetTemp: number().nullable().meta(readingMeta("temperature")),
64741
64629
  /** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
64742
64630
  * `heat_pump` / `high_demand` / `performance` / `off`). Null when the
64743
64631
  * device reports an unknown state. */
@@ -64748,9 +64636,9 @@ object({
64748
64636
  * has no away surface. */
64749
64637
  away: boolean().nullable(),
64750
64638
  /** HA `min_temp` attribute. Null when not reported. */
64751
- minTemp: number().nullable(),
64639
+ minTemp: number().nullable().meta(readingMeta("temperature")),
64752
64640
  /** HA `max_temp` attribute. Null when not reported. */
64753
- maxTemp: number().nullable(),
64641
+ maxTemp: number().nullable().meta(readingMeta("temperature")),
64754
64642
  /** Ms epoch when the slice was last updated. */
64755
64643
  lastChangedAt: number()
64756
64644
  });
@@ -64774,25 +64662,18 @@ DeviceType.WaterHeater, method(object({
64774
64662
  auth: "protected"
64775
64663
  });
64776
64664
  object({
64777
- /** Verbatim HA condition state (`sunny`, `cloudy`, `rainy`, …). Null
64778
- * when no condition has been reported yet. */
64665
+ /** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
64779
64666
  condition: string().nullable(),
64780
- /** Current temperature in the reported unit. Null when not provided. */
64781
- temperature: number().nullable(),
64782
- /** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
64783
- temperatureUnit: string().nullable(),
64667
+ /** Current temperature in Celsius. Null when not provided or refused. */
64668
+ temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
64784
64669
  /** Relative humidity (0..100). Null when not provided. */
64785
- humidity: number().min(0).max(100).nullable(),
64786
- /** Barometric pressure in the reported unit. Null when not provided. */
64787
- pressure: number().nullable(),
64788
- /** Pressure unit string (e.g. `hPa` / `inHg`). Null when not provided. */
64789
- pressureUnit: string().nullable(),
64790
- /** Wind speed in the reported unit. Null when not provided. */
64791
- windSpeed: number().nullable(),
64792
- /** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
64793
- windSpeedUnit: string().nullable(),
64670
+ humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
64671
+ /** Barometric pressure in hPa. Null when not provided or refused. */
64672
+ pressure: number().nullable().meta(readingMeta("pressure")),
64673
+ /** Wind speed in metres per second. Null when not provided or refused. */
64674
+ windSpeed: number().nullable().meta(readingMeta("speed")),
64794
64675
  /** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
64795
- windBearing: number().nullable(),
64676
+ windBearing: number().nullable().meta(readingMeta("angle")),
64796
64677
  /** Ms epoch when the slice was last updated. */
64797
64678
  lastFetchedAt: number()
64798
64679
  });
@@ -65669,6 +65550,11 @@ var UNIT_TABLE = {
65669
65550
  factor: 1609.344,
65670
65551
  offset: 0
65671
65552
  },
65553
+ "m²": {
65554
+ dimension: "area",
65555
+ factor: 1,
65556
+ offset: 0
65557
+ },
65672
65558
  lx: {
65673
65559
  dimension: "illuminance",
65674
65560
  factor: 1,
@@ -65773,6 +65659,121 @@ var UNIT_TABLE = {
65773
65659
  dimension: "percentage",
65774
65660
  factor: 1,
65775
65661
  offset: 0
65662
+ },
65663
+ L: {
65664
+ dimension: "volume",
65665
+ factor: 1,
65666
+ offset: 0
65667
+ },
65668
+ mL: {
65669
+ dimension: "volume",
65670
+ factor: .001,
65671
+ offset: 0
65672
+ },
65673
+ gal: {
65674
+ dimension: "volume",
65675
+ factor: 3.785411784,
65676
+ offset: 0
65677
+ },
65678
+ s: {
65679
+ dimension: "duration",
65680
+ factor: 1,
65681
+ offset: 0
65682
+ },
65683
+ ms: {
65684
+ dimension: "duration",
65685
+ factor: .001,
65686
+ offset: 0
65687
+ },
65688
+ min: {
65689
+ dimension: "duration",
65690
+ factor: 60,
65691
+ offset: 0
65692
+ },
65693
+ h: {
65694
+ dimension: "duration",
65695
+ factor: 3600,
65696
+ offset: 0
65697
+ },
65698
+ d: {
65699
+ dimension: "duration",
65700
+ factor: 86400,
65701
+ offset: 0
65702
+ },
65703
+ B: {
65704
+ dimension: "information",
65705
+ factor: 1,
65706
+ offset: 0
65707
+ },
65708
+ KiB: {
65709
+ dimension: "information",
65710
+ factor: 1024,
65711
+ offset: 0
65712
+ },
65713
+ MiB: {
65714
+ dimension: "information",
65715
+ factor: 1024 ** 2,
65716
+ offset: 0
65717
+ },
65718
+ GiB: {
65719
+ dimension: "information",
65720
+ factor: 1024 ** 3,
65721
+ offset: 0
65722
+ },
65723
+ TiB: {
65724
+ dimension: "information",
65725
+ factor: 1024 ** 4,
65726
+ offset: 0
65727
+ },
65728
+ kB: {
65729
+ dimension: "information",
65730
+ factor: 1e3,
65731
+ offset: 0
65732
+ },
65733
+ MB: {
65734
+ dimension: "information",
65735
+ factor: 1e6,
65736
+ offset: 0
65737
+ },
65738
+ GB: {
65739
+ dimension: "information",
65740
+ factor: 1e9,
65741
+ offset: 0
65742
+ },
65743
+ "°": {
65744
+ dimension: "angle",
65745
+ factor: 1,
65746
+ offset: 0
65747
+ },
65748
+ dBFS: {
65749
+ dimension: "sound-level",
65750
+ factor: 1,
65751
+ offset: 0
65752
+ },
65753
+ dBm: {
65754
+ dimension: "signal-level",
65755
+ factor: 1,
65756
+ offset: 0
65757
+ },
65758
+ mired: {
65759
+ dimension: "colour-temperature",
65760
+ factor: 1,
65761
+ offset: 0
65762
+ },
65763
+ kbps: {
65764
+ dimension: "bitrate",
65765
+ factor: 1,
65766
+ offset: 0
65767
+ },
65768
+ Mbps: {
65769
+ dimension: "bitrate",
65770
+ factor: 1e3,
65771
+ offset: 0
65772
+ },
65773
+ fps: {
65774
+ dimension: "rate",
65775
+ factor: 1,
65776
+ offset: 0
65776
65777
  }
65777
65778
  };
65778
65779
  /** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
@@ -78983,7 +78984,6 @@ var SENSOR_CAP_VOCABULARY = {
78983
78984
  field: "value",
78984
78985
  quantity: t("the reading", "il valore"),
78985
78986
  label: t("Reading", "Valore"),
78986
- unitField: "unit",
78987
78987
  icon: "gauge"
78988
78988
  }]
78989
78989
  },
@@ -79119,7 +79119,7 @@ var SENSOR_CAP_VOCABULARY = {
79119
79119
  field: "temperature",
79120
79120
  quantity: t("the outdoor temperature", "la temperatura esterna"),
79121
79121
  label: t("Temperature", "Temperatura"),
79122
- unitField: "temperatureUnit",
79122
+ unit: "°C",
79123
79123
  icon: "thermometer"
79124
79124
  },
79125
79125
  {
@@ -79135,7 +79135,7 @@ var SENSOR_CAP_VOCABULARY = {
79135
79135
  field: "windSpeed",
79136
79136
  quantity: t("the wind speed", "la velocità del vento"),
79137
79137
  label: t("Wind", "Vento"),
79138
- unitField: "windSpeedUnit",
79138
+ unit: "m/s",
79139
79139
  icon: "wind"
79140
79140
  },
79141
79141
  {
@@ -79143,7 +79143,7 @@ var SENSOR_CAP_VOCABULARY = {
79143
79143
  field: "pressure",
79144
79144
  quantity: t("the air pressure", "la pressione atmosferica"),
79145
79145
  label: t("Pressure", "Pressione"),
79146
- unitField: "pressureUnit",
79146
+ unit: "hPa",
79147
79147
  icon: "gauge"
79148
79148
  }
79149
79149
  ]
@@ -82280,6 +82280,11 @@ var PulseOverrideDraftSchema = object({
82280
82280
  targetId: string().min(1),
82281
82281
  enabled: boolean(),
82282
82282
  debounceMs: number().nonnegative(),
82283
+ edges: array(_enum([
82284
+ "rising",
82285
+ "button",
82286
+ "native-event"
82287
+ ])).min(1).optional(),
82283
82288
  sources: array(OverrideSourceSchema).min(1)
82284
82289
  });
82285
82290
  var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([