@camstack/addon-model-studio 1.1.295 → 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-DNRDIeO2.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-U1Dq0t4d.mjs} +4 -4
  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-DLBoEjee.mjs → hostInit-lgxajgO5.mjs} +3 -3
  8. package/dist/model-studio.addon.js +278 -422
  9. package/dist/model-studio.addon.mjs +278 -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-Bu2vRo0B.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-ZIEWPFwu.mjs} +1 -1
  17. package/package.json +7 -2
  18. package/dist/_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CDZyIXC0.mjs +0 -26
@@ -5829,7 +5829,7 @@ var ZodIssueCode = {
5829
5829
  var ZodFirstPartyTypeKind;
5830
5830
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5831
5831
  //#endregion
5832
- //#region ../types/dist/sleep-BErAxhul.mjs
5832
+ //#region ../types/dist/sleep-CARd6wZF.mjs
5833
5833
  /**
5834
5834
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5835
5835
  * window to float samples (D455).
@@ -8316,6 +8316,52 @@ var RecordKindDeclarationSchema = object({
8316
8316
  }).strict();
8317
8317
  /** The kind names: lower-case, dash separated, short enough for an index name. */
8318
8318
  var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
8319
+ var READING_QUANTITIES = [
8320
+ "temperature",
8321
+ "pressure",
8322
+ "speed",
8323
+ "precipitation",
8324
+ "precipitation-rate",
8325
+ "length",
8326
+ "area",
8327
+ "illuminance",
8328
+ "irradiance",
8329
+ "power",
8330
+ "energy",
8331
+ "voltage",
8332
+ "current",
8333
+ "apparent-power",
8334
+ "humidity",
8335
+ "percentage",
8336
+ "concentration-volume",
8337
+ "concentration-mass",
8338
+ "concentration-ppb",
8339
+ "volume",
8340
+ "duration",
8341
+ "duration-ms",
8342
+ "information",
8343
+ "sound-level",
8344
+ "signal-level",
8345
+ "colour-temperature",
8346
+ "angle",
8347
+ "bitrate",
8348
+ "rate",
8349
+ "mass",
8350
+ "index",
8351
+ "ratio",
8352
+ "count",
8353
+ "none",
8354
+ "device"
8355
+ ];
8356
+ var ReadingQuantitySchema = _enum(READING_QUANTITIES);
8357
+ new Set(READING_QUANTITIES);
8358
+ function readingMeta(quantity, extras) {
8359
+ return {
8360
+ quantity,
8361
+ ...extras?.precision !== void 0 ? { precision: extras.precision } : {},
8362
+ ...extras?.label !== void 0 ? { label: extras.label } : {}
8363
+ };
8364
+ }
8319
8365
  /** Shared Zod schemas used across streaming capabilities. */
8320
8366
  var CamProfileSchema = _enum([
8321
8367
  "high",
@@ -8343,8 +8389,8 @@ var CamStreamKindSchema = _enum([
8343
8389
  "derived"
8344
8390
  ]);
8345
8391
  var CamStreamResolutionSchema = object({
8346
- width: number().int().positive(),
8347
- height: number().int().positive()
8392
+ width: number().int().positive().meta(readingMeta("none")),
8393
+ height: number().int().positive().meta(readingMeta("none"))
8348
8394
  });
8349
8395
  var CameraStreamSchema = object({
8350
8396
  /** Stable, provider-assigned id unique within the (deviceId) scope. */
@@ -9068,14 +9114,6 @@ Object.freeze({
9068
9114
  {
9069
9115
  "path": "aqi",
9070
9116
  "valueType": "number"
9071
- },
9072
- {
9073
- "path": "unit",
9074
- "valueType": "string"
9075
- },
9076
- {
9077
- "path": "precision",
9078
- "valueType": "number"
9079
9117
  }
9080
9118
  ],
9081
9119
  "alarm-panel": [
@@ -9111,20 +9149,10 @@ Object.freeze({
9111
9149
  "valueType": "boolean"
9112
9150
  }
9113
9151
  ],
9114
- "ambient-light-sensor": [
9115
- {
9116
- "path": "lux",
9117
- "valueType": "number"
9118
- },
9119
- {
9120
- "path": "unit",
9121
- "valueType": "string"
9122
- },
9123
- {
9124
- "path": "precision",
9125
- "valueType": "number"
9126
- }
9127
- ],
9152
+ "ambient-light-sensor": [{
9153
+ "path": "lux",
9154
+ "valueType": "number"
9155
+ }],
9128
9156
  "audio-metrics": [
9129
9157
  {
9130
9158
  "path": "ts",
@@ -9204,6 +9232,10 @@ Object.freeze({
9204
9232
  "path": "lastUpdated",
9205
9233
  "valueType": "number"
9206
9234
  },
9235
+ {
9236
+ "path": "volts",
9237
+ "valueType": "number"
9238
+ },
9207
9239
  {
9208
9240
  "path": "lastContactAt",
9209
9241
  "valueType": "number"
@@ -9446,10 +9478,6 @@ Object.freeze({
9446
9478
  "datetime"
9447
9479
  ]
9448
9480
  },
9449
- {
9450
- "path": "unit",
9451
- "valueType": "string"
9452
- },
9453
9481
  {
9454
9482
  "path": "min",
9455
9483
  "valueType": "number"
@@ -9461,19 +9489,6 @@ Object.freeze({
9461
9489
  {
9462
9490
  "path": "step",
9463
9491
  "valueType": "number"
9464
- },
9465
- {
9466
- "path": "precision",
9467
- "valueType": "number"
9468
- },
9469
- {
9470
- "path": "format",
9471
- "valueType": "enum",
9472
- "enumValues": [
9473
- "date",
9474
- "time",
9475
- "datetime"
9476
- ]
9477
9492
  }
9478
9493
  ],
9479
9494
  "cover": [
@@ -9714,13 +9729,8 @@ Object.freeze({
9714
9729
  "path": "value",
9715
9730
  "valueType": "string"
9716
9731
  }, {
9717
- "path": "format",
9718
- "valueType": "enum",
9719
- "enumValues": [
9720
- "date",
9721
- "time",
9722
- "datetime"
9723
- ]
9732
+ "path": "timestamp",
9733
+ "valueType": "boolean"
9724
9734
  }],
9725
9735
  "event-emitter": [
9726
9736
  {
@@ -9827,20 +9837,10 @@ Object.freeze({
9827
9837
  "valueType": "number"
9828
9838
  }
9829
9839
  ],
9830
- "humidity-sensor": [
9831
- {
9832
- "path": "percent",
9833
- "valueType": "number"
9834
- },
9835
- {
9836
- "path": "unit",
9837
- "valueType": "string"
9838
- },
9839
- {
9840
- "path": "precision",
9841
- "valueType": "number"
9842
- }
9843
- ],
9840
+ "humidity-sensor": [{
9841
+ "path": "percent",
9842
+ "valueType": "number"
9843
+ }],
9844
9844
  "image": [{
9845
9845
  "path": "url",
9846
9846
  "valueType": "string"
@@ -10179,20 +10179,10 @@ Object.freeze({
10179
10179
  "valueType": "boolean"
10180
10180
  }
10181
10181
  ],
10182
- "numeric-sensor": [
10183
- {
10184
- "path": "value",
10185
- "valueType": "number"
10186
- },
10187
- {
10188
- "path": "unit",
10189
- "valueType": "string"
10190
- },
10191
- {
10192
- "path": "precision",
10193
- "valueType": "number"
10194
- }
10195
- ],
10182
+ "numeric-sensor": [{
10183
+ "path": "value",
10184
+ "valueType": "number"
10185
+ }],
10196
10186
  "pet-feeder": [
10197
10187
  {
10198
10188
  "path": "foodLevel",
@@ -10284,14 +10274,6 @@ Object.freeze({
10284
10274
  {
10285
10275
  "path": "powerFactor",
10286
10276
  "valueType": "number"
10287
- },
10288
- {
10289
- "path": "unit",
10290
- "valueType": "string"
10291
- },
10292
- {
10293
- "path": "precision",
10294
- "valueType": "number"
10295
10277
  }
10296
10278
  ],
10297
10279
  "presence": [
@@ -10320,20 +10302,10 @@ Object.freeze({
10320
10302
  "valueType": "number"
10321
10303
  }
10322
10304
  ],
10323
- "pressure-sensor": [
10324
- {
10325
- "path": "hpa",
10326
- "valueType": "number"
10327
- },
10328
- {
10329
- "path": "unit",
10330
- "valueType": "string"
10331
- },
10332
- {
10333
- "path": "precision",
10334
- "valueType": "number"
10335
- }
10336
- ],
10305
+ "pressure-sensor": [{
10306
+ "path": "hpa",
10307
+ "valueType": "number"
10308
+ }],
10337
10309
  "privacy-mask": [{
10338
10310
  "path": "enabled",
10339
10311
  "valueType": "boolean"
@@ -10644,20 +10616,10 @@ Object.freeze({
10644
10616
  "path": "tampered",
10645
10617
  "valueType": "boolean"
10646
10618
  }],
10647
- "temperature-sensor": [
10648
- {
10649
- "path": "celsius",
10650
- "valueType": "number"
10651
- },
10652
- {
10653
- "path": "unit",
10654
- "valueType": "string"
10655
- },
10656
- {
10657
- "path": "precision",
10658
- "valueType": "number"
10659
- }
10660
- ],
10619
+ "temperature-sensor": [{
10620
+ "path": "celsius",
10621
+ "valueType": "number"
10622
+ }],
10661
10623
  "update": [
10662
10624
  {
10663
10625
  "path": "currentVersion",
@@ -10768,10 +10730,6 @@ Object.freeze({
10768
10730
  "path": "temperature",
10769
10731
  "valueType": "number"
10770
10732
  },
10771
- {
10772
- "path": "temperatureUnit",
10773
- "valueType": "string"
10774
- },
10775
10733
  {
10776
10734
  "path": "humidity",
10777
10735
  "valueType": "number"
@@ -10780,18 +10738,10 @@ Object.freeze({
10780
10738
  "path": "pressure",
10781
10739
  "valueType": "number"
10782
10740
  },
10783
- {
10784
- "path": "pressureUnit",
10785
- "valueType": "string"
10786
- },
10787
10741
  {
10788
10742
  "path": "windSpeed",
10789
10743
  "valueType": "number"
10790
10744
  },
10791
- {
10792
- "path": "windSpeedUnit",
10793
- "valueType": "string"
10794
- },
10795
10745
  {
10796
10746
  "path": "windBearing",
10797
10747
  "valueType": "number"
@@ -18295,7 +18245,8 @@ var CompositionProblemSchema = object({
18295
18245
  "item-field-reserved",
18296
18246
  "target-missing",
18297
18247
  "target-unreadable",
18298
- "not-replaceable"
18248
+ "not-replaceable",
18249
+ "native-capability-replace"
18299
18250
  ]),
18300
18251
  path: string(),
18301
18252
  message: string()
@@ -18353,9 +18304,11 @@ var CompositionFieldPlanSchema = object({
18353
18304
  reads: array(CompositionFieldReadSchema)
18354
18305
  });
18355
18306
  /**
18356
- * What a feature does to its target (D663): `new` builds a capability on a new
18357
- * device; on an existing device it `add`s a capability the device lacks, or
18358
- * `replace`s named fields of one it has natively.
18307
+ * What a feature does to its target (D663, D944): `new` builds a capability on a
18308
+ * new device; on an existing device it `add`s a capability the device lacks.
18309
+ * Replacing a capability the device already serves is refused
18310
+ * (`native-capability-replace`), not planned. `replace` remains on the claim
18311
+ * wire for a claim the hub already holds.
18359
18312
  */
18360
18313
  var CompositionFeatureModeSchema = _enum([
18361
18314
  "new",
@@ -20269,12 +20222,12 @@ method(object({ deviceId: number().int().nonnegative() }), DeviceAdminLinkSchema
20269
20222
  * providers detect renames by matching `uniqueId` and patch `id`
20270
20223
  * in-place so dispatch keeps working.
20271
20224
  *
20272
- * **Rendering metadata is NOT stored here.** Display fields (`unit`,
20273
- * `precision`) live in the cap STATUS SLICE so they flow live with every
20274
- * state push and are the single source of truth. The `device_class` tag is
20275
- * an HA-specific input that is consumed at the discovery boundary
20276
- * (`ha-domain-mapping`) to derive the CamStack `DeviceRole`; it is never
20277
- * stored after that point.
20225
+ * **Display unit and precision are NOT stored here.** They are a read-time
20226
+ * preference (D944). A polymorphic reading stores its `quantity` here once,
20227
+ * at adopt, and a date/time value stores `dateTimeShape` the same way —
20228
+ * neither is repeated on each sample. The `device_class` tag is an
20229
+ * HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
20230
+ * to derive the CamStack `DeviceRole`; it is never stored after that point.
20278
20231
  *
20279
20232
  * The `raw` escape hatch carries provider-specific attributes that don't
20280
20233
  * fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
@@ -20288,6 +20241,14 @@ var SourceInfoSchema = object({
20288
20241
  /** Immutable upstream identifier when available (HA `unique_id`, … ).
20289
20242
  * Used to detect rename when `id` changes. */
20290
20243
  uniqueId: string().optional(),
20244
+ /** Quantity of a polymorphic reading, written once at adopt (D944). */
20245
+ quantity: ReadingQuantitySchema.optional(),
20246
+ /** Date / time / datetime shape of a string reading, written once at adopt. */
20247
+ dateTimeShape: _enum([
20248
+ "date",
20249
+ "time",
20250
+ "datetime"
20251
+ ]).optional(),
20291
20252
  /** Provider-specific extras that don't fit the structured slots.
20292
20253
  * Whitelist what goes here per provider — do NOT dump entire HA
20293
20254
  * attribute blobs (would explode the meta JSON column). */
@@ -41200,7 +41161,8 @@ method(object({
41200
41161
  "enum"
41201
41162
  ]),
41202
41163
  enumValues: array(string()).optional(),
41203
- item: boolean().optional()
41164
+ item: boolean().optional(),
41165
+ quantity: ReadingQuantitySchema.optional()
41204
41166
  })).readonly(),
41205
41167
  itemArray: object({
41206
41168
  path: string(),
@@ -45006,8 +44968,8 @@ var MaskPolygonShapeSchema = object({
45006
44968
  /** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
45007
44969
  var MaskGridShapeSchema = object({
45008
44970
  kind: literal("grid"),
45009
- gridWidth: number(),
45010
- gridHeight: number(),
44971
+ gridWidth: number().meta(readingMeta("none")),
44972
+ gridHeight: number().meta(readingMeta("none")),
45011
44973
  cells: array(boolean())
45012
44974
  });
45013
44975
  discriminatedUnion("kind", [
@@ -56401,46 +56363,27 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
56401
56363
  }), LogStreamEntrySchema, { kind: "subscription" });
56402
56364
  object({
56403
56365
  /** Carbon dioxide concentration in ppm. */
56404
- co2Ppm: number().min(0).optional(),
56366
+ co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
56405
56367
  /** Total volatile organic compounds in ppb. */
56406
- vocPpb: number().min(0).optional(),
56368
+ vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
56407
56369
  /** Particulate matter ≤ 2.5 μm in µg/m³. */
56408
- pm25: number().min(0).optional(),
56370
+ pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
56409
56371
  /** Particulate matter ≤ 10 μm in µg/m³. */
56410
- pm10: number().min(0).optional(),
56372
+ pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
56411
56373
  /** Composite AQI value (typically 0..500). */
56412
- aqi: number().optional(),
56374
+ aqi: number().optional().meta(readingMeta("index")),
56413
56375
  /** Ms epoch when the slice was last updated. */
56414
- lastFetchedAt: number(),
56415
- /** Live display unit of the single metric this slice carries (e.g. HA
56416
- * `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
56417
- * upstream `sensor.*` entity surfaces ONE device_class, so one unit
56418
- * per slice is unambiguous. */
56419
- unit: string().optional(),
56420
- /** Suggested decimal places for numeric display.
56421
- * Populated live from the upstream source when provided (e.g. HA
56422
- * `attributes.suggested_display_precision`). Falls back to
56423
- * auto-formatting when absent. */
56424
- precision: number().int().min(0).max(10).optional()
56376
+ lastFetchedAt: number()
56425
56377
  });
56426
56378
  DeviceType.Sensor;
56427
56379
  object({
56428
- /** Current illuminance in lux (lx). */
56429
- lux: number().min(0).meta({
56430
- unit: "lx",
56380
+ /** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
56381
+ lux: number().min(0).nullable().meta(readingMeta("illuminance", {
56382
+ precision: 0,
56431
56383
  label: "Illuminance"
56432
- }),
56384
+ })),
56433
56385
  /** Ms epoch when the slice was last updated. */
56434
- lastFetchedAt: number(),
56435
- /** Live display unit from the upstream source (e.g. HA
56436
- * `attributes.unit_of_measurement`). The UI prefers this over the
56437
- * role's canonical unit. Absent → fall back to the canonical unit. */
56438
- unit: string().optional(),
56439
- /** Suggested decimal places for numeric display.
56440
- * Populated live from the upstream source when provided (e.g. HA
56441
- * `attributes.suggested_display_precision`). Falls back to
56442
- * auto-formatting when absent. */
56443
- precision: number().int().min(0).max(10).optional()
56386
+ lastFetchedAt: number()
56444
56387
  });
56445
56388
  DeviceType.Sensor;
56446
56389
  /**
@@ -56585,10 +56528,7 @@ var BatteryStatusSchema = object({
56585
56528
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
56586
56529
  * declines to band a non-finite reading, which is the correct shape.
56587
56530
  */
56588
- percentage: number().min(0).max(100).nullable().meta({
56589
- unit: "%",
56590
- label: "Charge"
56591
- }),
56531
+ percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
56592
56532
  /**
56593
56533
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
56594
56534
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -56615,7 +56555,14 @@ var BatteryStatusSchema = object({
56615
56555
  */
56616
56556
  sleeping: boolean().meta({ label: "Sleeping" }),
56617
56557
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
56618
- lastUpdated: number(),
56558
+ lastUpdated: number().meta(readingMeta("none")),
56559
+ /**
56560
+ * Battery voltage in volts, for a cell the firmware reports as voltage
56561
+ * rather than a percentage (Ecowitt capacitor packs). Absent on cameras
56562
+ * that only report charge. A second reading on this cap, not a second
56563
+ * `numeric-sensor` on the same device.
56564
+ */
56565
+ volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
56619
56566
  /**
56620
56567
  * Ms epoch of the last time the device PROVED it was reachable — a
56621
56568
  * completed firmware round-trip, an observed wake, or an inbound push
@@ -56675,7 +56622,7 @@ object({
56675
56622
  DeviceType.Sensor;
56676
56623
  object({
56677
56624
  /** Current level as 0..100 inclusive. Firmware-reported. */
56678
- percentage: number().min(0).max(100),
56625
+ percentage: number().min(0).max(100).meta(readingMeta("percentage")),
56679
56626
  /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
56680
56627
  lastChangedAt: number()
56681
56628
  });
@@ -57060,21 +57007,21 @@ object({
57060
57007
  /** Single setpoint in Celsius. Used by single-target modes
57061
57008
  * (heat / cool / dry). Null when the device is in a range mode
57062
57009
  * (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
57063
- target: number().nullable(),
57010
+ target: number().nullable().meta(readingMeta("temperature")),
57064
57011
  /** Upper bound of the dual setpoint in Celsius. Populated only
57065
57012
  * in `heat_cool` mode. */
57066
- targetHigh: number().nullable(),
57013
+ targetHigh: number().nullable().meta(readingMeta("temperature")),
57067
57014
  /** Lower bound of the dual setpoint in Celsius. Populated only
57068
57015
  * in `heat_cool` mode. */
57069
- targetLow: number().nullable(),
57016
+ targetLow: number().nullable().meta(readingMeta("temperature")),
57070
57017
  /** Target relative humidity (0..100). Null when the device has no
57071
57018
  * humidity control. */
57072
- targetHumidity: number().min(0).max(100).nullable(),
57019
+ targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
57073
57020
  /** Current measured temperature in Celsius. Read-only — pushed by
57074
57021
  * the device's internal sensor. */
57075
- currentTemp: number().nullable(),
57022
+ currentTemp: number().nullable().meta(readingMeta("temperature")),
57076
57023
  /** Current measured relative humidity (0..100). Read-only. */
57077
- currentHumidity: number().min(0).max(100).nullable(),
57024
+ currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
57078
57025
  /** Vertical louver swing on/off. Null when the device has no vertical
57079
57026
  * swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
57080
57027
  swingVertical: boolean().nullable(),
@@ -57082,11 +57029,11 @@ object({
57082
57029
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
57083
57030
  swingHorizontal: boolean().nullable(),
57084
57031
  /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
57085
- minTemp: number().nullable().optional(),
57032
+ minTemp: number().nullable().optional().meta(readingMeta("temperature")),
57086
57033
  /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
57087
- maxTemp: number().nullable().optional(),
57034
+ maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
57088
57035
  /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
57089
- tempStep: number().positive().nullable().optional(),
57036
+ tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
57090
57037
  /** Ms epoch when the slice was last updated (push or command). */
57091
57038
  lastFetchedAt: number()
57092
57039
  });
@@ -57165,17 +57112,17 @@ DeviceType.Thermostat, DeviceType.Climate, method(object({
57165
57112
  * modes to expose in the picker without a slice fetch.
57166
57113
  */
57167
57114
  var RgbTripletSchema = object({
57168
- r: number().int().min(0).max(255),
57169
- g: number().int().min(0).max(255),
57170
- b: number().int().min(0).max(255)
57115
+ r: number().int().min(0).max(255).meta(readingMeta("none")),
57116
+ g: number().int().min(0).max(255).meta(readingMeta("none")),
57117
+ b: number().int().min(0).max(255).meta(readingMeta("none"))
57171
57118
  });
57172
57119
  var HsvTripletSchema = object({
57173
57120
  /** Hue in degrees, 0..360. */
57174
- h: number().min(0).max(360),
57121
+ h: number().min(0).max(360).meta(readingMeta("none")),
57175
57122
  /** Saturation as 0..100 inclusive. */
57176
- s: number().min(0).max(100),
57123
+ s: number().min(0).max(100).meta(readingMeta("none")),
57177
57124
  /** Value/brightness as 0..100 inclusive. */
57178
- v: number().min(0).max(100)
57125
+ v: number().min(0).max(100).meta(readingMeta("none"))
57179
57126
  });
57180
57127
  var ColorInputSchema = discriminatedUnion("mode", [
57181
57128
  object({
@@ -57204,7 +57151,7 @@ object({
57204
57151
  /** Populated when `mode === 'hsv'`. */
57205
57152
  hsv: HsvTripletSchema.optional(),
57206
57153
  /** Populated when `mode === 'mired'`. */
57207
- mireds: number().int().optional(),
57154
+ mireds: number().int().optional().meta(readingMeta("colour-temperature")),
57208
57155
  /** Ms epoch of the last operator-driven change. */
57209
57156
  lastChangedAt: number()
57210
57157
  });
@@ -57328,18 +57275,12 @@ _enum([
57328
57275
  "empty",
57329
57276
  "full"
57330
57277
  ]);
57331
- /** Units a remaining-life counter is reported in. Closed: the panel renders
57332
- * each. */
57333
- var ConsumableRemainingUnitSchema = _enum([
57334
- "days",
57335
- "hours",
57336
- "cycles"
57337
- ]);
57338
- /** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
57339
- * days). `value` null = unknown (D393). */
57278
+ /** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
57279
+ var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
57280
+ /** Remaining life. `value` null = unknown (D393). Duration is seconds. */
57340
57281
  var ConsumableRemainingSchema = object({
57341
- value: number().min(0).nullable(),
57342
- unit: ConsumableRemainingUnitSchema
57282
+ value: number().min(0).nullable().meta(readingMeta("device")),
57283
+ kind: ConsumableRemainingKindSchema
57343
57284
  });
57344
57285
  var ConsumablesStatusSchema = object({
57345
57286
  items: array(object({
@@ -57348,7 +57289,7 @@ var ConsumablesStatusSchema = object({
57348
57289
  /** Display name. */
57349
57290
  label: string().min(1),
57350
57291
  /** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
57351
- level: number().min(0).max(100).nullable(),
57292
+ level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57352
57293
  /** The provider's discrete verdict when known (binary mode). */
57353
57294
  status: ConsumableStatusSchema.nullable(),
57354
57295
  /** What the item is — decides whether low or full is bad. Absent = `wear`. */
@@ -57365,7 +57306,7 @@ var ConsumablesStatusSchema = object({
57365
57306
  })),
57366
57307
  lastChangedAt: number()
57367
57308
  });
57368
- Object.values(DeviceType), [...ConsumableRemainingUnitSchema.options], [...ConsumableStatusSchema.options], [...ConsumableKindSchema.options], [...ConsumableFormSchema.options];
57309
+ Object.values(DeviceType), [...ConsumableRemainingKindSchema.options], [...ConsumableStatusSchema.options], [...ConsumableKindSchema.options], [...ConsumableFormSchema.options];
57369
57310
  Object.values(DeviceType), method(object({
57370
57311
  deviceId: number().int().nonnegative(),
57371
57312
  key: string().min(1)
@@ -57390,39 +57331,17 @@ object({
57390
57331
  /** Current value. Numeric kind → number; select/text/datetime → string.
57391
57332
  * Datetime values are ISO 8601 strings (full timestamp, date-only,
57392
57333
  * or time-only — HA accepts all three forms). */
57393
- value: union([number(), string()]),
57334
+ value: union([number().meta(readingMeta("device")), string()]),
57394
57335
  /** Acceptable values when `kind === 'select'`. Empty for other kinds. */
57395
57336
  options: array(string()),
57396
57337
  /** Ms epoch when the slice was last updated. */
57397
57338
  lastChangedAt: number(),
57398
- /** Display unit-of-measurement (e.g. '°C', '%', 'lx').
57399
- * Populated live from HA `attributes.unit_of_measurement` on each push.
57400
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57401
- unit: string().optional(),
57402
- /** Minimum allowed value. Populated live from HA `attributes.min`.
57403
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57404
- min: number().optional(),
57405
- /** Maximum allowed value. Populated live from HA `attributes.max`.
57406
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57407
- max: number().optional(),
57408
- /** Step increment. Populated live from HA `attributes.step`.
57409
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57410
- step: number().optional(),
57411
- /** Suggested decimal places for numeric display. Populated live from HA
57412
- * `attributes.suggested_display_precision`, or derived from `step` when the
57413
- * HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
57414
- * other kinds. Falls back to auto-formatting when absent. */
57415
- precision: number().int().min(0).max(10).optional(),
57416
- /** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
57417
- * 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
57418
- * (UI defaults to 'datetime'). Derived live from the device's HA domain
57419
- * (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
57420
- * `has_date`/`has_time` attributes on each push. */
57421
- format: _enum([
57422
- "date",
57423
- "time",
57424
- "datetime"
57425
- ]).optional()
57339
+ /** Minimum allowed value, in the device quantity's base unit. */
57340
+ min: number().optional().meta(readingMeta("device")),
57341
+ /** Maximum allowed value, in the device quantity's base unit. */
57342
+ max: number().optional().meta(readingMeta("device")),
57343
+ /** Step increment, in the device quantity's base unit. */
57344
+ step: number().optional().meta(readingMeta("device"))
57426
57345
  });
57427
57346
  var ControlSetValueInputSchema = discriminatedUnion("kind", [
57428
57347
  object({
@@ -57460,9 +57379,9 @@ object({
57460
57379
  ]),
57461
57380
  /** 0 = fully closed, 100 = fully open. Null when the device has
57462
57381
  * no intermediate position surface. */
57463
- position: number().min(0).max(100).nullable(),
57382
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57464
57383
  /** 0 = closed slats, 100 = open slats. Null when no tilt support. */
57465
- tiltPosition: number().min(0).max(100).nullable(),
57384
+ tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57466
57385
  /** Ms epoch when the slice was last updated. */
57467
57386
  lastChangedAt: number()
57468
57387
  });
@@ -57520,9 +57439,9 @@ var NormalizedRangeSchema$1 = object({
57520
57439
  object({
57521
57440
  mode: DayNightModeSchema,
57522
57441
  /** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
57523
- sensitivity: number().optional(),
57442
+ sensitivity: number().optional().meta(readingMeta("none")),
57524
57443
  /** Delay before the IR-cut filter flips, in seconds. */
57525
- switchDelaySec: number().optional(),
57444
+ switchDelaySec: number().optional().meta(readingMeta("duration")),
57526
57445
  lastFetchedAt: number()
57527
57446
  });
57528
57447
  /**
@@ -57677,11 +57596,11 @@ object({
57677
57596
  /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
57678
57597
  availablePrograms: array(string()),
57679
57598
  /** Seconds remaining on the active cycle, or null when not running / not known. */
57680
- remainingSeconds: number().int().nonnegative().nullable(),
57599
+ remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
57681
57600
  /** Seconds the delayed start is set to (time until the cycle starts), or null. */
57682
- delayedStartSeconds: number().int().nonnegative().nullable(),
57601
+ delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
57683
57602
  /** Cycle completion 0..100, or null when not applicable. */
57684
- progressPercent: number().min(0).max(100).nullable(),
57603
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57685
57604
  /** True when the door is reported open; null when the door state is not known. */
57686
57605
  doorOpen: boolean().nullable(),
57687
57606
  /** True when the door is latched (locked) during a cycle; null when not reported. */
@@ -57705,7 +57624,7 @@ object({
57705
57624
  /** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
57706
57625
  machineCareDue: boolean().nullable(),
57707
57626
  /** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
57708
- machineCareRemainingRuns: number().int().nonnegative().nullable(),
57627
+ machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
57709
57628
  /** Option states: null when the appliance does not expose the option or has not said. */
57710
57629
  halfLoad: boolean().nullable(),
57711
57630
  hygienePlus: boolean().nullable(),
@@ -57720,7 +57639,7 @@ object({
57720
57639
  /** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
57721
57640
  activeEvents: array(string()),
57722
57641
  /** Numeric error code when the appliance reports one; null otherwise. */
57723
- errorCode: number().nullable(),
57642
+ errorCode: number().nullable().meta(readingMeta("none")),
57724
57643
  /** Name of the raised alert-level event while the appliance is in error; null otherwise. */
57725
57644
  errorLabel: string().nullable(),
57726
57645
  /** Ms epoch of the last value received from the appliance; 0 = never. */
@@ -57866,24 +57785,14 @@ object({
57866
57785
  /** Ms epoch of the last press. null = never observed since this provider started. */
57867
57786
  lastPressedAt: number().nullable(),
57868
57787
  /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
57869
- pressCountSinceStart: number()
57788
+ pressCountSinceStart: number().meta(readingMeta("count"))
57870
57789
  });
57871
57790
  object({
57872
57791
  deviceId: number(),
57873
57792
  timestamp: number()
57874
57793
  });
57875
57794
  DeviceType.Button, DeviceType.Camera;
57876
- /**
57877
- * Enum-state sensor — a string value picked from a finite option set.
57878
- * Drives HA `sensor` entries with `state_class: enum` (HVAC action
57879
- * states, weather conditions, contact-source channels, mode strings).
57880
- * The option set is stable across observations; it is persisted in the
57881
- * device config blob (via the control slice at adoption) and is NOT
57882
- * stored in `sourceInfo`.
57883
- */
57884
- /** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
57885
- * enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
57886
- var EnumSensorDateTimeFormatSchema = _enum([
57795
+ _enum([
57887
57796
  "date",
57888
57797
  "time",
57889
57798
  "datetime"
@@ -57891,12 +57800,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
57891
57800
  object({
57892
57801
  value: string(),
57893
57802
  /**
57894
- * Set for `DateTimeSensor`-role sensors so the UI renders the ISO `value`
57895
- * as a locale date/time/datetime; absent for plain enum sensors. HA
57896
- * `device_class=timestamp` → `'datetime'`, `device_class=date` → `'date'`,
57897
- * a time-only sensor → `'time'`.
57803
+ * Set when `value` is a timestamp rather than an enum state, so history
57804
+ * does not learn ISO strings as states. The display shape (date / time /
57805
+ * datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
57898
57806
  */
57899
- format: EnumSensorDateTimeFormatSchema.optional(),
57807
+ timestamp: boolean().optional(),
57900
57808
  /** Ms epoch when the slice was last updated. */
57901
57809
  lastFetchedAt: number()
57902
57810
  });
@@ -57915,13 +57823,13 @@ var EventFireSchema = object({
57915
57823
  deviceId: number(),
57916
57824
  eventType: string(),
57917
57825
  data: record(string(), unknown()).nullable(),
57918
- timestamp: number(),
57919
- seq: number()
57826
+ timestamp: number().meta(readingMeta("none")),
57827
+ seq: number().meta(readingMeta("count"))
57920
57828
  });
57921
57829
  object({
57922
57830
  eventTypes: array(string()),
57923
57831
  lastEvent: EventFireSchema.nullable(),
57924
- eventCountSinceStart: number()
57832
+ eventCountSinceStart: number().meta(readingMeta("count"))
57925
57833
  });
57926
57834
  DeviceType.EventEmitter;
57927
57835
  var IdentitySchema = object({
@@ -58310,10 +58218,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
58310
58218
  object({
58311
58219
  /** Active speed as 0..100 inclusive. Null when the device has no
58312
58220
  * speed surface (single-speed fan). */
58313
- percentage: number().min(0).max(100).nullable(),
58221
+ percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58314
58222
  /** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
58315
58223
  * a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
58316
- percentageStep: number().positive().optional(),
58224
+ percentageStep: number().positive().optional().meta(readingMeta("percentage")),
58317
58225
  /** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
58318
58226
  * — empty string when no preset is active or supported. */
58319
58227
  preset: string(),
@@ -58395,7 +58303,7 @@ var FeatureProbeStatusSchema = object({
58395
58303
  /** Camera/firmware model string. `null` when the firmware doesn't expose it. */
58396
58304
  model: string().nullable(),
58397
58305
  /** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
58398
- channelCount: number().nullable(),
58306
+ channelCount: number().nullable().meta(readingMeta("count")),
58399
58307
  /**
58400
58308
  * Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
58401
58309
  * completes — drivers' `getAccessoryChildren()` should treat zero as
@@ -58429,7 +58337,7 @@ object({
58429
58337
  DeviceType.Sensor;
58430
58338
  object({
58431
58339
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
58432
- position: number().min(0).max(100).nullable(),
58340
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58433
58341
  /** Ms epoch when the slice was last updated (push or poll). */
58434
58342
  lastFetchedAt: number()
58435
58343
  });
@@ -58438,9 +58346,9 @@ object({
58438
58346
  /** Whether the humidifier is currently on. */
58439
58347
  on: boolean(),
58440
58348
  /** Current measured relative humidity (0..100). Null when not reported. */
58441
- currentHumidity: number().min(0).max(100).nullable(),
58349
+ currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
58442
58350
  /** Target relative humidity (0..100). Null when no setpoint surface. */
58443
- targetHumidity: number().min(0).max(100).nullable(),
58351
+ targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
58444
58352
  /** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
58445
58353
  * device has no mode surface. */
58446
58354
  mode: string().nullable(),
@@ -58450,9 +58358,9 @@ object({
58450
58358
  * `idle` / `off`). Null when the device doesn't report it. */
58451
58359
  action: string().nullable(),
58452
58360
  /** HA `min_humidity` attribute. Null → UI uses 0. */
58453
- minHumidity: number().nullable(),
58361
+ minHumidity: number().nullable().meta(readingMeta("humidity")),
58454
58362
  /** HA `max_humidity` attribute. Null → UI uses 100. */
58455
- maxHumidity: number().nullable(),
58363
+ maxHumidity: number().nullable().meta(readingMeta("humidity")),
58456
58364
  /** Ms epoch when the slice was last updated. */
58457
58365
  lastChangedAt: number()
58458
58366
  });
@@ -58476,22 +58384,13 @@ DeviceType.Humidifier, method(object({
58476
58384
  auth: "protected"
58477
58385
  });
58478
58386
  object({
58479
- /** Current relative humidity, 0..100. */
58480
- percent: number().min(0).max(100).meta({
58481
- unit: "%",
58387
+ /** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
58388
+ percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
58389
+ precision: 0,
58482
58390
  label: "Humidity"
58483
- }),
58391
+ })),
58484
58392
  /** Ms epoch when the slice was last updated. */
58485
- lastFetchedAt: number(),
58486
- /** Live display unit from the upstream source (e.g. HA
58487
- * `attributes.unit_of_measurement`). The UI prefers this over the
58488
- * role's canonical unit. Absent → fall back to the canonical unit. */
58489
- unit: string().optional(),
58490
- /** Suggested decimal places for numeric display.
58491
- * Populated live from the upstream source when provided (e.g. HA
58492
- * `attributes.suggested_display_precision`). Falls back to
58493
- * auto-formatting when absent. */
58494
- precision: number().int().min(0).max(10).optional()
58393
+ lastFetchedAt: number()
58495
58394
  });
58496
58395
  DeviceType.Sensor, DeviceType.Thermostat;
58497
58396
  object({
@@ -58499,7 +58398,7 @@ object({
58499
58398
  * entity exposes no `entity_picture` (yet). */
58500
58399
  url: string().nullable(),
58501
58400
  /** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
58502
- lastUpdated: number().nullable()
58401
+ lastUpdated: number().nullable().meta(readingMeta("none"))
58503
58402
  });
58504
58403
  DeviceType.Image;
58505
58404
  /**
@@ -58555,19 +58454,19 @@ var NormalizedRangeSchema = object({
58555
58454
  });
58556
58455
  object({
58557
58456
  /** Normalized 0–100. */
58558
- brightness: number().optional(),
58457
+ brightness: number().optional().meta(readingMeta("none")),
58559
58458
  /** Normalized 0–100. */
58560
- contrast: number().optional(),
58459
+ contrast: number().optional().meta(readingMeta("none")),
58561
58460
  /** Normalized 0–100. */
58562
- saturation: number().optional(),
58461
+ saturation: number().optional().meta(readingMeta("none")),
58563
58462
  /** Normalized 0–100. */
58564
- sharpness: number().optional(),
58463
+ sharpness: number().optional().meta(readingMeta("none")),
58565
58464
  mirror: boolean().optional(),
58566
58465
  flip: boolean().optional(),
58567
58466
  rotate: ImageRotateSchema.optional(),
58568
58467
  whiteBalance: WhiteBalanceModeSchema.optional(),
58569
58468
  /** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
58570
- warmth: number().optional(),
58469
+ warmth: number().optional().meta(readingMeta("none")),
58571
58470
  exposureMode: ExposureModeSchema.optional(),
58572
58471
  backlightMode: BacklightModeSchema.optional(),
58573
58472
  lastFetchedAt: number()
@@ -58812,10 +58711,10 @@ var IntercomAbilitySchema = object({
58812
58711
  /** Codecs accepted by firmware, in preference order. Driver-authored strings. */
58813
58712
  codecs: array(string()),
58814
58713
  /** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
58815
- sampleRate: number(),
58714
+ sampleRate: number().meta(readingMeta("none")),
58816
58715
  duplex: _enum(["half", "full"]),
58817
58716
  /** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
58818
- maxBacklogMs: number()
58717
+ maxBacklogMs: number().meta(readingMeta("duration-ms"))
58819
58718
  });
58820
58719
  var IntercomStatusSchema = object({
58821
58720
  /** True while a talk session is open. */
@@ -58926,13 +58825,13 @@ object({
58926
58825
  activity: LawnMowerActivitySchema,
58927
58826
  /** 0..100 battery percentage. Null when the device has no battery
58928
58827
  * reading. */
58929
- batteryLevel: number().min(0).max(100).nullable(),
58828
+ batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58930
58829
  /** 0..100 mowing-completion percentage of the current task, or null when no
58931
58830
  * task is active / progress is unavailable. */
58932
- progressPercent: number().min(0).max(100).nullable(),
58831
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58933
58832
  /** Current device/event code (dynamic — mostly status, sometimes an error),
58934
58833
  * or null when unknown. */
58935
- currentCode: number().nullable(),
58834
+ currentCode: number().nullable().meta(readingMeta("none")),
58936
58835
  /** Human label for {@link currentCode}, or null when undecodable. */
58937
58836
  currentCodeLabel: string().nullable(),
58938
58837
  /** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
@@ -59394,7 +59293,7 @@ object({
59394
59293
  state: MediaPlayerStateSchema,
59395
59294
  /** Volume as 0..100 inclusive. Null when the device has no volume
59396
59295
  * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
59397
- volumeLevel: number().min(0).max(100).nullable(),
59296
+ volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59398
59297
  /** Mute toggle distinct from volume=0. Null when no mute surface.
59399
59298
  * Pair with `DeviceFeature.MediaPlayerMute`. */
59400
59299
  isMuted: boolean().nullable(),
@@ -59407,10 +59306,10 @@ object({
59407
59306
  currentMedia: MediaInfoSchema.nullable(),
59408
59307
  /** Current playback position in ms. Null when not seekable or
59409
59308
  * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
59410
- positionMs: number().int().nonnegative().nullable(),
59309
+ positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
59411
59310
  /** Total duration of the current media in ms. Null when unknown
59412
59311
  * (live stream) or nothing is playing. */
59413
- durationMs: number().int().nonnegative().nullable(),
59312
+ durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
59414
59313
  /** Shuffle toggle. Null when no shuffle surface.
59415
59314
  * Pair with `DeviceFeature.MediaPlayerShuffle`. */
59416
59315
  shuffle: boolean().nullable(),
@@ -59710,7 +59609,7 @@ object({
59710
59609
  * it, every provider seeded it at boot, and every session left it
59711
59610
  * `detected: true` after its detach.
59712
59611
  */
59713
- autoClearAfterMs: number().nullable()
59612
+ autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
59714
59613
  });
59715
59614
  /**
59716
59615
  * Payload of `motion.onMotionChanged` event + the corresponding bus
@@ -59760,7 +59659,7 @@ var MotionZoneRegionSchema = object({
59760
59659
  });
59761
59660
  object({
59762
59661
  enabled: boolean(),
59763
- sensitivity: number(),
59662
+ sensitivity: number().meta(readingMeta("none")),
59764
59663
  /** Grid region(s). Today exactly one `grid` shape. */
59765
59664
  regions: array(MotionZoneRegionSchema),
59766
59665
  lastFetchedAt: number()
@@ -60064,7 +59963,7 @@ var NavigationStatusSchema = object({
60064
59963
  following: boolean(),
60065
59964
  flash: boolean(),
60066
59965
  lightMode: NavigationLightModeSchema,
60067
- lightLevel: number().min(40).max(100),
59966
+ lightLevel: number().min(40).max(100).meta(readingMeta("none")),
60068
59967
  /** Ms epoch when the slice was last updated. */
60069
59968
  lastChangedAt: number()
60070
59969
  });
@@ -60110,13 +60009,13 @@ var NetworkLinkStatusSchema = object({
60110
60009
  * one whose reading has not landed must not be drawn at 0 %. Consumers
60111
60010
  * SKIP a null rather than coerce it.
60112
60011
  */
60113
- signalPercent: number().min(0).max(100).nullable(),
60012
+ signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60114
60013
  /** Raw received signal strength in dBm, when the firmware reports one. */
60115
- rssiDbm: number().optional(),
60014
+ rssiDbm: number().optional().meta(readingMeta("signal-level")),
60116
60015
  /** Network name of a wireless link, when the firmware reports it. */
60117
60016
  ssid: string().optional(),
60118
60017
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
60119
- lastUpdated: number()
60018
+ lastUpdated: number().meta(readingMeta("none"))
60120
60019
  });
60121
60020
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.Light, DeviceType.Lock, DeviceType.Siren, object({
60122
60021
  deviceId: number(),
@@ -60435,7 +60334,7 @@ object({
60435
60334
  lastError: string().nullable(),
60436
60335
  /** Number of deliveries currently buffered server-side (rate-limited
60437
60336
  * notifiers may queue). 0 when the provider sends synchronously. */
60438
- queueDepth: number().int().nonnegative(),
60337
+ queueDepth: number().int().nonnegative().meta(readingMeta("count")),
60439
60338
  /** Per-feature capability matrix — authoritative source for the
60440
60339
  * compose-form field gating. */
60441
60340
  supports: NotifierSupportsSchema
@@ -60485,15 +60384,8 @@ DeviceType.Notifier, method(NotifierSendInputSchema, NotifierSendResultSchema, {
60485
60384
  acceptedAt: number()
60486
60385
  });
60487
60386
  object({
60488
- value: number(),
60489
- /** Display unit-of-measurement (e.g. 'dBm', 's', 'rpm', 'steps').
60490
- * Populated live from the upstream source on each state push.
60491
- * Absent for unitless sensors. */
60492
- unit: string().optional(),
60493
- /** Suggested decimal places for numeric display.
60494
- * Populated live from the upstream source when provided.
60495
- * Falls back to auto-formatting when absent. */
60496
- precision: number().int().min(0).max(10).optional(),
60387
+ /** Reading in the device quantity's base unit. `null` when refused (D393). */
60388
+ value: number().nullable().meta(readingMeta("device")),
60497
60389
  /** Ms epoch when the slice was last updated. */
60498
60390
  lastFetchedAt: number()
60499
60391
  });
@@ -60783,23 +60675,26 @@ object({
60783
60675
  /** Food currently in the bowl (grams). Null when the device has not
60784
60676
  * reported a reading yet. On dual-hopper models this is the combined
60785
60677
  * bowl reading; per-hopper levels live in `food1`/`food2`. */
60786
- foodLevel: number().nullable(),
60678
+ foodLevel: number().nullable().meta(readingMeta("none")),
60787
60679
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
60788
60680
  * single-hopper models. */
60789
- food1: number().nullable(),
60681
+ food1: number().nullable().meta(readingMeta("none")),
60790
60682
  /** Hopper-2 food level (grams) on dual-hopper feeders; null on
60791
60683
  * single-hopper models. */
60792
- food2: number().nullable(),
60684
+ food2: number().nullable().meta(readingMeta("none")),
60793
60685
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
60794
60686
  * (`device_class: problem`, on = low). True when the bowl is empty /
60795
60687
  * below the feeder's low threshold. */
60796
60688
  lowFood: boolean(),
60797
60689
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
60798
60690
  * device has no battery reading. */
60799
- batteryPower: number().min(0).max(100).nullable(),
60800
- /** Days of desiccant life remaining. Null when the model has no
60801
- * desiccant sensor. */
60802
- desiccantLeftDays: number().nullable(),
60691
+ batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60692
+ /**
60693
+ * Desiccant life remaining, stored in seconds (duration base, D944).
60694
+ * Providers convert from the days the feeder reports. Null when the
60695
+ * model has no desiccant sensor.
60696
+ */
60697
+ desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
60803
60698
  /** True while a feed is in progress. */
60804
60699
  feeding: boolean(),
60805
60700
  /** Decoded connectivity / power status (HA petkit device-status enum).
@@ -60810,7 +60705,7 @@ object({
60810
60705
  * with `errorCode` for consumers that want the raw integer. */
60811
60706
  error: string().nullable(),
60812
60707
  /** Raw device error code (0 / null = no error). */
60813
- errorCode: number().nullable(),
60708
+ errorCode: number().nullable().meta(readingMeta("none")),
60814
60709
  /** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
60815
60710
  isDualHopper: boolean(),
60816
60711
  /** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
@@ -60820,7 +60715,7 @@ object({
60820
60715
  /** Play a chime when dispensing. */
60821
60716
  feedSound: boolean(),
60822
60717
  /** Speaker / prompt volume level (device-scaled integer). */
60823
- volume: number(),
60718
+ volume: number().meta(readingMeta("none")),
60824
60719
  /** Ms epoch when the slice was last refreshed from the cloud. */
60825
60720
  lastFetchedAt: number()
60826
60721
  });
@@ -61257,36 +61152,19 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
61257
61152
  });
61258
61153
  object({
61259
61154
  /** Instantaneous power draw in watts. */
61260
- watts: number().optional().meta({
61261
- unit: "W",
61262
- label: "Power"
61263
- }),
61155
+ watts: number().optional().meta(readingMeta("power", { label: "Power" })),
61264
61156
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
61265
- kwhTotal: number().optional().meta({
61266
- unit: "kWh",
61267
- label: "Energy"
61268
- }),
61157
+ kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
61269
61158
  /** Voltage in volts. */
61270
- volts: number().optional(),
61159
+ volts: number().optional().meta(readingMeta("voltage")),
61271
61160
  /** Current in amperes. */
61272
- amps: number().optional(),
61273
- /** Apparent power in volt-amperes (W × power-factor reciprocal). */
61274
- va: number().optional(),
61161
+ amps: number().optional().meta(readingMeta("current")),
61162
+ /** Apparent power in volt-amperes. */
61163
+ va: number().optional().meta(readingMeta("apparent-power")),
61275
61164
  /** Power factor (0..1) if reported. */
61276
- powerFactor: number().min(0).max(1).optional(),
61165
+ powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
61277
61166
  /** Ms epoch when the slice was last updated. */
61278
- lastFetchedAt: number(),
61279
- /** Live display unit of the single metric this slice carries (e.g. HA
61280
- * `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
61281
- * Each upstream `sensor.*` entity surfaces ONE device_class, so one
61282
- * unit per slice is unambiguous. The UI prefers this over the role's
61283
- * canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
61284
- unit: string().optional(),
61285
- /** Suggested decimal places for numeric display.
61286
- * Populated live from the upstream source when provided (e.g. HA
61287
- * `attributes.suggested_display_precision`). Falls back to
61288
- * auto-formatting when absent. */
61289
- precision: number().int().min(0).max(10).optional()
61167
+ lastFetchedAt: number()
61290
61168
  });
61291
61169
  DeviceType.Sensor;
61292
61170
  /**
@@ -61307,11 +61185,11 @@ DeviceType.Sensor;
61307
61185
  */
61308
61186
  var GpsLocationSchema = object({
61309
61187
  /** Latitude in decimal degrees, -90..90. */
61310
- latitude: number().min(-90).max(90),
61188
+ latitude: number().min(-90).max(90).meta(readingMeta("angle")),
61311
61189
  /** Longitude in decimal degrees, -180..180. */
61312
- longitude: number().min(-180).max(180),
61190
+ longitude: number().min(-180).max(180).meta(readingMeta("angle")),
61313
61191
  /** Reported accuracy in meters (lower = better). */
61314
- accuracyMeters: number().nonnegative()
61192
+ accuracyMeters: number().nonnegative().meta(readingMeta("length"))
61315
61193
  });
61316
61194
  object({
61317
61195
  /** `home` / `not_home` / any user-defined zone name. */
@@ -61323,25 +61201,16 @@ object({
61323
61201
  gps: GpsLocationSchema.nullable(),
61324
61202
  /** Optional battery level of the tracking device (0..100). Null
61325
61203
  * when not reported. */
61326
- batteryPercent: number().min(0).max(100).nullable(),
61204
+ batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61327
61205
  /** Ms epoch when the slice was last updated. */
61328
61206
  lastChangedAt: number()
61329
61207
  });
61330
61208
  DeviceType.Presence;
61331
61209
  object({
61332
- /** Current pressure in hPa. */
61333
- hpa: number(),
61210
+ /** Current pressure in hPa. `null` when the sample was refused (D393). */
61211
+ hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
61334
61212
  /** Ms epoch when the slice was last updated. */
61335
- lastFetchedAt: number(),
61336
- /** Live display unit from the upstream source (e.g. HA
61337
- * `attributes.unit_of_measurement`). The UI prefers this over the
61338
- * role's canonical unit. Absent → fall back to the canonical unit. */
61339
- unit: string().optional(),
61340
- /** Suggested decimal places for numeric display.
61341
- * Populated live from the upstream source when provided (e.g. HA
61342
- * `attributes.suggested_display_precision`). Falls back to
61343
- * auto-formatting when absent. */
61344
- precision: number().int().min(0).max(10).optional()
61213
+ lastFetchedAt: number()
61345
61214
  });
61346
61215
  DeviceType.Sensor;
61347
61216
  /**
@@ -61674,8 +61543,8 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
61674
61543
  }), _void(), { kind: "mutation" });
61675
61544
  var PtzAutotrackSettingsSchema = object({
61676
61545
  targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
61677
- stopDelaySeconds: number().int().min(0).max(300),
61678
- disappearDelaySeconds: number().int().min(0).max(300)
61546
+ stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
61547
+ disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
61679
61548
  });
61680
61549
  /**
61681
61550
  * One option the camera advertises as a valid `targetType`. Drivers
@@ -61732,7 +61601,7 @@ object({
61732
61601
  /** null = unknown — never coerced to false (D393). */
61733
61602
  running: boolean().nullable(),
61734
61603
  /** Epoch ms the current boolean began; null while `running` is null. */
61735
- since: number().nullable(),
61604
+ since: number().nullable().meta(readingMeta("none")),
61736
61605
  source: RunningStateSourceSchema.meta({ label: "Source" }),
61737
61606
  phase: string().nullable(),
61738
61607
  /** null until a reading has been published (D393). */
@@ -62930,9 +62799,9 @@ var RecordTriggerSchema = _enum([
62930
62799
  */
62931
62800
  var RecordWindowSchema = object({
62932
62801
  trigger: RecordTriggerSchema,
62933
- day: number().int().min(0).max(6),
62934
- startMinute: number().int().min(0).max(1439),
62935
- endMinute: number().int().min(1).max(1440)
62802
+ day: number().int().min(0).max(6).meta(readingMeta("none")),
62803
+ startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
62804
+ endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
62936
62805
  });
62937
62806
  /** Status of one physical volume, as the camera itself describes it. */
62938
62807
  var OnboardStorageVolumeSchema = object({
@@ -63001,10 +62870,10 @@ object({
63001
62870
  /** Master "record to the card at all" switch. */
63002
62871
  enabled: boolean().nullable(),
63003
62872
  overwriteWhenFull: boolean().nullable(),
63004
- preRecordSec: number().nullable(),
63005
- postRecordSec: number().nullable(),
62873
+ preRecordSec: number().nullable().meta(readingMeta("duration")),
62874
+ postRecordSec: number().nullable().meta(readingMeta("duration")),
63006
62875
  /** Length of one recorded file, in minutes. */
63007
- segmentMinutes: number().nullable(),
62876
+ segmentMinutes: number().nullable().meta(readingMeta("none")),
63008
62877
  /** The primary track's weekly windows, flattened. */
63009
62878
  windows: array(RecordWindowSchema),
63010
62879
  /**
@@ -63020,7 +62889,7 @@ object({
63020
62889
  * provider reporting a non-zero count also reports the schedule as not
63021
62890
  * writable.
63022
62891
  */
63023
- unreadableWindows: number(),
62892
+ unreadableWindows: number().meta(readingMeta("count")),
63024
62893
  /**
63025
62894
  * The camera is scheduled to record and has NO usable storage.
63026
62895
  *
@@ -63657,7 +63526,7 @@ var CamStreamDescriptorSchema = object({
63657
63526
  url: string().optional(),
63658
63527
  codec: string().optional(),
63659
63528
  resolution: CamStreamResolutionSchema.optional(),
63660
- fps: number().positive().optional(),
63529
+ fps: number().positive().optional().meta(readingMeta("rate")),
63661
63530
  label: string().optional(),
63662
63531
  /** Device-level features (e.g. `battery-operated`) — drives broker policy. */
63663
63532
  deviceFeatures: array(string()).optional(),
@@ -63687,18 +63556,18 @@ var StreamProfileSchema = _enum([
63687
63556
  ]);
63688
63557
  /** The current encoding config of one stream profile. */
63689
63558
  var StreamProfileConfigSchema = object({
63690
- width: number(),
63691
- height: number(),
63559
+ width: number().meta(readingMeta("none")),
63560
+ height: number().meta(readingMeta("none")),
63692
63561
  codec: _enum(["h264", "h265"]),
63693
- framerate: number(),
63694
- bitrate: number(),
63562
+ framerate: number().meta(readingMeta("rate")),
63563
+ bitrate: number().meta(readingMeta("bitrate")),
63695
63564
  bitrateMode: _enum(["vbr", "cbr"]).optional(),
63696
63565
  encoderProfile: _enum([
63697
63566
  "high",
63698
63567
  "main",
63699
63568
  "baseline"
63700
63569
  ]).optional(),
63701
- gop: number().optional(),
63570
+ gop: number().optional().meta(readingMeta("none")),
63702
63571
  /**
63703
63572
  * Whether THIS profile currently carries an audio track. READ-ONLY here.
63704
63573
  *
@@ -64341,23 +64210,13 @@ object({
64341
64210
  });
64342
64211
  DeviceType.Sensor;
64343
64212
  object({
64344
- /** Current temperature in Celsius. */
64345
- celsius: number().meta({
64346
- unit: "°C",
64213
+ /** Current temperature in Celsius. `null` when the sample was refused (D393). */
64214
+ celsius: number().nullable().meta(readingMeta("temperature", {
64215
+ precision: 1,
64347
64216
  label: "Temperature"
64348
- }),
64217
+ })),
64349
64218
  /** Ms epoch when the slice was last updated (push or poll). */
64350
- lastFetchedAt: number(),
64351
- /** Live display unit from the upstream source (e.g. HA
64352
- * `attributes.unit_of_measurement`). The UI prefers this over the
64353
- * role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
64354
- * Absent → the UI falls back to the role's canonical unit. */
64355
- unit: string().optional(),
64356
- /** Suggested decimal places for numeric display.
64357
- * Populated live from the upstream source when provided (e.g. HA
64358
- * `attributes.suggested_display_precision`). Falls back to
64359
- * auto-formatting when absent. */
64360
- precision: number().int().min(0).max(10).optional()
64219
+ lastFetchedAt: number()
64361
64220
  });
64362
64221
  DeviceType.Sensor, DeviceType.Thermostat;
64363
64222
  /**
@@ -64609,16 +64468,16 @@ object({
64609
64468
  ]),
64610
64469
  /** 0..100 battery percentage. Null when the device has no battery
64611
64470
  * reading. */
64612
- batteryLevel: number().min(0).max(100).nullable(),
64471
+ batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64613
64472
  /** Current fan-speed token (provider-verbatim). Null when unknown or
64614
64473
  * the vacuum has no speed control. */
64615
64474
  fanSpeed: string().nullable(),
64616
64475
  /** Speed tokens the hardware accepts — drives the UI selector. */
64617
64476
  availableFanSpeeds: array(string()),
64618
64477
  /** 0..100 cleaning-completion percentage of the current task, or null. */
64619
- progressPercent: number().min(0).max(100).nullable(),
64478
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64620
64479
  /** Current error code (0 / null = no error). */
64621
- errorCode: number().nullable(),
64480
+ errorCode: number().nullable().meta(readingMeta("none")),
64622
64481
  /** Human label for {@link errorCode}, or null when none / undecodable. */
64623
64482
  errorLabel: string().nullable(),
64624
64483
  /** Ms epoch when the slice was last updated. */
@@ -64657,7 +64516,7 @@ object({
64657
64516
  ]),
64658
64517
  /** 0 = fully closed, 100 = fully open. Null when the device has no
64659
64518
  * intermediate position surface. */
64660
- position: number().min(0).max(100).nullable(),
64519
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64661
64520
  /** Ms epoch when the slice was last updated. */
64662
64521
  lastChangedAt: number()
64663
64522
  });
@@ -64685,9 +64544,9 @@ object({
64685
64544
  DeviceType.Sensor;
64686
64545
  object({
64687
64546
  /** Current measured temperature. Null when not reported. */
64688
- currentTemp: number().nullable(),
64547
+ currentTemp: number().nullable().meta(readingMeta("temperature")),
64689
64548
  /** Target temperature setpoint. Null when no setpoint surface. */
64690
- targetTemp: number().nullable(),
64549
+ targetTemp: number().nullable().meta(readingMeta("temperature")),
64691
64550
  /** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
64692
64551
  * `heat_pump` / `high_demand` / `performance` / `off`). Null when the
64693
64552
  * device reports an unknown state. */
@@ -64698,9 +64557,9 @@ object({
64698
64557
  * has no away surface. */
64699
64558
  away: boolean().nullable(),
64700
64559
  /** HA `min_temp` attribute. Null when not reported. */
64701
- minTemp: number().nullable(),
64560
+ minTemp: number().nullable().meta(readingMeta("temperature")),
64702
64561
  /** HA `max_temp` attribute. Null when not reported. */
64703
- maxTemp: number().nullable(),
64562
+ maxTemp: number().nullable().meta(readingMeta("temperature")),
64704
64563
  /** Ms epoch when the slice was last updated. */
64705
64564
  lastChangedAt: number()
64706
64565
  });
@@ -64724,25 +64583,18 @@ DeviceType.WaterHeater, method(object({
64724
64583
  auth: "protected"
64725
64584
  });
64726
64585
  object({
64727
- /** Verbatim HA condition state (`sunny`, `cloudy`, `rainy`, …). Null
64728
- * when no condition has been reported yet. */
64586
+ /** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
64729
64587
  condition: string().nullable(),
64730
- /** Current temperature in the reported unit. Null when not provided. */
64731
- temperature: number().nullable(),
64732
- /** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
64733
- temperatureUnit: string().nullable(),
64588
+ /** Current temperature in Celsius. Null when not provided or refused. */
64589
+ temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
64734
64590
  /** Relative humidity (0..100). Null when not provided. */
64735
- humidity: number().min(0).max(100).nullable(),
64736
- /** Barometric pressure in the reported unit. Null when not provided. */
64737
- pressure: number().nullable(),
64738
- /** Pressure unit string (e.g. `hPa` / `inHg`). Null when not provided. */
64739
- pressureUnit: string().nullable(),
64740
- /** Wind speed in the reported unit. Null when not provided. */
64741
- windSpeed: number().nullable(),
64742
- /** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
64743
- windSpeedUnit: string().nullable(),
64591
+ humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
64592
+ /** Barometric pressure in hPa. Null when not provided or refused. */
64593
+ pressure: number().nullable().meta(readingMeta("pressure")),
64594
+ /** Wind speed in metres per second. Null when not provided or refused. */
64595
+ windSpeed: number().nullable().meta(readingMeta("speed")),
64744
64596
  /** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
64745
- windBearing: number().nullable(),
64597
+ windBearing: number().nullable().meta(readingMeta("angle")),
64746
64598
  /** Ms epoch when the slice was last updated. */
64747
64599
  lastFetchedAt: number()
64748
64600
  });
@@ -78715,7 +78567,6 @@ var SENSOR_CAP_VOCABULARY = {
78715
78567
  field: "value",
78716
78568
  quantity: t("the reading", "il valore"),
78717
78569
  label: t("Reading", "Valore"),
78718
- unitField: "unit",
78719
78570
  icon: "gauge"
78720
78571
  }]
78721
78572
  },
@@ -78851,7 +78702,7 @@ var SENSOR_CAP_VOCABULARY = {
78851
78702
  field: "temperature",
78852
78703
  quantity: t("the outdoor temperature", "la temperatura esterna"),
78853
78704
  label: t("Temperature", "Temperatura"),
78854
- unitField: "temperatureUnit",
78705
+ unit: "°C",
78855
78706
  icon: "thermometer"
78856
78707
  },
78857
78708
  {
@@ -78867,7 +78718,7 @@ var SENSOR_CAP_VOCABULARY = {
78867
78718
  field: "windSpeed",
78868
78719
  quantity: t("the wind speed", "la velocità del vento"),
78869
78720
  label: t("Wind", "Vento"),
78870
- unitField: "windSpeedUnit",
78721
+ unit: "m/s",
78871
78722
  icon: "wind"
78872
78723
  },
78873
78724
  {
@@ -78875,7 +78726,7 @@ var SENSOR_CAP_VOCABULARY = {
78875
78726
  field: "pressure",
78876
78727
  quantity: t("the air pressure", "la pressione atmosferica"),
78877
78728
  label: t("Pressure", "Pressione"),
78878
- unitField: "pressureUnit",
78729
+ unit: "hPa",
78879
78730
  icon: "gauge"
78880
78731
  }
78881
78732
  ]
@@ -82012,6 +81863,11 @@ var PulseOverrideDraftSchema = object({
82012
81863
  targetId: string().min(1),
82013
81864
  enabled: boolean(),
82014
81865
  debounceMs: number().nonnegative(),
81866
+ edges: array(_enum([
81867
+ "rising",
81868
+ "button",
81869
+ "native-event"
81870
+ ])).min(1).optional(),
82015
81871
  sources: array(OverrideSourceSchema).min(1)
82016
81872
  });
82017
81873
  var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([