@camstack/addon-model-studio 1.1.296 → 1.1.297

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-DOhuE4t6.mjs → MotionZonesSettings-D_HfgGVx.mjs} +21 -21
  2. package/dist/{PrivacyMaskSettings-BtQIysUm.mjs → PrivacyMaskSettings-lrZujhrs.mjs} +4 -4
  3. package/dist/{SceneMonitorEditor-B4cEP3UH.mjs → SceneMonitorEditor-D_k9w6y8.mjs} +71 -71
  4. package/dist/_stub.js +4254 -4181
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-DoLsYz9K.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-U1Dq0t4d.mjs} +3 -3
  6. package/dist/_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DX4Jz8LK.mjs +26 -0
  7. package/dist/{hostInit-BfsN6Dh3.mjs → hostInit-lgxajgO5.mjs} +2 -2
  8. package/dist/model-studio.addon.js +273 -422
  9. package/dist/model-studio.addon.mjs +273 -422
  10. package/dist/{player-overlays-BVHDRE8f.mjs → player-overlays-BErbmrZZ.mjs} +1 -1
  11. package/dist/remoteEntry.js +1 -1
  12. package/dist/{responsive-CI-NtcQi.mjs → responsive-BaiZrRiv.mjs} +13 -10
  13. package/dist/{scene-monitor-copy-CvzuJJ8Z.mjs → scene-monitor-copy-DSS-RbWQ.mjs} +1 -1
  14. package/dist/{square-CsljmL2M.mjs → square-CjAc_uJa.mjs} +1 -1
  15. package/dist/{trash-2-DwX9K1LA.mjs → trash-2--He_-Iao.mjs} +1 -1
  16. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-CoN9WY8L.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-ZIEWPFwu.mjs} +1 -1
  17. package/package.json +1 -1
  18. package/dist/_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CDZyIXC0.mjs +0 -26
@@ -5803,7 +5803,7 @@ var ZodIssueCode = {
5803
5803
  var ZodFirstPartyTypeKind;
5804
5804
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5805
5805
  //#endregion
5806
- //#region ../types/dist/sleep-BErAxhul.mjs
5806
+ //#region ../types/dist/sleep-CARd6wZF.mjs
5807
5807
  /**
5808
5808
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5809
5809
  * window to float samples (D455).
@@ -8290,6 +8290,52 @@ var RecordKindDeclarationSchema = object({
8290
8290
  }).strict();
8291
8291
  /** The kind names: lower-case, dash separated, short enough for an index name. */
8292
8292
  var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
8293
+ var READING_QUANTITIES = [
8294
+ "temperature",
8295
+ "pressure",
8296
+ "speed",
8297
+ "precipitation",
8298
+ "precipitation-rate",
8299
+ "length",
8300
+ "area",
8301
+ "illuminance",
8302
+ "irradiance",
8303
+ "power",
8304
+ "energy",
8305
+ "voltage",
8306
+ "current",
8307
+ "apparent-power",
8308
+ "humidity",
8309
+ "percentage",
8310
+ "concentration-volume",
8311
+ "concentration-mass",
8312
+ "concentration-ppb",
8313
+ "volume",
8314
+ "duration",
8315
+ "duration-ms",
8316
+ "information",
8317
+ "sound-level",
8318
+ "signal-level",
8319
+ "colour-temperature",
8320
+ "angle",
8321
+ "bitrate",
8322
+ "rate",
8323
+ "mass",
8324
+ "index",
8325
+ "ratio",
8326
+ "count",
8327
+ "none",
8328
+ "device"
8329
+ ];
8330
+ var ReadingQuantitySchema = _enum(READING_QUANTITIES);
8331
+ new Set(READING_QUANTITIES);
8332
+ function readingMeta(quantity, extras) {
8333
+ return {
8334
+ quantity,
8335
+ ...extras?.precision !== void 0 ? { precision: extras.precision } : {},
8336
+ ...extras?.label !== void 0 ? { label: extras.label } : {}
8337
+ };
8338
+ }
8293
8339
  /** Shared Zod schemas used across streaming capabilities. */
8294
8340
  var CamProfileSchema = _enum([
8295
8341
  "high",
@@ -8317,8 +8363,8 @@ var CamStreamKindSchema = _enum([
8317
8363
  "derived"
8318
8364
  ]);
8319
8365
  var CamStreamResolutionSchema = object({
8320
- width: number().int().positive(),
8321
- height: number().int().positive()
8366
+ width: number().int().positive().meta(readingMeta("none")),
8367
+ height: number().int().positive().meta(readingMeta("none"))
8322
8368
  });
8323
8369
  var CameraStreamSchema = object({
8324
8370
  /** Stable, provider-assigned id unique within the (deviceId) scope. */
@@ -9042,14 +9088,6 @@ Object.freeze({
9042
9088
  {
9043
9089
  "path": "aqi",
9044
9090
  "valueType": "number"
9045
- },
9046
- {
9047
- "path": "unit",
9048
- "valueType": "string"
9049
- },
9050
- {
9051
- "path": "precision",
9052
- "valueType": "number"
9053
9091
  }
9054
9092
  ],
9055
9093
  "alarm-panel": [
@@ -9085,20 +9123,10 @@ Object.freeze({
9085
9123
  "valueType": "boolean"
9086
9124
  }
9087
9125
  ],
9088
- "ambient-light-sensor": [
9089
- {
9090
- "path": "lux",
9091
- "valueType": "number"
9092
- },
9093
- {
9094
- "path": "unit",
9095
- "valueType": "string"
9096
- },
9097
- {
9098
- "path": "precision",
9099
- "valueType": "number"
9100
- }
9101
- ],
9126
+ "ambient-light-sensor": [{
9127
+ "path": "lux",
9128
+ "valueType": "number"
9129
+ }],
9102
9130
  "audio-metrics": [
9103
9131
  {
9104
9132
  "path": "ts",
@@ -9178,6 +9206,10 @@ Object.freeze({
9178
9206
  "path": "lastUpdated",
9179
9207
  "valueType": "number"
9180
9208
  },
9209
+ {
9210
+ "path": "volts",
9211
+ "valueType": "number"
9212
+ },
9181
9213
  {
9182
9214
  "path": "lastContactAt",
9183
9215
  "valueType": "number"
@@ -9420,10 +9452,6 @@ Object.freeze({
9420
9452
  "datetime"
9421
9453
  ]
9422
9454
  },
9423
- {
9424
- "path": "unit",
9425
- "valueType": "string"
9426
- },
9427
9455
  {
9428
9456
  "path": "min",
9429
9457
  "valueType": "number"
@@ -9435,19 +9463,6 @@ Object.freeze({
9435
9463
  {
9436
9464
  "path": "step",
9437
9465
  "valueType": "number"
9438
- },
9439
- {
9440
- "path": "precision",
9441
- "valueType": "number"
9442
- },
9443
- {
9444
- "path": "format",
9445
- "valueType": "enum",
9446
- "enumValues": [
9447
- "date",
9448
- "time",
9449
- "datetime"
9450
- ]
9451
9466
  }
9452
9467
  ],
9453
9468
  "cover": [
@@ -9688,13 +9703,8 @@ Object.freeze({
9688
9703
  "path": "value",
9689
9704
  "valueType": "string"
9690
9705
  }, {
9691
- "path": "format",
9692
- "valueType": "enum",
9693
- "enumValues": [
9694
- "date",
9695
- "time",
9696
- "datetime"
9697
- ]
9706
+ "path": "timestamp",
9707
+ "valueType": "boolean"
9698
9708
  }],
9699
9709
  "event-emitter": [
9700
9710
  {
@@ -9801,20 +9811,10 @@ Object.freeze({
9801
9811
  "valueType": "number"
9802
9812
  }
9803
9813
  ],
9804
- "humidity-sensor": [
9805
- {
9806
- "path": "percent",
9807
- "valueType": "number"
9808
- },
9809
- {
9810
- "path": "unit",
9811
- "valueType": "string"
9812
- },
9813
- {
9814
- "path": "precision",
9815
- "valueType": "number"
9816
- }
9817
- ],
9814
+ "humidity-sensor": [{
9815
+ "path": "percent",
9816
+ "valueType": "number"
9817
+ }],
9818
9818
  "image": [{
9819
9819
  "path": "url",
9820
9820
  "valueType": "string"
@@ -10153,20 +10153,10 @@ Object.freeze({
10153
10153
  "valueType": "boolean"
10154
10154
  }
10155
10155
  ],
10156
- "numeric-sensor": [
10157
- {
10158
- "path": "value",
10159
- "valueType": "number"
10160
- },
10161
- {
10162
- "path": "unit",
10163
- "valueType": "string"
10164
- },
10165
- {
10166
- "path": "precision",
10167
- "valueType": "number"
10168
- }
10169
- ],
10156
+ "numeric-sensor": [{
10157
+ "path": "value",
10158
+ "valueType": "number"
10159
+ }],
10170
10160
  "pet-feeder": [
10171
10161
  {
10172
10162
  "path": "foodLevel",
@@ -10258,14 +10248,6 @@ Object.freeze({
10258
10248
  {
10259
10249
  "path": "powerFactor",
10260
10250
  "valueType": "number"
10261
- },
10262
- {
10263
- "path": "unit",
10264
- "valueType": "string"
10265
- },
10266
- {
10267
- "path": "precision",
10268
- "valueType": "number"
10269
10251
  }
10270
10252
  ],
10271
10253
  "presence": [
@@ -10294,20 +10276,10 @@ Object.freeze({
10294
10276
  "valueType": "number"
10295
10277
  }
10296
10278
  ],
10297
- "pressure-sensor": [
10298
- {
10299
- "path": "hpa",
10300
- "valueType": "number"
10301
- },
10302
- {
10303
- "path": "unit",
10304
- "valueType": "string"
10305
- },
10306
- {
10307
- "path": "precision",
10308
- "valueType": "number"
10309
- }
10310
- ],
10279
+ "pressure-sensor": [{
10280
+ "path": "hpa",
10281
+ "valueType": "number"
10282
+ }],
10311
10283
  "privacy-mask": [{
10312
10284
  "path": "enabled",
10313
10285
  "valueType": "boolean"
@@ -10618,20 +10590,10 @@ Object.freeze({
10618
10590
  "path": "tampered",
10619
10591
  "valueType": "boolean"
10620
10592
  }],
10621
- "temperature-sensor": [
10622
- {
10623
- "path": "celsius",
10624
- "valueType": "number"
10625
- },
10626
- {
10627
- "path": "unit",
10628
- "valueType": "string"
10629
- },
10630
- {
10631
- "path": "precision",
10632
- "valueType": "number"
10633
- }
10634
- ],
10593
+ "temperature-sensor": [{
10594
+ "path": "celsius",
10595
+ "valueType": "number"
10596
+ }],
10635
10597
  "update": [
10636
10598
  {
10637
10599
  "path": "currentVersion",
@@ -10742,10 +10704,6 @@ Object.freeze({
10742
10704
  "path": "temperature",
10743
10705
  "valueType": "number"
10744
10706
  },
10745
- {
10746
- "path": "temperatureUnit",
10747
- "valueType": "string"
10748
- },
10749
10707
  {
10750
10708
  "path": "humidity",
10751
10709
  "valueType": "number"
@@ -10754,18 +10712,10 @@ Object.freeze({
10754
10712
  "path": "pressure",
10755
10713
  "valueType": "number"
10756
10714
  },
10757
- {
10758
- "path": "pressureUnit",
10759
- "valueType": "string"
10760
- },
10761
10715
  {
10762
10716
  "path": "windSpeed",
10763
10717
  "valueType": "number"
10764
10718
  },
10765
- {
10766
- "path": "windSpeedUnit",
10767
- "valueType": "string"
10768
- },
10769
10719
  {
10770
10720
  "path": "windBearing",
10771
10721
  "valueType": "number"
@@ -18269,7 +18219,8 @@ var CompositionProblemSchema = object({
18269
18219
  "item-field-reserved",
18270
18220
  "target-missing",
18271
18221
  "target-unreadable",
18272
- "not-replaceable"
18222
+ "not-replaceable",
18223
+ "native-capability-replace"
18273
18224
  ]),
18274
18225
  path: string(),
18275
18226
  message: string()
@@ -18327,9 +18278,11 @@ var CompositionFieldPlanSchema = object({
18327
18278
  reads: array(CompositionFieldReadSchema)
18328
18279
  });
18329
18280
  /**
18330
- * What a feature does to its target (D663): `new` builds a capability on a new
18331
- * device; on an existing device it `add`s a capability the device lacks, or
18332
- * `replace`s named fields of one it has natively.
18281
+ * What a feature does to its target (D663, D944): `new` builds a capability on a
18282
+ * new device; on an existing device it `add`s a capability the device lacks.
18283
+ * Replacing a capability the device already serves is refused
18284
+ * (`native-capability-replace`), not planned. `replace` remains on the claim
18285
+ * wire for a claim the hub already holds.
18333
18286
  */
18334
18287
  var CompositionFeatureModeSchema = _enum([
18335
18288
  "new",
@@ -20243,12 +20196,12 @@ method(object({ deviceId: number().int().nonnegative() }), DeviceAdminLinkSchema
20243
20196
  * providers detect renames by matching `uniqueId` and patch `id`
20244
20197
  * in-place so dispatch keeps working.
20245
20198
  *
20246
- * **Rendering metadata is NOT stored here.** Display fields (`unit`,
20247
- * `precision`) live in the cap STATUS SLICE so they flow live with every
20248
- * state push and are the single source of truth. The `device_class` tag is
20249
- * an HA-specific input that is consumed at the discovery boundary
20250
- * (`ha-domain-mapping`) to derive the CamStack `DeviceRole`; it is never
20251
- * stored after that point.
20199
+ * **Display unit and precision are NOT stored here.** They are a read-time
20200
+ * preference (D944). A polymorphic reading stores its `quantity` here once,
20201
+ * at adopt, and a date/time value stores `dateTimeShape` the same way —
20202
+ * neither is repeated on each sample. The `device_class` tag is an
20203
+ * HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
20204
+ * to derive the CamStack `DeviceRole`; it is never stored after that point.
20252
20205
  *
20253
20206
  * The `raw` escape hatch carries provider-specific attributes that don't
20254
20207
  * fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
@@ -20262,6 +20215,14 @@ var SourceInfoSchema = object({
20262
20215
  /** Immutable upstream identifier when available (HA `unique_id`, … ).
20263
20216
  * Used to detect rename when `id` changes. */
20264
20217
  uniqueId: string().optional(),
20218
+ /** Quantity of a polymorphic reading, written once at adopt (D944). */
20219
+ quantity: ReadingQuantitySchema.optional(),
20220
+ /** Date / time / datetime shape of a string reading, written once at adopt. */
20221
+ dateTimeShape: _enum([
20222
+ "date",
20223
+ "time",
20224
+ "datetime"
20225
+ ]).optional(),
20265
20226
  /** Provider-specific extras that don't fit the structured slots.
20266
20227
  * Whitelist what goes here per provider — do NOT dump entire HA
20267
20228
  * attribute blobs (would explode the meta JSON column). */
@@ -41174,7 +41135,8 @@ method(object({
41174
41135
  "enum"
41175
41136
  ]),
41176
41137
  enumValues: array(string()).optional(),
41177
- item: boolean().optional()
41138
+ item: boolean().optional(),
41139
+ quantity: ReadingQuantitySchema.optional()
41178
41140
  })).readonly(),
41179
41141
  itemArray: object({
41180
41142
  path: string(),
@@ -44980,8 +44942,8 @@ var MaskPolygonShapeSchema = object({
44980
44942
  /** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
44981
44943
  var MaskGridShapeSchema = object({
44982
44944
  kind: literal("grid"),
44983
- gridWidth: number(),
44984
- gridHeight: number(),
44945
+ gridWidth: number().meta(readingMeta("none")),
44946
+ gridHeight: number().meta(readingMeta("none")),
44985
44947
  cells: array(boolean())
44986
44948
  });
44987
44949
  discriminatedUnion("kind", [
@@ -56375,46 +56337,27 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
56375
56337
  }), LogStreamEntrySchema, { kind: "subscription" });
56376
56338
  object({
56377
56339
  /** Carbon dioxide concentration in ppm. */
56378
- co2Ppm: number().min(0).optional(),
56340
+ co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
56379
56341
  /** Total volatile organic compounds in ppb. */
56380
- vocPpb: number().min(0).optional(),
56342
+ vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
56381
56343
  /** Particulate matter ≤ 2.5 μm in µg/m³. */
56382
- pm25: number().min(0).optional(),
56344
+ pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
56383
56345
  /** Particulate matter ≤ 10 μm in µg/m³. */
56384
- pm10: number().min(0).optional(),
56346
+ pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
56385
56347
  /** Composite AQI value (typically 0..500). */
56386
- aqi: number().optional(),
56348
+ aqi: number().optional().meta(readingMeta("index")),
56387
56349
  /** Ms epoch when the slice was last updated. */
56388
- lastFetchedAt: number(),
56389
- /** Live display unit of the single metric this slice carries (e.g. HA
56390
- * `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
56391
- * upstream `sensor.*` entity surfaces ONE device_class, so one unit
56392
- * per slice is unambiguous. */
56393
- unit: string().optional(),
56394
- /** Suggested decimal places for numeric display.
56395
- * Populated live from the upstream source when provided (e.g. HA
56396
- * `attributes.suggested_display_precision`). Falls back to
56397
- * auto-formatting when absent. */
56398
- precision: number().int().min(0).max(10).optional()
56350
+ lastFetchedAt: number()
56399
56351
  });
56400
56352
  DeviceType.Sensor;
56401
56353
  object({
56402
- /** Current illuminance in lux (lx). */
56403
- lux: number().min(0).meta({
56404
- unit: "lx",
56354
+ /** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
56355
+ lux: number().min(0).nullable().meta(readingMeta("illuminance", {
56356
+ precision: 0,
56405
56357
  label: "Illuminance"
56406
- }),
56358
+ })),
56407
56359
  /** Ms epoch when the slice was last updated. */
56408
- lastFetchedAt: number(),
56409
- /** Live display unit from the upstream source (e.g. HA
56410
- * `attributes.unit_of_measurement`). The UI prefers this over the
56411
- * role's canonical unit. Absent → fall back to the canonical unit. */
56412
- unit: string().optional(),
56413
- /** Suggested decimal places for numeric display.
56414
- * Populated live from the upstream source when provided (e.g. HA
56415
- * `attributes.suggested_display_precision`). Falls back to
56416
- * auto-formatting when absent. */
56417
- precision: number().int().min(0).max(10).optional()
56360
+ lastFetchedAt: number()
56418
56361
  });
56419
56362
  DeviceType.Sensor;
56420
56363
  /**
@@ -56559,10 +56502,7 @@ var BatteryStatusSchema = object({
56559
56502
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
56560
56503
  * declines to band a non-finite reading, which is the correct shape.
56561
56504
  */
56562
- percentage: number().min(0).max(100).nullable().meta({
56563
- unit: "%",
56564
- label: "Charge"
56565
- }),
56505
+ percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
56566
56506
  /**
56567
56507
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
56568
56508
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -56589,7 +56529,14 @@ var BatteryStatusSchema = object({
56589
56529
  */
56590
56530
  sleeping: boolean().meta({ label: "Sleeping" }),
56591
56531
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
56592
- lastUpdated: number(),
56532
+ lastUpdated: number().meta(readingMeta("none")),
56533
+ /**
56534
+ * Battery voltage in volts, for a cell the firmware reports as voltage
56535
+ * rather than a percentage (Ecowitt capacitor packs). Absent on cameras
56536
+ * that only report charge. A second reading on this cap, not a second
56537
+ * `numeric-sensor` on the same device.
56538
+ */
56539
+ volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
56593
56540
  /**
56594
56541
  * Ms epoch of the last time the device PROVED it was reachable — a
56595
56542
  * completed firmware round-trip, an observed wake, or an inbound push
@@ -56649,7 +56596,7 @@ object({
56649
56596
  DeviceType.Sensor;
56650
56597
  object({
56651
56598
  /** Current level as 0..100 inclusive. Firmware-reported. */
56652
- percentage: number().min(0).max(100),
56599
+ percentage: number().min(0).max(100).meta(readingMeta("percentage")),
56653
56600
  /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
56654
56601
  lastChangedAt: number()
56655
56602
  });
@@ -57034,21 +56981,21 @@ object({
57034
56981
  /** Single setpoint in Celsius. Used by single-target modes
57035
56982
  * (heat / cool / dry). Null when the device is in a range mode
57036
56983
  * (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
57037
- target: number().nullable(),
56984
+ target: number().nullable().meta(readingMeta("temperature")),
57038
56985
  /** Upper bound of the dual setpoint in Celsius. Populated only
57039
56986
  * in `heat_cool` mode. */
57040
- targetHigh: number().nullable(),
56987
+ targetHigh: number().nullable().meta(readingMeta("temperature")),
57041
56988
  /** Lower bound of the dual setpoint in Celsius. Populated only
57042
56989
  * in `heat_cool` mode. */
57043
- targetLow: number().nullable(),
56990
+ targetLow: number().nullable().meta(readingMeta("temperature")),
57044
56991
  /** Target relative humidity (0..100). Null when the device has no
57045
56992
  * humidity control. */
57046
- targetHumidity: number().min(0).max(100).nullable(),
56993
+ targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
57047
56994
  /** Current measured temperature in Celsius. Read-only — pushed by
57048
56995
  * the device's internal sensor. */
57049
- currentTemp: number().nullable(),
56996
+ currentTemp: number().nullable().meta(readingMeta("temperature")),
57050
56997
  /** Current measured relative humidity (0..100). Read-only. */
57051
- currentHumidity: number().min(0).max(100).nullable(),
56998
+ currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
57052
56999
  /** Vertical louver swing on/off. Null when the device has no vertical
57053
57000
  * swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
57054
57001
  swingVertical: boolean().nullable(),
@@ -57056,11 +57003,11 @@ object({
57056
57003
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
57057
57004
  swingHorizontal: boolean().nullable(),
57058
57005
  /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
57059
- minTemp: number().nullable().optional(),
57006
+ minTemp: number().nullable().optional().meta(readingMeta("temperature")),
57060
57007
  /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
57061
- maxTemp: number().nullable().optional(),
57008
+ maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
57062
57009
  /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
57063
- tempStep: number().positive().nullable().optional(),
57010
+ tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
57064
57011
  /** Ms epoch when the slice was last updated (push or command). */
57065
57012
  lastFetchedAt: number()
57066
57013
  });
@@ -57139,17 +57086,17 @@ DeviceType.Thermostat, DeviceType.Climate, method(object({
57139
57086
  * modes to expose in the picker without a slice fetch.
57140
57087
  */
57141
57088
  var RgbTripletSchema = object({
57142
- r: number().int().min(0).max(255),
57143
- g: number().int().min(0).max(255),
57144
- b: number().int().min(0).max(255)
57089
+ r: number().int().min(0).max(255).meta(readingMeta("none")),
57090
+ g: number().int().min(0).max(255).meta(readingMeta("none")),
57091
+ b: number().int().min(0).max(255).meta(readingMeta("none"))
57145
57092
  });
57146
57093
  var HsvTripletSchema = object({
57147
57094
  /** Hue in degrees, 0..360. */
57148
- h: number().min(0).max(360),
57095
+ h: number().min(0).max(360).meta(readingMeta("none")),
57149
57096
  /** Saturation as 0..100 inclusive. */
57150
- s: number().min(0).max(100),
57097
+ s: number().min(0).max(100).meta(readingMeta("none")),
57151
57098
  /** Value/brightness as 0..100 inclusive. */
57152
- v: number().min(0).max(100)
57099
+ v: number().min(0).max(100).meta(readingMeta("none"))
57153
57100
  });
57154
57101
  var ColorInputSchema = discriminatedUnion("mode", [
57155
57102
  object({
@@ -57178,7 +57125,7 @@ object({
57178
57125
  /** Populated when `mode === 'hsv'`. */
57179
57126
  hsv: HsvTripletSchema.optional(),
57180
57127
  /** Populated when `mode === 'mired'`. */
57181
- mireds: number().int().optional(),
57128
+ mireds: number().int().optional().meta(readingMeta("colour-temperature")),
57182
57129
  /** Ms epoch of the last operator-driven change. */
57183
57130
  lastChangedAt: number()
57184
57131
  });
@@ -57302,18 +57249,12 @@ _enum([
57302
57249
  "empty",
57303
57250
  "full"
57304
57251
  ]);
57305
- /** Units a remaining-life counter is reported in. Closed: the panel renders
57306
- * each. */
57307
- var ConsumableRemainingUnitSchema = _enum([
57308
- "days",
57309
- "hours",
57310
- "cycles"
57311
- ]);
57312
- /** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
57313
- * days). `value` null = unknown (D393). */
57252
+ /** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
57253
+ var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
57254
+ /** Remaining life. `value` null = unknown (D393). Duration is seconds. */
57314
57255
  var ConsumableRemainingSchema = object({
57315
- value: number().min(0).nullable(),
57316
- unit: ConsumableRemainingUnitSchema
57256
+ value: number().min(0).nullable().meta(readingMeta("device")),
57257
+ kind: ConsumableRemainingKindSchema
57317
57258
  });
57318
57259
  var ConsumablesStatusSchema = object({
57319
57260
  items: array(object({
@@ -57322,7 +57263,7 @@ var ConsumablesStatusSchema = object({
57322
57263
  /** Display name. */
57323
57264
  label: string().min(1),
57324
57265
  /** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
57325
- level: number().min(0).max(100).nullable(),
57266
+ level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57326
57267
  /** The provider's discrete verdict when known (binary mode). */
57327
57268
  status: ConsumableStatusSchema.nullable(),
57328
57269
  /** What the item is — decides whether low or full is bad. Absent = `wear`. */
@@ -57339,7 +57280,7 @@ var ConsumablesStatusSchema = object({
57339
57280
  })),
57340
57281
  lastChangedAt: number()
57341
57282
  });
57342
- Object.values(DeviceType), [...ConsumableRemainingUnitSchema.options], [...ConsumableStatusSchema.options], [...ConsumableKindSchema.options], [...ConsumableFormSchema.options];
57283
+ Object.values(DeviceType), [...ConsumableRemainingKindSchema.options], [...ConsumableStatusSchema.options], [...ConsumableKindSchema.options], [...ConsumableFormSchema.options];
57343
57284
  Object.values(DeviceType), method(object({
57344
57285
  deviceId: number().int().nonnegative(),
57345
57286
  key: string().min(1)
@@ -57364,39 +57305,17 @@ object({
57364
57305
  /** Current value. Numeric kind → number; select/text/datetime → string.
57365
57306
  * Datetime values are ISO 8601 strings (full timestamp, date-only,
57366
57307
  * or time-only — HA accepts all three forms). */
57367
- value: union([number(), string()]),
57308
+ value: union([number().meta(readingMeta("device")), string()]),
57368
57309
  /** Acceptable values when `kind === 'select'`. Empty for other kinds. */
57369
57310
  options: array(string()),
57370
57311
  /** Ms epoch when the slice was last updated. */
57371
57312
  lastChangedAt: number(),
57372
- /** Display unit-of-measurement (e.g. '°C', '%', 'lx').
57373
- * Populated live from HA `attributes.unit_of_measurement` on each push.
57374
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57375
- unit: string().optional(),
57376
- /** Minimum allowed value. Populated live from HA `attributes.min`.
57377
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57378
- min: number().optional(),
57379
- /** Maximum allowed value. Populated live from HA `attributes.max`.
57380
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57381
- max: number().optional(),
57382
- /** Step increment. Populated live from HA `attributes.step`.
57383
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57384
- step: number().optional(),
57385
- /** Suggested decimal places for numeric display. Populated live from HA
57386
- * `attributes.suggested_display_precision`, or derived from `step` when the
57387
- * HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
57388
- * other kinds. Falls back to auto-formatting when absent. */
57389
- precision: number().int().min(0).max(10).optional(),
57390
- /** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
57391
- * 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
57392
- * (UI defaults to 'datetime'). Derived live from the device's HA domain
57393
- * (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
57394
- * `has_date`/`has_time` attributes on each push. */
57395
- format: _enum([
57396
- "date",
57397
- "time",
57398
- "datetime"
57399
- ]).optional()
57313
+ /** Minimum allowed value, in the device quantity's base unit. */
57314
+ min: number().optional().meta(readingMeta("device")),
57315
+ /** Maximum allowed value, in the device quantity's base unit. */
57316
+ max: number().optional().meta(readingMeta("device")),
57317
+ /** Step increment, in the device quantity's base unit. */
57318
+ step: number().optional().meta(readingMeta("device"))
57400
57319
  });
57401
57320
  var ControlSetValueInputSchema = discriminatedUnion("kind", [
57402
57321
  object({
@@ -57434,9 +57353,9 @@ object({
57434
57353
  ]),
57435
57354
  /** 0 = fully closed, 100 = fully open. Null when the device has
57436
57355
  * no intermediate position surface. */
57437
- position: number().min(0).max(100).nullable(),
57356
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57438
57357
  /** 0 = closed slats, 100 = open slats. Null when no tilt support. */
57439
- tiltPosition: number().min(0).max(100).nullable(),
57358
+ tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57440
57359
  /** Ms epoch when the slice was last updated. */
57441
57360
  lastChangedAt: number()
57442
57361
  });
@@ -57494,9 +57413,9 @@ var NormalizedRangeSchema$1 = object({
57494
57413
  object({
57495
57414
  mode: DayNightModeSchema,
57496
57415
  /** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
57497
- sensitivity: number().optional(),
57416
+ sensitivity: number().optional().meta(readingMeta("none")),
57498
57417
  /** Delay before the IR-cut filter flips, in seconds. */
57499
- switchDelaySec: number().optional(),
57418
+ switchDelaySec: number().optional().meta(readingMeta("duration")),
57500
57419
  lastFetchedAt: number()
57501
57420
  });
57502
57421
  /**
@@ -57651,11 +57570,11 @@ object({
57651
57570
  /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
57652
57571
  availablePrograms: array(string()),
57653
57572
  /** Seconds remaining on the active cycle, or null when not running / not known. */
57654
- remainingSeconds: number().int().nonnegative().nullable(),
57573
+ remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
57655
57574
  /** Seconds the delayed start is set to (time until the cycle starts), or null. */
57656
- delayedStartSeconds: number().int().nonnegative().nullable(),
57575
+ delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
57657
57576
  /** Cycle completion 0..100, or null when not applicable. */
57658
- progressPercent: number().min(0).max(100).nullable(),
57577
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57659
57578
  /** True when the door is reported open; null when the door state is not known. */
57660
57579
  doorOpen: boolean().nullable(),
57661
57580
  /** True when the door is latched (locked) during a cycle; null when not reported. */
@@ -57679,7 +57598,7 @@ object({
57679
57598
  /** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
57680
57599
  machineCareDue: boolean().nullable(),
57681
57600
  /** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
57682
- machineCareRemainingRuns: number().int().nonnegative().nullable(),
57601
+ machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
57683
57602
  /** Option states: null when the appliance does not expose the option or has not said. */
57684
57603
  halfLoad: boolean().nullable(),
57685
57604
  hygienePlus: boolean().nullable(),
@@ -57694,7 +57613,7 @@ object({
57694
57613
  /** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
57695
57614
  activeEvents: array(string()),
57696
57615
  /** Numeric error code when the appliance reports one; null otherwise. */
57697
- errorCode: number().nullable(),
57616
+ errorCode: number().nullable().meta(readingMeta("none")),
57698
57617
  /** Name of the raised alert-level event while the appliance is in error; null otherwise. */
57699
57618
  errorLabel: string().nullable(),
57700
57619
  /** Ms epoch of the last value received from the appliance; 0 = never. */
@@ -57840,24 +57759,14 @@ object({
57840
57759
  /** Ms epoch of the last press. null = never observed since this provider started. */
57841
57760
  lastPressedAt: number().nullable(),
57842
57761
  /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
57843
- pressCountSinceStart: number()
57762
+ pressCountSinceStart: number().meta(readingMeta("count"))
57844
57763
  });
57845
57764
  object({
57846
57765
  deviceId: number(),
57847
57766
  timestamp: number()
57848
57767
  });
57849
57768
  DeviceType.Button, DeviceType.Camera;
57850
- /**
57851
- * Enum-state sensor — a string value picked from a finite option set.
57852
- * Drives HA `sensor` entries with `state_class: enum` (HVAC action
57853
- * states, weather conditions, contact-source channels, mode strings).
57854
- * The option set is stable across observations; it is persisted in the
57855
- * device config blob (via the control slice at adoption) and is NOT
57856
- * stored in `sourceInfo`.
57857
- */
57858
- /** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
57859
- * enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
57860
- var EnumSensorDateTimeFormatSchema = _enum([
57769
+ _enum([
57861
57770
  "date",
57862
57771
  "time",
57863
57772
  "datetime"
@@ -57865,12 +57774,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
57865
57774
  object({
57866
57775
  value: string(),
57867
57776
  /**
57868
- * Set for `DateTimeSensor`-role sensors so the UI renders the ISO `value`
57869
- * as a locale date/time/datetime; absent for plain enum sensors. HA
57870
- * `device_class=timestamp` → `'datetime'`, `device_class=date` → `'date'`,
57871
- * a time-only sensor → `'time'`.
57777
+ * Set when `value` is a timestamp rather than an enum state, so history
57778
+ * does not learn ISO strings as states. The display shape (date / time /
57779
+ * datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
57872
57780
  */
57873
- format: EnumSensorDateTimeFormatSchema.optional(),
57781
+ timestamp: boolean().optional(),
57874
57782
  /** Ms epoch when the slice was last updated. */
57875
57783
  lastFetchedAt: number()
57876
57784
  });
@@ -57889,13 +57797,13 @@ var EventFireSchema = object({
57889
57797
  deviceId: number(),
57890
57798
  eventType: string(),
57891
57799
  data: record(string(), unknown()).nullable(),
57892
- timestamp: number(),
57893
- seq: number()
57800
+ timestamp: number().meta(readingMeta("none")),
57801
+ seq: number().meta(readingMeta("count"))
57894
57802
  });
57895
57803
  object({
57896
57804
  eventTypes: array(string()),
57897
57805
  lastEvent: EventFireSchema.nullable(),
57898
- eventCountSinceStart: number()
57806
+ eventCountSinceStart: number().meta(readingMeta("count"))
57899
57807
  });
57900
57808
  DeviceType.EventEmitter;
57901
57809
  var IdentitySchema = object({
@@ -58284,10 +58192,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
58284
58192
  object({
58285
58193
  /** Active speed as 0..100 inclusive. Null when the device has no
58286
58194
  * speed surface (single-speed fan). */
58287
- percentage: number().min(0).max(100).nullable(),
58195
+ percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58288
58196
  /** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
58289
58197
  * a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
58290
- percentageStep: number().positive().optional(),
58198
+ percentageStep: number().positive().optional().meta(readingMeta("percentage")),
58291
58199
  /** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
58292
58200
  * — empty string when no preset is active or supported. */
58293
58201
  preset: string(),
@@ -58369,7 +58277,7 @@ var FeatureProbeStatusSchema = object({
58369
58277
  /** Camera/firmware model string. `null` when the firmware doesn't expose it. */
58370
58278
  model: string().nullable(),
58371
58279
  /** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
58372
- channelCount: number().nullable(),
58280
+ channelCount: number().nullable().meta(readingMeta("count")),
58373
58281
  /**
58374
58282
  * Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
58375
58283
  * completes — drivers' `getAccessoryChildren()` should treat zero as
@@ -58403,7 +58311,7 @@ object({
58403
58311
  DeviceType.Sensor;
58404
58312
  object({
58405
58313
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
58406
- position: number().min(0).max(100).nullable(),
58314
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58407
58315
  /** Ms epoch when the slice was last updated (push or poll). */
58408
58316
  lastFetchedAt: number()
58409
58317
  });
@@ -58412,9 +58320,9 @@ object({
58412
58320
  /** Whether the humidifier is currently on. */
58413
58321
  on: boolean(),
58414
58322
  /** Current measured relative humidity (0..100). Null when not reported. */
58415
- currentHumidity: number().min(0).max(100).nullable(),
58323
+ currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
58416
58324
  /** Target relative humidity (0..100). Null when no setpoint surface. */
58417
- targetHumidity: number().min(0).max(100).nullable(),
58325
+ targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
58418
58326
  /** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
58419
58327
  * device has no mode surface. */
58420
58328
  mode: string().nullable(),
@@ -58424,9 +58332,9 @@ object({
58424
58332
  * `idle` / `off`). Null when the device doesn't report it. */
58425
58333
  action: string().nullable(),
58426
58334
  /** HA `min_humidity` attribute. Null → UI uses 0. */
58427
- minHumidity: number().nullable(),
58335
+ minHumidity: number().nullable().meta(readingMeta("humidity")),
58428
58336
  /** HA `max_humidity` attribute. Null → UI uses 100. */
58429
- maxHumidity: number().nullable(),
58337
+ maxHumidity: number().nullable().meta(readingMeta("humidity")),
58430
58338
  /** Ms epoch when the slice was last updated. */
58431
58339
  lastChangedAt: number()
58432
58340
  });
@@ -58450,22 +58358,13 @@ DeviceType.Humidifier, method(object({
58450
58358
  auth: "protected"
58451
58359
  });
58452
58360
  object({
58453
- /** Current relative humidity, 0..100. */
58454
- percent: number().min(0).max(100).meta({
58455
- unit: "%",
58361
+ /** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
58362
+ percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
58363
+ precision: 0,
58456
58364
  label: "Humidity"
58457
- }),
58365
+ })),
58458
58366
  /** Ms epoch when the slice was last updated. */
58459
- lastFetchedAt: number(),
58460
- /** Live display unit from the upstream source (e.g. HA
58461
- * `attributes.unit_of_measurement`). The UI prefers this over the
58462
- * role's canonical unit. Absent → fall back to the canonical unit. */
58463
- unit: string().optional(),
58464
- /** Suggested decimal places for numeric display.
58465
- * Populated live from the upstream source when provided (e.g. HA
58466
- * `attributes.suggested_display_precision`). Falls back to
58467
- * auto-formatting when absent. */
58468
- precision: number().int().min(0).max(10).optional()
58367
+ lastFetchedAt: number()
58469
58368
  });
58470
58369
  DeviceType.Sensor, DeviceType.Thermostat;
58471
58370
  object({
@@ -58473,7 +58372,7 @@ object({
58473
58372
  * entity exposes no `entity_picture` (yet). */
58474
58373
  url: string().nullable(),
58475
58374
  /** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
58476
- lastUpdated: number().nullable()
58375
+ lastUpdated: number().nullable().meta(readingMeta("none"))
58477
58376
  });
58478
58377
  DeviceType.Image;
58479
58378
  /**
@@ -58529,19 +58428,19 @@ var NormalizedRangeSchema = object({
58529
58428
  });
58530
58429
  object({
58531
58430
  /** Normalized 0–100. */
58532
- brightness: number().optional(),
58431
+ brightness: number().optional().meta(readingMeta("none")),
58533
58432
  /** Normalized 0–100. */
58534
- contrast: number().optional(),
58433
+ contrast: number().optional().meta(readingMeta("none")),
58535
58434
  /** Normalized 0–100. */
58536
- saturation: number().optional(),
58435
+ saturation: number().optional().meta(readingMeta("none")),
58537
58436
  /** Normalized 0–100. */
58538
- sharpness: number().optional(),
58437
+ sharpness: number().optional().meta(readingMeta("none")),
58539
58438
  mirror: boolean().optional(),
58540
58439
  flip: boolean().optional(),
58541
58440
  rotate: ImageRotateSchema.optional(),
58542
58441
  whiteBalance: WhiteBalanceModeSchema.optional(),
58543
58442
  /** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
58544
- warmth: number().optional(),
58443
+ warmth: number().optional().meta(readingMeta("none")),
58545
58444
  exposureMode: ExposureModeSchema.optional(),
58546
58445
  backlightMode: BacklightModeSchema.optional(),
58547
58446
  lastFetchedAt: number()
@@ -58786,10 +58685,10 @@ var IntercomAbilitySchema = object({
58786
58685
  /** Codecs accepted by firmware, in preference order. Driver-authored strings. */
58787
58686
  codecs: array(string()),
58788
58687
  /** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
58789
- sampleRate: number(),
58688
+ sampleRate: number().meta(readingMeta("none")),
58790
58689
  duplex: _enum(["half", "full"]),
58791
58690
  /** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
58792
- maxBacklogMs: number()
58691
+ maxBacklogMs: number().meta(readingMeta("duration-ms"))
58793
58692
  });
58794
58693
  var IntercomStatusSchema = object({
58795
58694
  /** True while a talk session is open. */
@@ -58900,13 +58799,13 @@ object({
58900
58799
  activity: LawnMowerActivitySchema,
58901
58800
  /** 0..100 battery percentage. Null when the device has no battery
58902
58801
  * reading. */
58903
- batteryLevel: number().min(0).max(100).nullable(),
58802
+ batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58904
58803
  /** 0..100 mowing-completion percentage of the current task, or null when no
58905
58804
  * task is active / progress is unavailable. */
58906
- progressPercent: number().min(0).max(100).nullable(),
58805
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58907
58806
  /** Current device/event code (dynamic — mostly status, sometimes an error),
58908
58807
  * or null when unknown. */
58909
- currentCode: number().nullable(),
58808
+ currentCode: number().nullable().meta(readingMeta("none")),
58910
58809
  /** Human label for {@link currentCode}, or null when undecodable. */
58911
58810
  currentCodeLabel: string().nullable(),
58912
58811
  /** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
@@ -59368,7 +59267,7 @@ object({
59368
59267
  state: MediaPlayerStateSchema,
59369
59268
  /** Volume as 0..100 inclusive. Null when the device has no volume
59370
59269
  * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
59371
- volumeLevel: number().min(0).max(100).nullable(),
59270
+ volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59372
59271
  /** Mute toggle distinct from volume=0. Null when no mute surface.
59373
59272
  * Pair with `DeviceFeature.MediaPlayerMute`. */
59374
59273
  isMuted: boolean().nullable(),
@@ -59381,10 +59280,10 @@ object({
59381
59280
  currentMedia: MediaInfoSchema.nullable(),
59382
59281
  /** Current playback position in ms. Null when not seekable or
59383
59282
  * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
59384
- positionMs: number().int().nonnegative().nullable(),
59283
+ positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
59385
59284
  /** Total duration of the current media in ms. Null when unknown
59386
59285
  * (live stream) or nothing is playing. */
59387
- durationMs: number().int().nonnegative().nullable(),
59286
+ durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
59388
59287
  /** Shuffle toggle. Null when no shuffle surface.
59389
59288
  * Pair with `DeviceFeature.MediaPlayerShuffle`. */
59390
59289
  shuffle: boolean().nullable(),
@@ -59684,7 +59583,7 @@ object({
59684
59583
  * it, every provider seeded it at boot, and every session left it
59685
59584
  * `detected: true` after its detach.
59686
59585
  */
59687
- autoClearAfterMs: number().nullable()
59586
+ autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
59688
59587
  });
59689
59588
  /**
59690
59589
  * Payload of `motion.onMotionChanged` event + the corresponding bus
@@ -59734,7 +59633,7 @@ var MotionZoneRegionSchema = object({
59734
59633
  });
59735
59634
  object({
59736
59635
  enabled: boolean(),
59737
- sensitivity: number(),
59636
+ sensitivity: number().meta(readingMeta("none")),
59738
59637
  /** Grid region(s). Today exactly one `grid` shape. */
59739
59638
  regions: array(MotionZoneRegionSchema),
59740
59639
  lastFetchedAt: number()
@@ -60038,7 +59937,7 @@ var NavigationStatusSchema = object({
60038
59937
  following: boolean(),
60039
59938
  flash: boolean(),
60040
59939
  lightMode: NavigationLightModeSchema,
60041
- lightLevel: number().min(40).max(100),
59940
+ lightLevel: number().min(40).max(100).meta(readingMeta("none")),
60042
59941
  /** Ms epoch when the slice was last updated. */
60043
59942
  lastChangedAt: number()
60044
59943
  });
@@ -60084,13 +59983,13 @@ var NetworkLinkStatusSchema = object({
60084
59983
  * one whose reading has not landed must not be drawn at 0 %. Consumers
60085
59984
  * SKIP a null rather than coerce it.
60086
59985
  */
60087
- signalPercent: number().min(0).max(100).nullable(),
59986
+ signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60088
59987
  /** Raw received signal strength in dBm, when the firmware reports one. */
60089
- rssiDbm: number().optional(),
59988
+ rssiDbm: number().optional().meta(readingMeta("signal-level")),
60090
59989
  /** Network name of a wireless link, when the firmware reports it. */
60091
59990
  ssid: string().optional(),
60092
59991
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
60093
- lastUpdated: number()
59992
+ lastUpdated: number().meta(readingMeta("none"))
60094
59993
  });
60095
59994
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.Light, DeviceType.Lock, DeviceType.Siren, object({
60096
59995
  deviceId: number(),
@@ -60409,7 +60308,7 @@ object({
60409
60308
  lastError: string().nullable(),
60410
60309
  /** Number of deliveries currently buffered server-side (rate-limited
60411
60310
  * notifiers may queue). 0 when the provider sends synchronously. */
60412
- queueDepth: number().int().nonnegative(),
60311
+ queueDepth: number().int().nonnegative().meta(readingMeta("count")),
60413
60312
  /** Per-feature capability matrix — authoritative source for the
60414
60313
  * compose-form field gating. */
60415
60314
  supports: NotifierSupportsSchema
@@ -60459,15 +60358,8 @@ DeviceType.Notifier, method(NotifierSendInputSchema, NotifierSendResultSchema, {
60459
60358
  acceptedAt: number()
60460
60359
  });
60461
60360
  object({
60462
- value: number(),
60463
- /** Display unit-of-measurement (e.g. 'dBm', 's', 'rpm', 'steps').
60464
- * Populated live from the upstream source on each state push.
60465
- * Absent for unitless sensors. */
60466
- unit: string().optional(),
60467
- /** Suggested decimal places for numeric display.
60468
- * Populated live from the upstream source when provided.
60469
- * Falls back to auto-formatting when absent. */
60470
- precision: number().int().min(0).max(10).optional(),
60361
+ /** Reading in the device quantity's base unit. `null` when refused (D393). */
60362
+ value: number().nullable().meta(readingMeta("device")),
60471
60363
  /** Ms epoch when the slice was last updated. */
60472
60364
  lastFetchedAt: number()
60473
60365
  });
@@ -60757,23 +60649,26 @@ object({
60757
60649
  /** Food currently in the bowl (grams). Null when the device has not
60758
60650
  * reported a reading yet. On dual-hopper models this is the combined
60759
60651
  * bowl reading; per-hopper levels live in `food1`/`food2`. */
60760
- foodLevel: number().nullable(),
60652
+ foodLevel: number().nullable().meta(readingMeta("none")),
60761
60653
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
60762
60654
  * single-hopper models. */
60763
- food1: number().nullable(),
60655
+ food1: number().nullable().meta(readingMeta("none")),
60764
60656
  /** Hopper-2 food level (grams) on dual-hopper feeders; null on
60765
60657
  * single-hopper models. */
60766
- food2: number().nullable(),
60658
+ food2: number().nullable().meta(readingMeta("none")),
60767
60659
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
60768
60660
  * (`device_class: problem`, on = low). True when the bowl is empty /
60769
60661
  * below the feeder's low threshold. */
60770
60662
  lowFood: boolean(),
60771
60663
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
60772
60664
  * device has no battery reading. */
60773
- batteryPower: number().min(0).max(100).nullable(),
60774
- /** Days of desiccant life remaining. Null when the model has no
60775
- * desiccant sensor. */
60776
- desiccantLeftDays: number().nullable(),
60665
+ batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60666
+ /**
60667
+ * Desiccant life remaining, stored in seconds (duration base, D944).
60668
+ * Providers convert from the days the feeder reports. Null when the
60669
+ * model has no desiccant sensor.
60670
+ */
60671
+ desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
60777
60672
  /** True while a feed is in progress. */
60778
60673
  feeding: boolean(),
60779
60674
  /** Decoded connectivity / power status (HA petkit device-status enum).
@@ -60784,7 +60679,7 @@ object({
60784
60679
  * with `errorCode` for consumers that want the raw integer. */
60785
60680
  error: string().nullable(),
60786
60681
  /** Raw device error code (0 / null = no error). */
60787
- errorCode: number().nullable(),
60682
+ errorCode: number().nullable().meta(readingMeta("none")),
60788
60683
  /** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
60789
60684
  isDualHopper: boolean(),
60790
60685
  /** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
@@ -60794,7 +60689,7 @@ object({
60794
60689
  /** Play a chime when dispensing. */
60795
60690
  feedSound: boolean(),
60796
60691
  /** Speaker / prompt volume level (device-scaled integer). */
60797
- volume: number(),
60692
+ volume: number().meta(readingMeta("none")),
60798
60693
  /** Ms epoch when the slice was last refreshed from the cloud. */
60799
60694
  lastFetchedAt: number()
60800
60695
  });
@@ -61231,36 +61126,19 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
61231
61126
  });
61232
61127
  object({
61233
61128
  /** Instantaneous power draw in watts. */
61234
- watts: number().optional().meta({
61235
- unit: "W",
61236
- label: "Power"
61237
- }),
61129
+ watts: number().optional().meta(readingMeta("power", { label: "Power" })),
61238
61130
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
61239
- kwhTotal: number().optional().meta({
61240
- unit: "kWh",
61241
- label: "Energy"
61242
- }),
61131
+ kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
61243
61132
  /** Voltage in volts. */
61244
- volts: number().optional(),
61133
+ volts: number().optional().meta(readingMeta("voltage")),
61245
61134
  /** Current in amperes. */
61246
- amps: number().optional(),
61247
- /** Apparent power in volt-amperes (W × power-factor reciprocal). */
61248
- va: number().optional(),
61135
+ amps: number().optional().meta(readingMeta("current")),
61136
+ /** Apparent power in volt-amperes. */
61137
+ va: number().optional().meta(readingMeta("apparent-power")),
61249
61138
  /** Power factor (0..1) if reported. */
61250
- powerFactor: number().min(0).max(1).optional(),
61139
+ powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
61251
61140
  /** Ms epoch when the slice was last updated. */
61252
- lastFetchedAt: number(),
61253
- /** Live display unit of the single metric this slice carries (e.g. HA
61254
- * `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
61255
- * Each upstream `sensor.*` entity surfaces ONE device_class, so one
61256
- * unit per slice is unambiguous. The UI prefers this over the role's
61257
- * canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
61258
- unit: string().optional(),
61259
- /** Suggested decimal places for numeric display.
61260
- * Populated live from the upstream source when provided (e.g. HA
61261
- * `attributes.suggested_display_precision`). Falls back to
61262
- * auto-formatting when absent. */
61263
- precision: number().int().min(0).max(10).optional()
61141
+ lastFetchedAt: number()
61264
61142
  });
61265
61143
  DeviceType.Sensor;
61266
61144
  /**
@@ -61281,11 +61159,11 @@ DeviceType.Sensor;
61281
61159
  */
61282
61160
  var GpsLocationSchema = object({
61283
61161
  /** Latitude in decimal degrees, -90..90. */
61284
- latitude: number().min(-90).max(90),
61162
+ latitude: number().min(-90).max(90).meta(readingMeta("angle")),
61285
61163
  /** Longitude in decimal degrees, -180..180. */
61286
- longitude: number().min(-180).max(180),
61164
+ longitude: number().min(-180).max(180).meta(readingMeta("angle")),
61287
61165
  /** Reported accuracy in meters (lower = better). */
61288
- accuracyMeters: number().nonnegative()
61166
+ accuracyMeters: number().nonnegative().meta(readingMeta("length"))
61289
61167
  });
61290
61168
  object({
61291
61169
  /** `home` / `not_home` / any user-defined zone name. */
@@ -61297,25 +61175,16 @@ object({
61297
61175
  gps: GpsLocationSchema.nullable(),
61298
61176
  /** Optional battery level of the tracking device (0..100). Null
61299
61177
  * when not reported. */
61300
- batteryPercent: number().min(0).max(100).nullable(),
61178
+ batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61301
61179
  /** Ms epoch when the slice was last updated. */
61302
61180
  lastChangedAt: number()
61303
61181
  });
61304
61182
  DeviceType.Presence;
61305
61183
  object({
61306
- /** Current pressure in hPa. */
61307
- hpa: number(),
61184
+ /** Current pressure in hPa. `null` when the sample was refused (D393). */
61185
+ hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
61308
61186
  /** Ms epoch when the slice was last updated. */
61309
- lastFetchedAt: number(),
61310
- /** Live display unit from the upstream source (e.g. HA
61311
- * `attributes.unit_of_measurement`). The UI prefers this over the
61312
- * role's canonical unit. Absent → fall back to the canonical unit. */
61313
- unit: string().optional(),
61314
- /** Suggested decimal places for numeric display.
61315
- * Populated live from the upstream source when provided (e.g. HA
61316
- * `attributes.suggested_display_precision`). Falls back to
61317
- * auto-formatting when absent. */
61318
- precision: number().int().min(0).max(10).optional()
61187
+ lastFetchedAt: number()
61319
61188
  });
61320
61189
  DeviceType.Sensor;
61321
61190
  /**
@@ -61648,8 +61517,8 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
61648
61517
  }), _void(), { kind: "mutation" });
61649
61518
  var PtzAutotrackSettingsSchema = object({
61650
61519
  targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
61651
- stopDelaySeconds: number().int().min(0).max(300),
61652
- disappearDelaySeconds: number().int().min(0).max(300)
61520
+ stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
61521
+ disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
61653
61522
  });
61654
61523
  /**
61655
61524
  * One option the camera advertises as a valid `targetType`. Drivers
@@ -61706,7 +61575,7 @@ object({
61706
61575
  /** null = unknown — never coerced to false (D393). */
61707
61576
  running: boolean().nullable(),
61708
61577
  /** Epoch ms the current boolean began; null while `running` is null. */
61709
- since: number().nullable(),
61578
+ since: number().nullable().meta(readingMeta("none")),
61710
61579
  source: RunningStateSourceSchema.meta({ label: "Source" }),
61711
61580
  phase: string().nullable(),
61712
61581
  /** null until a reading has been published (D393). */
@@ -62904,9 +62773,9 @@ var RecordTriggerSchema = _enum([
62904
62773
  */
62905
62774
  var RecordWindowSchema = object({
62906
62775
  trigger: RecordTriggerSchema,
62907
- day: number().int().min(0).max(6),
62908
- startMinute: number().int().min(0).max(1439),
62909
- endMinute: number().int().min(1).max(1440)
62776
+ day: number().int().min(0).max(6).meta(readingMeta("none")),
62777
+ startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
62778
+ endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
62910
62779
  });
62911
62780
  /** Status of one physical volume, as the camera itself describes it. */
62912
62781
  var OnboardStorageVolumeSchema = object({
@@ -62975,10 +62844,10 @@ object({
62975
62844
  /** Master "record to the card at all" switch. */
62976
62845
  enabled: boolean().nullable(),
62977
62846
  overwriteWhenFull: boolean().nullable(),
62978
- preRecordSec: number().nullable(),
62979
- postRecordSec: number().nullable(),
62847
+ preRecordSec: number().nullable().meta(readingMeta("duration")),
62848
+ postRecordSec: number().nullable().meta(readingMeta("duration")),
62980
62849
  /** Length of one recorded file, in minutes. */
62981
- segmentMinutes: number().nullable(),
62850
+ segmentMinutes: number().nullable().meta(readingMeta("none")),
62982
62851
  /** The primary track's weekly windows, flattened. */
62983
62852
  windows: array(RecordWindowSchema),
62984
62853
  /**
@@ -62994,7 +62863,7 @@ object({
62994
62863
  * provider reporting a non-zero count also reports the schedule as not
62995
62864
  * writable.
62996
62865
  */
62997
- unreadableWindows: number(),
62866
+ unreadableWindows: number().meta(readingMeta("count")),
62998
62867
  /**
62999
62868
  * The camera is scheduled to record and has NO usable storage.
63000
62869
  *
@@ -63631,7 +63500,7 @@ var CamStreamDescriptorSchema = object({
63631
63500
  url: string().optional(),
63632
63501
  codec: string().optional(),
63633
63502
  resolution: CamStreamResolutionSchema.optional(),
63634
- fps: number().positive().optional(),
63503
+ fps: number().positive().optional().meta(readingMeta("rate")),
63635
63504
  label: string().optional(),
63636
63505
  /** Device-level features (e.g. `battery-operated`) — drives broker policy. */
63637
63506
  deviceFeatures: array(string()).optional(),
@@ -63661,18 +63530,18 @@ var StreamProfileSchema = _enum([
63661
63530
  ]);
63662
63531
  /** The current encoding config of one stream profile. */
63663
63532
  var StreamProfileConfigSchema = object({
63664
- width: number(),
63665
- height: number(),
63533
+ width: number().meta(readingMeta("none")),
63534
+ height: number().meta(readingMeta("none")),
63666
63535
  codec: _enum(["h264", "h265"]),
63667
- framerate: number(),
63668
- bitrate: number(),
63536
+ framerate: number().meta(readingMeta("rate")),
63537
+ bitrate: number().meta(readingMeta("bitrate")),
63669
63538
  bitrateMode: _enum(["vbr", "cbr"]).optional(),
63670
63539
  encoderProfile: _enum([
63671
63540
  "high",
63672
63541
  "main",
63673
63542
  "baseline"
63674
63543
  ]).optional(),
63675
- gop: number().optional(),
63544
+ gop: number().optional().meta(readingMeta("none")),
63676
63545
  /**
63677
63546
  * Whether THIS profile currently carries an audio track. READ-ONLY here.
63678
63547
  *
@@ -64315,23 +64184,13 @@ object({
64315
64184
  });
64316
64185
  DeviceType.Sensor;
64317
64186
  object({
64318
- /** Current temperature in Celsius. */
64319
- celsius: number().meta({
64320
- unit: "°C",
64187
+ /** Current temperature in Celsius. `null` when the sample was refused (D393). */
64188
+ celsius: number().nullable().meta(readingMeta("temperature", {
64189
+ precision: 1,
64321
64190
  label: "Temperature"
64322
- }),
64191
+ })),
64323
64192
  /** Ms epoch when the slice was last updated (push or poll). */
64324
- lastFetchedAt: number(),
64325
- /** Live display unit from the upstream source (e.g. HA
64326
- * `attributes.unit_of_measurement`). The UI prefers this over the
64327
- * role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
64328
- * Absent → the UI falls back to the role's canonical unit. */
64329
- unit: string().optional(),
64330
- /** Suggested decimal places for numeric display.
64331
- * Populated live from the upstream source when provided (e.g. HA
64332
- * `attributes.suggested_display_precision`). Falls back to
64333
- * auto-formatting when absent. */
64334
- precision: number().int().min(0).max(10).optional()
64193
+ lastFetchedAt: number()
64335
64194
  });
64336
64195
  DeviceType.Sensor, DeviceType.Thermostat;
64337
64196
  /**
@@ -64583,16 +64442,16 @@ object({
64583
64442
  ]),
64584
64443
  /** 0..100 battery percentage. Null when the device has no battery
64585
64444
  * reading. */
64586
- batteryLevel: number().min(0).max(100).nullable(),
64445
+ batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64587
64446
  /** Current fan-speed token (provider-verbatim). Null when unknown or
64588
64447
  * the vacuum has no speed control. */
64589
64448
  fanSpeed: string().nullable(),
64590
64449
  /** Speed tokens the hardware accepts — drives the UI selector. */
64591
64450
  availableFanSpeeds: array(string()),
64592
64451
  /** 0..100 cleaning-completion percentage of the current task, or null. */
64593
- progressPercent: number().min(0).max(100).nullable(),
64452
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64594
64453
  /** Current error code (0 / null = no error). */
64595
- errorCode: number().nullable(),
64454
+ errorCode: number().nullable().meta(readingMeta("none")),
64596
64455
  /** Human label for {@link errorCode}, or null when none / undecodable. */
64597
64456
  errorLabel: string().nullable(),
64598
64457
  /** Ms epoch when the slice was last updated. */
@@ -64631,7 +64490,7 @@ object({
64631
64490
  ]),
64632
64491
  /** 0 = fully closed, 100 = fully open. Null when the device has no
64633
64492
  * intermediate position surface. */
64634
- position: number().min(0).max(100).nullable(),
64493
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64635
64494
  /** Ms epoch when the slice was last updated. */
64636
64495
  lastChangedAt: number()
64637
64496
  });
@@ -64659,9 +64518,9 @@ object({
64659
64518
  DeviceType.Sensor;
64660
64519
  object({
64661
64520
  /** Current measured temperature. Null when not reported. */
64662
- currentTemp: number().nullable(),
64521
+ currentTemp: number().nullable().meta(readingMeta("temperature")),
64663
64522
  /** Target temperature setpoint. Null when no setpoint surface. */
64664
- targetTemp: number().nullable(),
64523
+ targetTemp: number().nullable().meta(readingMeta("temperature")),
64665
64524
  /** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
64666
64525
  * `heat_pump` / `high_demand` / `performance` / `off`). Null when the
64667
64526
  * device reports an unknown state. */
@@ -64672,9 +64531,9 @@ object({
64672
64531
  * has no away surface. */
64673
64532
  away: boolean().nullable(),
64674
64533
  /** HA `min_temp` attribute. Null when not reported. */
64675
- minTemp: number().nullable(),
64534
+ minTemp: number().nullable().meta(readingMeta("temperature")),
64676
64535
  /** HA `max_temp` attribute. Null when not reported. */
64677
- maxTemp: number().nullable(),
64536
+ maxTemp: number().nullable().meta(readingMeta("temperature")),
64678
64537
  /** Ms epoch when the slice was last updated. */
64679
64538
  lastChangedAt: number()
64680
64539
  });
@@ -64698,25 +64557,18 @@ DeviceType.WaterHeater, method(object({
64698
64557
  auth: "protected"
64699
64558
  });
64700
64559
  object({
64701
- /** Verbatim HA condition state (`sunny`, `cloudy`, `rainy`, …). Null
64702
- * when no condition has been reported yet. */
64560
+ /** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
64703
64561
  condition: string().nullable(),
64704
- /** Current temperature in the reported unit. Null when not provided. */
64705
- temperature: number().nullable(),
64706
- /** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
64707
- temperatureUnit: string().nullable(),
64562
+ /** Current temperature in Celsius. Null when not provided or refused. */
64563
+ temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
64708
64564
  /** Relative humidity (0..100). Null when not provided. */
64709
- humidity: number().min(0).max(100).nullable(),
64710
- /** Barometric pressure in the reported unit. Null when not provided. */
64711
- pressure: number().nullable(),
64712
- /** Pressure unit string (e.g. `hPa` / `inHg`). Null when not provided. */
64713
- pressureUnit: string().nullable(),
64714
- /** Wind speed in the reported unit. Null when not provided. */
64715
- windSpeed: number().nullable(),
64716
- /** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
64717
- windSpeedUnit: string().nullable(),
64565
+ humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
64566
+ /** Barometric pressure in hPa. Null when not provided or refused. */
64567
+ pressure: number().nullable().meta(readingMeta("pressure")),
64568
+ /** Wind speed in metres per second. Null when not provided or refused. */
64569
+ windSpeed: number().nullable().meta(readingMeta("speed")),
64718
64570
  /** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
64719
- windBearing: number().nullable(),
64571
+ windBearing: number().nullable().meta(readingMeta("angle")),
64720
64572
  /** Ms epoch when the slice was last updated. */
64721
64573
  lastFetchedAt: number()
64722
64574
  });
@@ -78689,7 +78541,6 @@ var SENSOR_CAP_VOCABULARY = {
78689
78541
  field: "value",
78690
78542
  quantity: t("the reading", "il valore"),
78691
78543
  label: t("Reading", "Valore"),
78692
- unitField: "unit",
78693
78544
  icon: "gauge"
78694
78545
  }]
78695
78546
  },
@@ -78825,7 +78676,7 @@ var SENSOR_CAP_VOCABULARY = {
78825
78676
  field: "temperature",
78826
78677
  quantity: t("the outdoor temperature", "la temperatura esterna"),
78827
78678
  label: t("Temperature", "Temperatura"),
78828
- unitField: "temperatureUnit",
78679
+ unit: "°C",
78829
78680
  icon: "thermometer"
78830
78681
  },
78831
78682
  {
@@ -78841,7 +78692,7 @@ var SENSOR_CAP_VOCABULARY = {
78841
78692
  field: "windSpeed",
78842
78693
  quantity: t("the wind speed", "la velocità del vento"),
78843
78694
  label: t("Wind", "Vento"),
78844
- unitField: "windSpeedUnit",
78695
+ unit: "m/s",
78845
78696
  icon: "wind"
78846
78697
  },
78847
78698
  {
@@ -78849,7 +78700,7 @@ var SENSOR_CAP_VOCABULARY = {
78849
78700
  field: "pressure",
78850
78701
  quantity: t("the air pressure", "la pressione atmosferica"),
78851
78702
  label: t("Pressure", "Pressione"),
78852
- unitField: "pressureUnit",
78703
+ unit: "hPa",
78853
78704
  icon: "gauge"
78854
78705
  }
78855
78706
  ]