@camstack/addon-training 0.1.81 → 0.1.83

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 (20) hide show
  1. package/dist/{MotionZonesSettings-Ca-6XXfI.mjs → MotionZonesSettings-ua7eMlj6.mjs} +2 -2
  2. package/dist/{PrivacyMaskSettings-BCUM9T8S.mjs → PrivacyMaskSettings-tOwuzUR7.mjs} +2 -2
  3. package/dist/{SceneMonitorEditor-CjJ891SK.mjs → SceneMonitorEditor-D70yDpkr.mjs} +1 -1
  4. package/dist/_stub.js +5358 -5290
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-Cr7Pmc7w.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-CX_OmaCS.mjs} +4 -4
  6. package/dist/_virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CF6J142s.mjs +26 -0
  7. package/dist/{dist-BEHbfGro.mjs → dist-BK5jIqgk.mjs} +278 -422
  8. package/dist/{dist-CdN9FdUa.js → dist-BinKWWJ5.js} +278 -422
  9. package/dist/{hostInit-XwE0hfA0.mjs → hostInit-DR72gpW3.mjs} +3 -3
  10. package/dist/index.js +1 -1
  11. package/dist/orchestrator/training.addon.js +1 -1
  12. package/dist/orchestrator/training.addon.mjs +1 -1
  13. package/dist/{player-overlays-Bk8LUHwl.mjs → player-overlays-DCStC-vp.mjs} +1 -1
  14. package/dist/providers/modal/training-modal.addon.js +1 -1
  15. package/dist/providers/modal/training-modal.addon.mjs +1 -1
  16. package/dist/remoteEntry.js +1 -1
  17. package/dist/{responsive-DkRlf-GQ.mjs → responsive-DsQ48pYk.mjs} +16 -13
  18. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js-DslbW4nN.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js-BToJVQAB.mjs} +1 -1
  19. package/package.json +7 -2
  20. package/dist/_virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-9NmkfCw8.mjs +0 -26
@@ -5600,7 +5600,7 @@ var ZodIssueCode = {
5600
5600
  var ZodFirstPartyTypeKind;
5601
5601
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5602
5602
  //#endregion
5603
- //#region ../types/dist/sleep-BErAxhul.mjs
5603
+ //#region ../types/dist/sleep-CARd6wZF.mjs
5604
5604
  /**
5605
5605
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5606
5606
  * window to float samples (D455).
@@ -8335,6 +8335,52 @@ function defineEntryKind(name, spec) {
8335
8335
  if (defined === void 0) throw new DeclarationRefusedError(`entry kind "${name}" was not built`);
8336
8336
  return defined;
8337
8337
  }
8338
+ var READING_QUANTITIES = [
8339
+ "temperature",
8340
+ "pressure",
8341
+ "speed",
8342
+ "precipitation",
8343
+ "precipitation-rate",
8344
+ "length",
8345
+ "area",
8346
+ "illuminance",
8347
+ "irradiance",
8348
+ "power",
8349
+ "energy",
8350
+ "voltage",
8351
+ "current",
8352
+ "apparent-power",
8353
+ "humidity",
8354
+ "percentage",
8355
+ "concentration-volume",
8356
+ "concentration-mass",
8357
+ "concentration-ppb",
8358
+ "volume",
8359
+ "duration",
8360
+ "duration-ms",
8361
+ "information",
8362
+ "sound-level",
8363
+ "signal-level",
8364
+ "colour-temperature",
8365
+ "angle",
8366
+ "bitrate",
8367
+ "rate",
8368
+ "mass",
8369
+ "index",
8370
+ "ratio",
8371
+ "count",
8372
+ "none",
8373
+ "device"
8374
+ ];
8375
+ var ReadingQuantitySchema = _enum(READING_QUANTITIES);
8376
+ new Set(READING_QUANTITIES);
8377
+ function readingMeta(quantity, extras) {
8378
+ return {
8379
+ quantity,
8380
+ ...extras?.precision !== void 0 ? { precision: extras.precision } : {},
8381
+ ...extras?.label !== void 0 ? { label: extras.label } : {}
8382
+ };
8383
+ }
8338
8384
  /** Shared Zod schemas used across streaming capabilities. */
8339
8385
  var CamProfileSchema = _enum([
8340
8386
  "high",
@@ -8362,8 +8408,8 @@ var CamStreamKindSchema = _enum([
8362
8408
  "derived"
8363
8409
  ]);
8364
8410
  var CamStreamResolutionSchema = object({
8365
- width: number().int().positive(),
8366
- height: number().int().positive()
8411
+ width: number().int().positive().meta(readingMeta("none")),
8412
+ height: number().int().positive().meta(readingMeta("none"))
8367
8413
  });
8368
8414
  var CameraStreamSchema = object({
8369
8415
  /** Stable, provider-assigned id unique within the (deviceId) scope. */
@@ -9006,14 +9052,6 @@ Object.freeze({
9006
9052
  {
9007
9053
  "path": "aqi",
9008
9054
  "valueType": "number"
9009
- },
9010
- {
9011
- "path": "unit",
9012
- "valueType": "string"
9013
- },
9014
- {
9015
- "path": "precision",
9016
- "valueType": "number"
9017
9055
  }
9018
9056
  ],
9019
9057
  "alarm-panel": [
@@ -9049,20 +9087,10 @@ Object.freeze({
9049
9087
  "valueType": "boolean"
9050
9088
  }
9051
9089
  ],
9052
- "ambient-light-sensor": [
9053
- {
9054
- "path": "lux",
9055
- "valueType": "number"
9056
- },
9057
- {
9058
- "path": "unit",
9059
- "valueType": "string"
9060
- },
9061
- {
9062
- "path": "precision",
9063
- "valueType": "number"
9064
- }
9065
- ],
9090
+ "ambient-light-sensor": [{
9091
+ "path": "lux",
9092
+ "valueType": "number"
9093
+ }],
9066
9094
  "audio-metrics": [
9067
9095
  {
9068
9096
  "path": "ts",
@@ -9142,6 +9170,10 @@ Object.freeze({
9142
9170
  "path": "lastUpdated",
9143
9171
  "valueType": "number"
9144
9172
  },
9173
+ {
9174
+ "path": "volts",
9175
+ "valueType": "number"
9176
+ },
9145
9177
  {
9146
9178
  "path": "lastContactAt",
9147
9179
  "valueType": "number"
@@ -9384,10 +9416,6 @@ Object.freeze({
9384
9416
  "datetime"
9385
9417
  ]
9386
9418
  },
9387
- {
9388
- "path": "unit",
9389
- "valueType": "string"
9390
- },
9391
9419
  {
9392
9420
  "path": "min",
9393
9421
  "valueType": "number"
@@ -9399,19 +9427,6 @@ Object.freeze({
9399
9427
  {
9400
9428
  "path": "step",
9401
9429
  "valueType": "number"
9402
- },
9403
- {
9404
- "path": "precision",
9405
- "valueType": "number"
9406
- },
9407
- {
9408
- "path": "format",
9409
- "valueType": "enum",
9410
- "enumValues": [
9411
- "date",
9412
- "time",
9413
- "datetime"
9414
- ]
9415
9430
  }
9416
9431
  ],
9417
9432
  "cover": [
@@ -9652,13 +9667,8 @@ Object.freeze({
9652
9667
  "path": "value",
9653
9668
  "valueType": "string"
9654
9669
  }, {
9655
- "path": "format",
9656
- "valueType": "enum",
9657
- "enumValues": [
9658
- "date",
9659
- "time",
9660
- "datetime"
9661
- ]
9670
+ "path": "timestamp",
9671
+ "valueType": "boolean"
9662
9672
  }],
9663
9673
  "event-emitter": [
9664
9674
  {
@@ -9765,20 +9775,10 @@ Object.freeze({
9765
9775
  "valueType": "number"
9766
9776
  }
9767
9777
  ],
9768
- "humidity-sensor": [
9769
- {
9770
- "path": "percent",
9771
- "valueType": "number"
9772
- },
9773
- {
9774
- "path": "unit",
9775
- "valueType": "string"
9776
- },
9777
- {
9778
- "path": "precision",
9779
- "valueType": "number"
9780
- }
9781
- ],
9778
+ "humidity-sensor": [{
9779
+ "path": "percent",
9780
+ "valueType": "number"
9781
+ }],
9782
9782
  "image": [{
9783
9783
  "path": "url",
9784
9784
  "valueType": "string"
@@ -10117,20 +10117,10 @@ Object.freeze({
10117
10117
  "valueType": "boolean"
10118
10118
  }
10119
10119
  ],
10120
- "numeric-sensor": [
10121
- {
10122
- "path": "value",
10123
- "valueType": "number"
10124
- },
10125
- {
10126
- "path": "unit",
10127
- "valueType": "string"
10128
- },
10129
- {
10130
- "path": "precision",
10131
- "valueType": "number"
10132
- }
10133
- ],
10120
+ "numeric-sensor": [{
10121
+ "path": "value",
10122
+ "valueType": "number"
10123
+ }],
10134
10124
  "pet-feeder": [
10135
10125
  {
10136
10126
  "path": "foodLevel",
@@ -10222,14 +10212,6 @@ Object.freeze({
10222
10212
  {
10223
10213
  "path": "powerFactor",
10224
10214
  "valueType": "number"
10225
- },
10226
- {
10227
- "path": "unit",
10228
- "valueType": "string"
10229
- },
10230
- {
10231
- "path": "precision",
10232
- "valueType": "number"
10233
10215
  }
10234
10216
  ],
10235
10217
  "presence": [
@@ -10258,20 +10240,10 @@ Object.freeze({
10258
10240
  "valueType": "number"
10259
10241
  }
10260
10242
  ],
10261
- "pressure-sensor": [
10262
- {
10263
- "path": "hpa",
10264
- "valueType": "number"
10265
- },
10266
- {
10267
- "path": "unit",
10268
- "valueType": "string"
10269
- },
10270
- {
10271
- "path": "precision",
10272
- "valueType": "number"
10273
- }
10274
- ],
10243
+ "pressure-sensor": [{
10244
+ "path": "hpa",
10245
+ "valueType": "number"
10246
+ }],
10275
10247
  "privacy-mask": [{
10276
10248
  "path": "enabled",
10277
10249
  "valueType": "boolean"
@@ -10582,20 +10554,10 @@ Object.freeze({
10582
10554
  "path": "tampered",
10583
10555
  "valueType": "boolean"
10584
10556
  }],
10585
- "temperature-sensor": [
10586
- {
10587
- "path": "celsius",
10588
- "valueType": "number"
10589
- },
10590
- {
10591
- "path": "unit",
10592
- "valueType": "string"
10593
- },
10594
- {
10595
- "path": "precision",
10596
- "valueType": "number"
10597
- }
10598
- ],
10557
+ "temperature-sensor": [{
10558
+ "path": "celsius",
10559
+ "valueType": "number"
10560
+ }],
10599
10561
  "update": [
10600
10562
  {
10601
10563
  "path": "currentVersion",
@@ -10706,10 +10668,6 @@ Object.freeze({
10706
10668
  "path": "temperature",
10707
10669
  "valueType": "number"
10708
10670
  },
10709
- {
10710
- "path": "temperatureUnit",
10711
- "valueType": "string"
10712
- },
10713
10671
  {
10714
10672
  "path": "humidity",
10715
10673
  "valueType": "number"
@@ -10718,18 +10676,10 @@ Object.freeze({
10718
10676
  "path": "pressure",
10719
10677
  "valueType": "number"
10720
10678
  },
10721
- {
10722
- "path": "pressureUnit",
10723
- "valueType": "string"
10724
- },
10725
10679
  {
10726
10680
  "path": "windSpeed",
10727
10681
  "valueType": "number"
10728
10682
  },
10729
- {
10730
- "path": "windSpeedUnit",
10731
- "valueType": "string"
10732
- },
10733
10683
  {
10734
10684
  "path": "windBearing",
10735
10685
  "valueType": "number"
@@ -18112,7 +18062,8 @@ var CompositionProblemSchema = object({
18112
18062
  "item-field-reserved",
18113
18063
  "target-missing",
18114
18064
  "target-unreadable",
18115
- "not-replaceable"
18065
+ "not-replaceable",
18066
+ "native-capability-replace"
18116
18067
  ]),
18117
18068
  path: string(),
18118
18069
  message: string()
@@ -18170,9 +18121,11 @@ var CompositionFieldPlanSchema = object({
18170
18121
  reads: array(CompositionFieldReadSchema)
18171
18122
  });
18172
18123
  /**
18173
- * What a feature does to its target (D663): `new` builds a capability on a new
18174
- * device; on an existing device it `add`s a capability the device lacks, or
18175
- * `replace`s named fields of one it has natively.
18124
+ * What a feature does to its target (D663, D944): `new` builds a capability on a
18125
+ * new device; on an existing device it `add`s a capability the device lacks.
18126
+ * Replacing a capability the device already serves is refused
18127
+ * (`native-capability-replace`), not planned. `replace` remains on the claim
18128
+ * wire for a claim the hub already holds.
18176
18129
  */
18177
18130
  var CompositionFeatureModeSchema = _enum([
18178
18131
  "new",
@@ -20058,12 +20011,12 @@ method(object({ deviceId: number().int().nonnegative() }), DeviceAdminLinkSchema
20058
20011
  * providers detect renames by matching `uniqueId` and patch `id`
20059
20012
  * in-place so dispatch keeps working.
20060
20013
  *
20061
- * **Rendering metadata is NOT stored here.** Display fields (`unit`,
20062
- * `precision`) live in the cap STATUS SLICE so they flow live with every
20063
- * state push and are the single source of truth. The `device_class` tag is
20064
- * an HA-specific input that is consumed at the discovery boundary
20065
- * (`ha-domain-mapping`) to derive the CamStack `DeviceRole`; it is never
20066
- * stored after that point.
20014
+ * **Display unit and precision are NOT stored here.** They are a read-time
20015
+ * preference (D944). A polymorphic reading stores its `quantity` here once,
20016
+ * at adopt, and a date/time value stores `dateTimeShape` the same way —
20017
+ * neither is repeated on each sample. The `device_class` tag is an
20018
+ * HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
20019
+ * to derive the CamStack `DeviceRole`; it is never stored after that point.
20067
20020
  *
20068
20021
  * The `raw` escape hatch carries provider-specific attributes that don't
20069
20022
  * fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
@@ -20077,6 +20030,14 @@ var SourceInfoSchema = object({
20077
20030
  /** Immutable upstream identifier when available (HA `unique_id`, … ).
20078
20031
  * Used to detect rename when `id` changes. */
20079
20032
  uniqueId: string().optional(),
20033
+ /** Quantity of a polymorphic reading, written once at adopt (D944). */
20034
+ quantity: ReadingQuantitySchema.optional(),
20035
+ /** Date / time / datetime shape of a string reading, written once at adopt. */
20036
+ dateTimeShape: _enum([
20037
+ "date",
20038
+ "time",
20039
+ "datetime"
20040
+ ]).optional(),
20080
20041
  /** Provider-specific extras that don't fit the structured slots.
20081
20042
  * Whitelist what goes here per provider — do NOT dump entire HA
20082
20043
  * attribute blobs (would explode the meta JSON column). */
@@ -40989,7 +40950,8 @@ method(object({
40989
40950
  "enum"
40990
40951
  ]),
40991
40952
  enumValues: array(string()).optional(),
40992
- item: boolean().optional()
40953
+ item: boolean().optional(),
40954
+ quantity: ReadingQuantitySchema.optional()
40993
40955
  })).readonly(),
40994
40956
  itemArray: object({
40995
40957
  path: string(),
@@ -44789,8 +44751,8 @@ var MaskPolygonShapeSchema = object({
44789
44751
  /** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
44790
44752
  var MaskGridShapeSchema = object({
44791
44753
  kind: literal("grid"),
44792
- gridWidth: number(),
44793
- gridHeight: number(),
44754
+ gridWidth: number().meta(readingMeta("none")),
44755
+ gridHeight: number().meta(readingMeta("none")),
44794
44756
  cells: array(boolean())
44795
44757
  });
44796
44758
  discriminatedUnion("kind", [
@@ -56184,46 +56146,27 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
56184
56146
  }), LogStreamEntrySchema, { kind: "subscription" });
56185
56147
  object({
56186
56148
  /** Carbon dioxide concentration in ppm. */
56187
- co2Ppm: number().min(0).optional(),
56149
+ co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
56188
56150
  /** Total volatile organic compounds in ppb. */
56189
- vocPpb: number().min(0).optional(),
56151
+ vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
56190
56152
  /** Particulate matter ≤ 2.5 μm in µg/m³. */
56191
- pm25: number().min(0).optional(),
56153
+ pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
56192
56154
  /** Particulate matter ≤ 10 μm in µg/m³. */
56193
- pm10: number().min(0).optional(),
56155
+ pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
56194
56156
  /** Composite AQI value (typically 0..500). */
56195
- aqi: number().optional(),
56157
+ aqi: number().optional().meta(readingMeta("index")),
56196
56158
  /** Ms epoch when the slice was last updated. */
56197
- lastFetchedAt: number(),
56198
- /** Live display unit of the single metric this slice carries (e.g. HA
56199
- * `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
56200
- * upstream `sensor.*` entity surfaces ONE device_class, so one unit
56201
- * per slice is unambiguous. */
56202
- unit: string().optional(),
56203
- /** Suggested decimal places for numeric display.
56204
- * Populated live from the upstream source when provided (e.g. HA
56205
- * `attributes.suggested_display_precision`). Falls back to
56206
- * auto-formatting when absent. */
56207
- precision: number().int().min(0).max(10).optional()
56159
+ lastFetchedAt: number()
56208
56160
  });
56209
56161
  DeviceType.Sensor;
56210
56162
  object({
56211
- /** Current illuminance in lux (lx). */
56212
- lux: number().min(0).meta({
56213
- unit: "lx",
56163
+ /** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
56164
+ lux: number().min(0).nullable().meta(readingMeta("illuminance", {
56165
+ precision: 0,
56214
56166
  label: "Illuminance"
56215
- }),
56167
+ })),
56216
56168
  /** Ms epoch when the slice was last updated. */
56217
- lastFetchedAt: number(),
56218
- /** Live display unit from the upstream source (e.g. HA
56219
- * `attributes.unit_of_measurement`). The UI prefers this over the
56220
- * role's canonical unit. Absent → fall back to the canonical unit. */
56221
- unit: string().optional(),
56222
- /** Suggested decimal places for numeric display.
56223
- * Populated live from the upstream source when provided (e.g. HA
56224
- * `attributes.suggested_display_precision`). Falls back to
56225
- * auto-formatting when absent. */
56226
- precision: number().int().min(0).max(10).optional()
56169
+ lastFetchedAt: number()
56227
56170
  });
56228
56171
  DeviceType.Sensor;
56229
56172
  /**
@@ -56368,10 +56311,7 @@ var BatteryStatusSchema = object({
56368
56311
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
56369
56312
  * declines to band a non-finite reading, which is the correct shape.
56370
56313
  */
56371
- percentage: number().min(0).max(100).nullable().meta({
56372
- unit: "%",
56373
- label: "Charge"
56374
- }),
56314
+ percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
56375
56315
  /**
56376
56316
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
56377
56317
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -56398,7 +56338,14 @@ var BatteryStatusSchema = object({
56398
56338
  */
56399
56339
  sleeping: boolean().meta({ label: "Sleeping" }),
56400
56340
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
56401
- lastUpdated: number(),
56341
+ lastUpdated: number().meta(readingMeta("none")),
56342
+ /**
56343
+ * Battery voltage in volts, for a cell the firmware reports as voltage
56344
+ * rather than a percentage (Ecowitt capacitor packs). Absent on cameras
56345
+ * that only report charge. A second reading on this cap, not a second
56346
+ * `numeric-sensor` on the same device.
56347
+ */
56348
+ volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
56402
56349
  /**
56403
56350
  * Ms epoch of the last time the device PROVED it was reachable — a
56404
56351
  * completed firmware round-trip, an observed wake, or an inbound push
@@ -56458,7 +56405,7 @@ object({
56458
56405
  DeviceType.Sensor;
56459
56406
  object({
56460
56407
  /** Current level as 0..100 inclusive. Firmware-reported. */
56461
- percentage: number().min(0).max(100),
56408
+ percentage: number().min(0).max(100).meta(readingMeta("percentage")),
56462
56409
  /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
56463
56410
  lastChangedAt: number()
56464
56411
  });
@@ -56843,21 +56790,21 @@ object({
56843
56790
  /** Single setpoint in Celsius. Used by single-target modes
56844
56791
  * (heat / cool / dry). Null when the device is in a range mode
56845
56792
  * (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
56846
- target: number().nullable(),
56793
+ target: number().nullable().meta(readingMeta("temperature")),
56847
56794
  /** Upper bound of the dual setpoint in Celsius. Populated only
56848
56795
  * in `heat_cool` mode. */
56849
- targetHigh: number().nullable(),
56796
+ targetHigh: number().nullable().meta(readingMeta("temperature")),
56850
56797
  /** Lower bound of the dual setpoint in Celsius. Populated only
56851
56798
  * in `heat_cool` mode. */
56852
- targetLow: number().nullable(),
56799
+ targetLow: number().nullable().meta(readingMeta("temperature")),
56853
56800
  /** Target relative humidity (0..100). Null when the device has no
56854
56801
  * humidity control. */
56855
- targetHumidity: number().min(0).max(100).nullable(),
56802
+ targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
56856
56803
  /** Current measured temperature in Celsius. Read-only — pushed by
56857
56804
  * the device's internal sensor. */
56858
- currentTemp: number().nullable(),
56805
+ currentTemp: number().nullable().meta(readingMeta("temperature")),
56859
56806
  /** Current measured relative humidity (0..100). Read-only. */
56860
- currentHumidity: number().min(0).max(100).nullable(),
56807
+ currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
56861
56808
  /** Vertical louver swing on/off. Null when the device has no vertical
56862
56809
  * swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
56863
56810
  swingVertical: boolean().nullable(),
@@ -56865,11 +56812,11 @@ object({
56865
56812
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
56866
56813
  swingHorizontal: boolean().nullable(),
56867
56814
  /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
56868
- minTemp: number().nullable().optional(),
56815
+ minTemp: number().nullable().optional().meta(readingMeta("temperature")),
56869
56816
  /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
56870
- maxTemp: number().nullable().optional(),
56817
+ maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
56871
56818
  /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
56872
- tempStep: number().positive().nullable().optional(),
56819
+ tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
56873
56820
  /** Ms epoch when the slice was last updated (push or command). */
56874
56821
  lastFetchedAt: number()
56875
56822
  });
@@ -56948,17 +56895,17 @@ DeviceType.Thermostat, DeviceType.Climate, method(object({
56948
56895
  * modes to expose in the picker without a slice fetch.
56949
56896
  */
56950
56897
  var RgbTripletSchema = object({
56951
- r: number().int().min(0).max(255),
56952
- g: number().int().min(0).max(255),
56953
- b: number().int().min(0).max(255)
56898
+ r: number().int().min(0).max(255).meta(readingMeta("none")),
56899
+ g: number().int().min(0).max(255).meta(readingMeta("none")),
56900
+ b: number().int().min(0).max(255).meta(readingMeta("none"))
56954
56901
  });
56955
56902
  var HsvTripletSchema = object({
56956
56903
  /** Hue in degrees, 0..360. */
56957
- h: number().min(0).max(360),
56904
+ h: number().min(0).max(360).meta(readingMeta("none")),
56958
56905
  /** Saturation as 0..100 inclusive. */
56959
- s: number().min(0).max(100),
56906
+ s: number().min(0).max(100).meta(readingMeta("none")),
56960
56907
  /** Value/brightness as 0..100 inclusive. */
56961
- v: number().min(0).max(100)
56908
+ v: number().min(0).max(100).meta(readingMeta("none"))
56962
56909
  });
56963
56910
  var ColorInputSchema = discriminatedUnion("mode", [
56964
56911
  object({
@@ -56987,7 +56934,7 @@ object({
56987
56934
  /** Populated when `mode === 'hsv'`. */
56988
56935
  hsv: HsvTripletSchema.optional(),
56989
56936
  /** Populated when `mode === 'mired'`. */
56990
- mireds: number().int().optional(),
56937
+ mireds: number().int().optional().meta(readingMeta("colour-temperature")),
56991
56938
  /** Ms epoch of the last operator-driven change. */
56992
56939
  lastChangedAt: number()
56993
56940
  });
@@ -57111,18 +57058,12 @@ _enum([
57111
57058
  "empty",
57112
57059
  "full"
57113
57060
  ]);
57114
- /** Units a remaining-life counter is reported in. Closed: the panel renders
57115
- * each. */
57116
- var ConsumableRemainingUnitSchema = _enum([
57117
- "days",
57118
- "hours",
57119
- "cycles"
57120
- ]);
57121
- /** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
57122
- * days). `value` null = unknown (D393). */
57061
+ /** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
57062
+ var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
57063
+ /** Remaining life. `value` null = unknown (D393). Duration is seconds. */
57123
57064
  var ConsumableRemainingSchema = object({
57124
- value: number().min(0).nullable(),
57125
- unit: ConsumableRemainingUnitSchema
57065
+ value: number().min(0).nullable().meta(readingMeta("device")),
57066
+ kind: ConsumableRemainingKindSchema
57126
57067
  });
57127
57068
  var ConsumablesStatusSchema = object({
57128
57069
  items: array(object({
@@ -57131,7 +57072,7 @@ var ConsumablesStatusSchema = object({
57131
57072
  /** Display name. */
57132
57073
  label: string().min(1),
57133
57074
  /** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
57134
- level: number().min(0).max(100).nullable(),
57075
+ level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57135
57076
  /** The provider's discrete verdict when known (binary mode). */
57136
57077
  status: ConsumableStatusSchema.nullable(),
57137
57078
  /** What the item is — decides whether low or full is bad. Absent = `wear`. */
@@ -57148,7 +57089,7 @@ var ConsumablesStatusSchema = object({
57148
57089
  })),
57149
57090
  lastChangedAt: number()
57150
57091
  });
57151
- Object.values(DeviceType), [...ConsumableRemainingUnitSchema.options], [...ConsumableStatusSchema.options], [...ConsumableKindSchema.options], [...ConsumableFormSchema.options];
57092
+ Object.values(DeviceType), [...ConsumableRemainingKindSchema.options], [...ConsumableStatusSchema.options], [...ConsumableKindSchema.options], [...ConsumableFormSchema.options];
57152
57093
  Object.values(DeviceType), method(object({
57153
57094
  deviceId: number().int().nonnegative(),
57154
57095
  key: string().min(1)
@@ -57173,39 +57114,17 @@ object({
57173
57114
  /** Current value. Numeric kind → number; select/text/datetime → string.
57174
57115
  * Datetime values are ISO 8601 strings (full timestamp, date-only,
57175
57116
  * or time-only — HA accepts all three forms). */
57176
- value: union([number(), string()]),
57117
+ value: union([number().meta(readingMeta("device")), string()]),
57177
57118
  /** Acceptable values when `kind === 'select'`. Empty for other kinds. */
57178
57119
  options: array(string()),
57179
57120
  /** Ms epoch when the slice was last updated. */
57180
57121
  lastChangedAt: number(),
57181
- /** Display unit-of-measurement (e.g. '°C', '%', 'lx').
57182
- * Populated live from HA `attributes.unit_of_measurement` on each push.
57183
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57184
- unit: string().optional(),
57185
- /** Minimum allowed value. Populated live from HA `attributes.min`.
57186
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57187
- min: number().optional(),
57188
- /** Maximum allowed value. Populated live from HA `attributes.max`.
57189
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57190
- max: number().optional(),
57191
- /** Step increment. Populated live from HA `attributes.step`.
57192
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
57193
- step: number().optional(),
57194
- /** Suggested decimal places for numeric display. Populated live from HA
57195
- * `attributes.suggested_display_precision`, or derived from `step` when the
57196
- * HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
57197
- * other kinds. Falls back to auto-formatting when absent. */
57198
- precision: number().int().min(0).max(10).optional(),
57199
- /** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
57200
- * 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
57201
- * (UI defaults to 'datetime'). Derived live from the device's HA domain
57202
- * (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
57203
- * `has_date`/`has_time` attributes on each push. */
57204
- format: _enum([
57205
- "date",
57206
- "time",
57207
- "datetime"
57208
- ]).optional()
57122
+ /** Minimum allowed value, in the device quantity's base unit. */
57123
+ min: number().optional().meta(readingMeta("device")),
57124
+ /** Maximum allowed value, in the device quantity's base unit. */
57125
+ max: number().optional().meta(readingMeta("device")),
57126
+ /** Step increment, in the device quantity's base unit. */
57127
+ step: number().optional().meta(readingMeta("device"))
57209
57128
  });
57210
57129
  var ControlSetValueInputSchema = discriminatedUnion("kind", [
57211
57130
  object({
@@ -57243,9 +57162,9 @@ object({
57243
57162
  ]),
57244
57163
  /** 0 = fully closed, 100 = fully open. Null when the device has
57245
57164
  * no intermediate position surface. */
57246
- position: number().min(0).max(100).nullable(),
57165
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57247
57166
  /** 0 = closed slats, 100 = open slats. Null when no tilt support. */
57248
- tiltPosition: number().min(0).max(100).nullable(),
57167
+ tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57249
57168
  /** Ms epoch when the slice was last updated. */
57250
57169
  lastChangedAt: number()
57251
57170
  });
@@ -57303,9 +57222,9 @@ var NormalizedRangeSchema$1 = object({
57303
57222
  object({
57304
57223
  mode: DayNightModeSchema,
57305
57224
  /** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
57306
- sensitivity: number().optional(),
57225
+ sensitivity: number().optional().meta(readingMeta("none")),
57307
57226
  /** Delay before the IR-cut filter flips, in seconds. */
57308
- switchDelaySec: number().optional(),
57227
+ switchDelaySec: number().optional().meta(readingMeta("duration")),
57309
57228
  lastFetchedAt: number()
57310
57229
  });
57311
57230
  /**
@@ -57460,11 +57379,11 @@ object({
57460
57379
  /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
57461
57380
  availablePrograms: array(string()),
57462
57381
  /** Seconds remaining on the active cycle, or null when not running / not known. */
57463
- remainingSeconds: number().int().nonnegative().nullable(),
57382
+ remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
57464
57383
  /** Seconds the delayed start is set to (time until the cycle starts), or null. */
57465
- delayedStartSeconds: number().int().nonnegative().nullable(),
57384
+ delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
57466
57385
  /** Cycle completion 0..100, or null when not applicable. */
57467
- progressPercent: number().min(0).max(100).nullable(),
57386
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57468
57387
  /** True when the door is reported open; null when the door state is not known. */
57469
57388
  doorOpen: boolean().nullable(),
57470
57389
  /** True when the door is latched (locked) during a cycle; null when not reported. */
@@ -57488,7 +57407,7 @@ object({
57488
57407
  /** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
57489
57408
  machineCareDue: boolean().nullable(),
57490
57409
  /** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
57491
- machineCareRemainingRuns: number().int().nonnegative().nullable(),
57410
+ machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
57492
57411
  /** Option states: null when the appliance does not expose the option or has not said. */
57493
57412
  halfLoad: boolean().nullable(),
57494
57413
  hygienePlus: boolean().nullable(),
@@ -57503,7 +57422,7 @@ object({
57503
57422
  /** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
57504
57423
  activeEvents: array(string()),
57505
57424
  /** Numeric error code when the appliance reports one; null otherwise. */
57506
- errorCode: number().nullable(),
57425
+ errorCode: number().nullable().meta(readingMeta("none")),
57507
57426
  /** Name of the raised alert-level event while the appliance is in error; null otherwise. */
57508
57427
  errorLabel: string().nullable(),
57509
57428
  /** Ms epoch of the last value received from the appliance; 0 = never. */
@@ -57649,24 +57568,14 @@ object({
57649
57568
  /** Ms epoch of the last press. null = never observed since this provider started. */
57650
57569
  lastPressedAt: number().nullable(),
57651
57570
  /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
57652
- pressCountSinceStart: number()
57571
+ pressCountSinceStart: number().meta(readingMeta("count"))
57653
57572
  });
57654
57573
  object({
57655
57574
  deviceId: number(),
57656
57575
  timestamp: number()
57657
57576
  });
57658
57577
  DeviceType.Button, DeviceType.Camera;
57659
- /**
57660
- * Enum-state sensor — a string value picked from a finite option set.
57661
- * Drives HA `sensor` entries with `state_class: enum` (HVAC action
57662
- * states, weather conditions, contact-source channels, mode strings).
57663
- * The option set is stable across observations; it is persisted in the
57664
- * device config blob (via the control slice at adoption) and is NOT
57665
- * stored in `sourceInfo`.
57666
- */
57667
- /** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
57668
- * enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
57669
- var EnumSensorDateTimeFormatSchema = _enum([
57578
+ _enum([
57670
57579
  "date",
57671
57580
  "time",
57672
57581
  "datetime"
@@ -57674,12 +57583,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
57674
57583
  object({
57675
57584
  value: string(),
57676
57585
  /**
57677
- * Set for `DateTimeSensor`-role sensors so the UI renders the ISO `value`
57678
- * as a locale date/time/datetime; absent for plain enum sensors. HA
57679
- * `device_class=timestamp` → `'datetime'`, `device_class=date` → `'date'`,
57680
- * a time-only sensor → `'time'`.
57586
+ * Set when `value` is a timestamp rather than an enum state, so history
57587
+ * does not learn ISO strings as states. The display shape (date / time /
57588
+ * datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
57681
57589
  */
57682
- format: EnumSensorDateTimeFormatSchema.optional(),
57590
+ timestamp: boolean().optional(),
57683
57591
  /** Ms epoch when the slice was last updated. */
57684
57592
  lastFetchedAt: number()
57685
57593
  });
@@ -57698,13 +57606,13 @@ var EventFireSchema = object({
57698
57606
  deviceId: number(),
57699
57607
  eventType: string(),
57700
57608
  data: record(string(), unknown()).nullable(),
57701
- timestamp: number(),
57702
- seq: number()
57609
+ timestamp: number().meta(readingMeta("none")),
57610
+ seq: number().meta(readingMeta("count"))
57703
57611
  });
57704
57612
  object({
57705
57613
  eventTypes: array(string()),
57706
57614
  lastEvent: EventFireSchema.nullable(),
57707
- eventCountSinceStart: number()
57615
+ eventCountSinceStart: number().meta(readingMeta("count"))
57708
57616
  });
57709
57617
  DeviceType.EventEmitter;
57710
57618
  var IdentitySchema = object({
@@ -58093,10 +58001,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
58093
58001
  object({
58094
58002
  /** Active speed as 0..100 inclusive. Null when the device has no
58095
58003
  * speed surface (single-speed fan). */
58096
- percentage: number().min(0).max(100).nullable(),
58004
+ percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58097
58005
  /** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
58098
58006
  * a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
58099
- percentageStep: number().positive().optional(),
58007
+ percentageStep: number().positive().optional().meta(readingMeta("percentage")),
58100
58008
  /** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
58101
58009
  * — empty string when no preset is active or supported. */
58102
58010
  preset: string(),
@@ -58178,7 +58086,7 @@ var FeatureProbeStatusSchema = object({
58178
58086
  /** Camera/firmware model string. `null` when the firmware doesn't expose it. */
58179
58087
  model: string().nullable(),
58180
58088
  /** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
58181
- channelCount: number().nullable(),
58089
+ channelCount: number().nullable().meta(readingMeta("count")),
58182
58090
  /**
58183
58091
  * Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
58184
58092
  * completes — drivers' `getAccessoryChildren()` should treat zero as
@@ -58212,7 +58120,7 @@ object({
58212
58120
  DeviceType.Sensor;
58213
58121
  object({
58214
58122
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
58215
- position: number().min(0).max(100).nullable(),
58123
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58216
58124
  /** Ms epoch when the slice was last updated (push or poll). */
58217
58125
  lastFetchedAt: number()
58218
58126
  });
@@ -58221,9 +58129,9 @@ object({
58221
58129
  /** Whether the humidifier is currently on. */
58222
58130
  on: boolean(),
58223
58131
  /** Current measured relative humidity (0..100). Null when not reported. */
58224
- currentHumidity: number().min(0).max(100).nullable(),
58132
+ currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
58225
58133
  /** Target relative humidity (0..100). Null when no setpoint surface. */
58226
- targetHumidity: number().min(0).max(100).nullable(),
58134
+ targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
58227
58135
  /** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
58228
58136
  * device has no mode surface. */
58229
58137
  mode: string().nullable(),
@@ -58233,9 +58141,9 @@ object({
58233
58141
  * `idle` / `off`). Null when the device doesn't report it. */
58234
58142
  action: string().nullable(),
58235
58143
  /** HA `min_humidity` attribute. Null → UI uses 0. */
58236
- minHumidity: number().nullable(),
58144
+ minHumidity: number().nullable().meta(readingMeta("humidity")),
58237
58145
  /** HA `max_humidity` attribute. Null → UI uses 100. */
58238
- maxHumidity: number().nullable(),
58146
+ maxHumidity: number().nullable().meta(readingMeta("humidity")),
58239
58147
  /** Ms epoch when the slice was last updated. */
58240
58148
  lastChangedAt: number()
58241
58149
  });
@@ -58259,22 +58167,13 @@ DeviceType.Humidifier, method(object({
58259
58167
  auth: "protected"
58260
58168
  });
58261
58169
  object({
58262
- /** Current relative humidity, 0..100. */
58263
- percent: number().min(0).max(100).meta({
58264
- unit: "%",
58170
+ /** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
58171
+ percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
58172
+ precision: 0,
58265
58173
  label: "Humidity"
58266
- }),
58174
+ })),
58267
58175
  /** Ms epoch when the slice was last updated. */
58268
- lastFetchedAt: number(),
58269
- /** Live display unit from the upstream source (e.g. HA
58270
- * `attributes.unit_of_measurement`). The UI prefers this over the
58271
- * role's canonical unit. Absent → fall back to the canonical unit. */
58272
- unit: string().optional(),
58273
- /** Suggested decimal places for numeric display.
58274
- * Populated live from the upstream source when provided (e.g. HA
58275
- * `attributes.suggested_display_precision`). Falls back to
58276
- * auto-formatting when absent. */
58277
- precision: number().int().min(0).max(10).optional()
58176
+ lastFetchedAt: number()
58278
58177
  });
58279
58178
  DeviceType.Sensor, DeviceType.Thermostat;
58280
58179
  object({
@@ -58282,7 +58181,7 @@ object({
58282
58181
  * entity exposes no `entity_picture` (yet). */
58283
58182
  url: string().nullable(),
58284
58183
  /** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
58285
- lastUpdated: number().nullable()
58184
+ lastUpdated: number().nullable().meta(readingMeta("none"))
58286
58185
  });
58287
58186
  DeviceType.Image;
58288
58187
  /**
@@ -58338,19 +58237,19 @@ var NormalizedRangeSchema = object({
58338
58237
  });
58339
58238
  object({
58340
58239
  /** Normalized 0–100. */
58341
- brightness: number().optional(),
58240
+ brightness: number().optional().meta(readingMeta("none")),
58342
58241
  /** Normalized 0–100. */
58343
- contrast: number().optional(),
58242
+ contrast: number().optional().meta(readingMeta("none")),
58344
58243
  /** Normalized 0–100. */
58345
- saturation: number().optional(),
58244
+ saturation: number().optional().meta(readingMeta("none")),
58346
58245
  /** Normalized 0–100. */
58347
- sharpness: number().optional(),
58246
+ sharpness: number().optional().meta(readingMeta("none")),
58348
58247
  mirror: boolean().optional(),
58349
58248
  flip: boolean().optional(),
58350
58249
  rotate: ImageRotateSchema.optional(),
58351
58250
  whiteBalance: WhiteBalanceModeSchema.optional(),
58352
58251
  /** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
58353
- warmth: number().optional(),
58252
+ warmth: number().optional().meta(readingMeta("none")),
58354
58253
  exposureMode: ExposureModeSchema.optional(),
58355
58254
  backlightMode: BacklightModeSchema.optional(),
58356
58255
  lastFetchedAt: number()
@@ -58595,10 +58494,10 @@ var IntercomAbilitySchema = object({
58595
58494
  /** Codecs accepted by firmware, in preference order. Driver-authored strings. */
58596
58495
  codecs: array(string()),
58597
58496
  /** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
58598
- sampleRate: number(),
58497
+ sampleRate: number().meta(readingMeta("none")),
58599
58498
  duplex: _enum(["half", "full"]),
58600
58499
  /** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
58601
- maxBacklogMs: number()
58500
+ maxBacklogMs: number().meta(readingMeta("duration-ms"))
58602
58501
  });
58603
58502
  var IntercomStatusSchema = object({
58604
58503
  /** True while a talk session is open. */
@@ -58709,13 +58608,13 @@ object({
58709
58608
  activity: LawnMowerActivitySchema,
58710
58609
  /** 0..100 battery percentage. Null when the device has no battery
58711
58610
  * reading. */
58712
- batteryLevel: number().min(0).max(100).nullable(),
58611
+ batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58713
58612
  /** 0..100 mowing-completion percentage of the current task, or null when no
58714
58613
  * task is active / progress is unavailable. */
58715
- progressPercent: number().min(0).max(100).nullable(),
58614
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58716
58615
  /** Current device/event code (dynamic — mostly status, sometimes an error),
58717
58616
  * or null when unknown. */
58718
- currentCode: number().nullable(),
58617
+ currentCode: number().nullable().meta(readingMeta("none")),
58719
58618
  /** Human label for {@link currentCode}, or null when undecodable. */
58720
58619
  currentCodeLabel: string().nullable(),
58721
58620
  /** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
@@ -59177,7 +59076,7 @@ object({
59177
59076
  state: MediaPlayerStateSchema,
59178
59077
  /** Volume as 0..100 inclusive. Null when the device has no volume
59179
59078
  * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
59180
- volumeLevel: number().min(0).max(100).nullable(),
59079
+ volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59181
59080
  /** Mute toggle distinct from volume=0. Null when no mute surface.
59182
59081
  * Pair with `DeviceFeature.MediaPlayerMute`. */
59183
59082
  isMuted: boolean().nullable(),
@@ -59190,10 +59089,10 @@ object({
59190
59089
  currentMedia: MediaInfoSchema.nullable(),
59191
59090
  /** Current playback position in ms. Null when not seekable or
59192
59091
  * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
59193
- positionMs: number().int().nonnegative().nullable(),
59092
+ positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
59194
59093
  /** Total duration of the current media in ms. Null when unknown
59195
59094
  * (live stream) or nothing is playing. */
59196
- durationMs: number().int().nonnegative().nullable(),
59095
+ durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
59197
59096
  /** Shuffle toggle. Null when no shuffle surface.
59198
59097
  * Pair with `DeviceFeature.MediaPlayerShuffle`. */
59199
59098
  shuffle: boolean().nullable(),
@@ -59493,7 +59392,7 @@ object({
59493
59392
  * it, every provider seeded it at boot, and every session left it
59494
59393
  * `detected: true` after its detach.
59495
59394
  */
59496
- autoClearAfterMs: number().nullable()
59395
+ autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
59497
59396
  });
59498
59397
  /**
59499
59398
  * Payload of `motion.onMotionChanged` event + the corresponding bus
@@ -59543,7 +59442,7 @@ var MotionZoneRegionSchema = object({
59543
59442
  });
59544
59443
  object({
59545
59444
  enabled: boolean(),
59546
- sensitivity: number(),
59445
+ sensitivity: number().meta(readingMeta("none")),
59547
59446
  /** Grid region(s). Today exactly one `grid` shape. */
59548
59447
  regions: array(MotionZoneRegionSchema),
59549
59448
  lastFetchedAt: number()
@@ -59847,7 +59746,7 @@ var NavigationStatusSchema = object({
59847
59746
  following: boolean(),
59848
59747
  flash: boolean(),
59849
59748
  lightMode: NavigationLightModeSchema,
59850
- lightLevel: number().min(40).max(100),
59749
+ lightLevel: number().min(40).max(100).meta(readingMeta("none")),
59851
59750
  /** Ms epoch when the slice was last updated. */
59852
59751
  lastChangedAt: number()
59853
59752
  });
@@ -59893,13 +59792,13 @@ var NetworkLinkStatusSchema = object({
59893
59792
  * one whose reading has not landed must not be drawn at 0 %. Consumers
59894
59793
  * SKIP a null rather than coerce it.
59895
59794
  */
59896
- signalPercent: number().min(0).max(100).nullable(),
59795
+ signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59897
59796
  /** Raw received signal strength in dBm, when the firmware reports one. */
59898
- rssiDbm: number().optional(),
59797
+ rssiDbm: number().optional().meta(readingMeta("signal-level")),
59899
59798
  /** Network name of a wireless link, when the firmware reports it. */
59900
59799
  ssid: string().optional(),
59901
59800
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
59902
- lastUpdated: number()
59801
+ lastUpdated: number().meta(readingMeta("none"))
59903
59802
  });
59904
59803
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.Light, DeviceType.Lock, DeviceType.Siren, object({
59905
59804
  deviceId: number(),
@@ -60218,7 +60117,7 @@ object({
60218
60117
  lastError: string().nullable(),
60219
60118
  /** Number of deliveries currently buffered server-side (rate-limited
60220
60119
  * notifiers may queue). 0 when the provider sends synchronously. */
60221
- queueDepth: number().int().nonnegative(),
60120
+ queueDepth: number().int().nonnegative().meta(readingMeta("count")),
60222
60121
  /** Per-feature capability matrix — authoritative source for the
60223
60122
  * compose-form field gating. */
60224
60123
  supports: NotifierSupportsSchema
@@ -60268,15 +60167,8 @@ DeviceType.Notifier, method(NotifierSendInputSchema, NotifierSendResultSchema, {
60268
60167
  acceptedAt: number()
60269
60168
  });
60270
60169
  object({
60271
- value: number(),
60272
- /** Display unit-of-measurement (e.g. 'dBm', 's', 'rpm', 'steps').
60273
- * Populated live from the upstream source on each state push.
60274
- * Absent for unitless sensors. */
60275
- unit: string().optional(),
60276
- /** Suggested decimal places for numeric display.
60277
- * Populated live from the upstream source when provided.
60278
- * Falls back to auto-formatting when absent. */
60279
- precision: number().int().min(0).max(10).optional(),
60170
+ /** Reading in the device quantity's base unit. `null` when refused (D393). */
60171
+ value: number().nullable().meta(readingMeta("device")),
60280
60172
  /** Ms epoch when the slice was last updated. */
60281
60173
  lastFetchedAt: number()
60282
60174
  });
@@ -60566,23 +60458,26 @@ object({
60566
60458
  /** Food currently in the bowl (grams). Null when the device has not
60567
60459
  * reported a reading yet. On dual-hopper models this is the combined
60568
60460
  * bowl reading; per-hopper levels live in `food1`/`food2`. */
60569
- foodLevel: number().nullable(),
60461
+ foodLevel: number().nullable().meta(readingMeta("none")),
60570
60462
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
60571
60463
  * single-hopper models. */
60572
- food1: number().nullable(),
60464
+ food1: number().nullable().meta(readingMeta("none")),
60573
60465
  /** Hopper-2 food level (grams) on dual-hopper feeders; null on
60574
60466
  * single-hopper models. */
60575
- food2: number().nullable(),
60467
+ food2: number().nullable().meta(readingMeta("none")),
60576
60468
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
60577
60469
  * (`device_class: problem`, on = low). True when the bowl is empty /
60578
60470
  * below the feeder's low threshold. */
60579
60471
  lowFood: boolean(),
60580
60472
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
60581
60473
  * device has no battery reading. */
60582
- batteryPower: number().min(0).max(100).nullable(),
60583
- /** Days of desiccant life remaining. Null when the model has no
60584
- * desiccant sensor. */
60585
- desiccantLeftDays: number().nullable(),
60474
+ batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60475
+ /**
60476
+ * Desiccant life remaining, stored in seconds (duration base, D944).
60477
+ * Providers convert from the days the feeder reports. Null when the
60478
+ * model has no desiccant sensor.
60479
+ */
60480
+ desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
60586
60481
  /** True while a feed is in progress. */
60587
60482
  feeding: boolean(),
60588
60483
  /** Decoded connectivity / power status (HA petkit device-status enum).
@@ -60593,7 +60488,7 @@ object({
60593
60488
  * with `errorCode` for consumers that want the raw integer. */
60594
60489
  error: string().nullable(),
60595
60490
  /** Raw device error code (0 / null = no error). */
60596
- errorCode: number().nullable(),
60491
+ errorCode: number().nullable().meta(readingMeta("none")),
60597
60492
  /** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
60598
60493
  isDualHopper: boolean(),
60599
60494
  /** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
@@ -60603,7 +60498,7 @@ object({
60603
60498
  /** Play a chime when dispensing. */
60604
60499
  feedSound: boolean(),
60605
60500
  /** Speaker / prompt volume level (device-scaled integer). */
60606
- volume: number(),
60501
+ volume: number().meta(readingMeta("none")),
60607
60502
  /** Ms epoch when the slice was last refreshed from the cloud. */
60608
60503
  lastFetchedAt: number()
60609
60504
  });
@@ -61040,36 +60935,19 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
61040
60935
  });
61041
60936
  object({
61042
60937
  /** Instantaneous power draw in watts. */
61043
- watts: number().optional().meta({
61044
- unit: "W",
61045
- label: "Power"
61046
- }),
60938
+ watts: number().optional().meta(readingMeta("power", { label: "Power" })),
61047
60939
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
61048
- kwhTotal: number().optional().meta({
61049
- unit: "kWh",
61050
- label: "Energy"
61051
- }),
60940
+ kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
61052
60941
  /** Voltage in volts. */
61053
- volts: number().optional(),
60942
+ volts: number().optional().meta(readingMeta("voltage")),
61054
60943
  /** Current in amperes. */
61055
- amps: number().optional(),
61056
- /** Apparent power in volt-amperes (W × power-factor reciprocal). */
61057
- va: number().optional(),
60944
+ amps: number().optional().meta(readingMeta("current")),
60945
+ /** Apparent power in volt-amperes. */
60946
+ va: number().optional().meta(readingMeta("apparent-power")),
61058
60947
  /** Power factor (0..1) if reported. */
61059
- powerFactor: number().min(0).max(1).optional(),
60948
+ powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
61060
60949
  /** Ms epoch when the slice was last updated. */
61061
- lastFetchedAt: number(),
61062
- /** Live display unit of the single metric this slice carries (e.g. HA
61063
- * `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
61064
- * Each upstream `sensor.*` entity surfaces ONE device_class, so one
61065
- * unit per slice is unambiguous. The UI prefers this over the role's
61066
- * canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
61067
- unit: string().optional(),
61068
- /** Suggested decimal places for numeric display.
61069
- * Populated live from the upstream source when provided (e.g. HA
61070
- * `attributes.suggested_display_precision`). Falls back to
61071
- * auto-formatting when absent. */
61072
- precision: number().int().min(0).max(10).optional()
60950
+ lastFetchedAt: number()
61073
60951
  });
61074
60952
  DeviceType.Sensor;
61075
60953
  /**
@@ -61090,11 +60968,11 @@ DeviceType.Sensor;
61090
60968
  */
61091
60969
  var GpsLocationSchema = object({
61092
60970
  /** Latitude in decimal degrees, -90..90. */
61093
- latitude: number().min(-90).max(90),
60971
+ latitude: number().min(-90).max(90).meta(readingMeta("angle")),
61094
60972
  /** Longitude in decimal degrees, -180..180. */
61095
- longitude: number().min(-180).max(180),
60973
+ longitude: number().min(-180).max(180).meta(readingMeta("angle")),
61096
60974
  /** Reported accuracy in meters (lower = better). */
61097
- accuracyMeters: number().nonnegative()
60975
+ accuracyMeters: number().nonnegative().meta(readingMeta("length"))
61098
60976
  });
61099
60977
  object({
61100
60978
  /** `home` / `not_home` / any user-defined zone name. */
@@ -61106,25 +60984,16 @@ object({
61106
60984
  gps: GpsLocationSchema.nullable(),
61107
60985
  /** Optional battery level of the tracking device (0..100). Null
61108
60986
  * when not reported. */
61109
- batteryPercent: number().min(0).max(100).nullable(),
60987
+ batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
61110
60988
  /** Ms epoch when the slice was last updated. */
61111
60989
  lastChangedAt: number()
61112
60990
  });
61113
60991
  DeviceType.Presence;
61114
60992
  object({
61115
- /** Current pressure in hPa. */
61116
- hpa: number(),
60993
+ /** Current pressure in hPa. `null` when the sample was refused (D393). */
60994
+ hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
61117
60995
  /** Ms epoch when the slice was last updated. */
61118
- lastFetchedAt: number(),
61119
- /** Live display unit from the upstream source (e.g. HA
61120
- * `attributes.unit_of_measurement`). The UI prefers this over the
61121
- * role's canonical unit. Absent → fall back to the canonical unit. */
61122
- unit: string().optional(),
61123
- /** Suggested decimal places for numeric display.
61124
- * Populated live from the upstream source when provided (e.g. HA
61125
- * `attributes.suggested_display_precision`). Falls back to
61126
- * auto-formatting when absent. */
61127
- precision: number().int().min(0).max(10).optional()
60996
+ lastFetchedAt: number()
61128
60997
  });
61129
60998
  DeviceType.Sensor;
61130
60999
  /**
@@ -61457,8 +61326,8 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
61457
61326
  }), _void(), { kind: "mutation" });
61458
61327
  var PtzAutotrackSettingsSchema = object({
61459
61328
  targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
61460
- stopDelaySeconds: number().int().min(0).max(300),
61461
- disappearDelaySeconds: number().int().min(0).max(300)
61329
+ stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
61330
+ disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
61462
61331
  });
61463
61332
  /**
61464
61333
  * One option the camera advertises as a valid `targetType`. Drivers
@@ -61515,7 +61384,7 @@ object({
61515
61384
  /** null = unknown — never coerced to false (D393). */
61516
61385
  running: boolean().nullable(),
61517
61386
  /** Epoch ms the current boolean began; null while `running` is null. */
61518
- since: number().nullable(),
61387
+ since: number().nullable().meta(readingMeta("none")),
61519
61388
  source: RunningStateSourceSchema.meta({ label: "Source" }),
61520
61389
  phase: string().nullable(),
61521
61390
  /** null until a reading has been published (D393). */
@@ -62713,9 +62582,9 @@ var RecordTriggerSchema = _enum([
62713
62582
  */
62714
62583
  var RecordWindowSchema = object({
62715
62584
  trigger: RecordTriggerSchema,
62716
- day: number().int().min(0).max(6),
62717
- startMinute: number().int().min(0).max(1439),
62718
- endMinute: number().int().min(1).max(1440)
62585
+ day: number().int().min(0).max(6).meta(readingMeta("none")),
62586
+ startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
62587
+ endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
62719
62588
  });
62720
62589
  /** Status of one physical volume, as the camera itself describes it. */
62721
62590
  var OnboardStorageVolumeSchema = object({
@@ -62784,10 +62653,10 @@ object({
62784
62653
  /** Master "record to the card at all" switch. */
62785
62654
  enabled: boolean().nullable(),
62786
62655
  overwriteWhenFull: boolean().nullable(),
62787
- preRecordSec: number().nullable(),
62788
- postRecordSec: number().nullable(),
62656
+ preRecordSec: number().nullable().meta(readingMeta("duration")),
62657
+ postRecordSec: number().nullable().meta(readingMeta("duration")),
62789
62658
  /** Length of one recorded file, in minutes. */
62790
- segmentMinutes: number().nullable(),
62659
+ segmentMinutes: number().nullable().meta(readingMeta("none")),
62791
62660
  /** The primary track's weekly windows, flattened. */
62792
62661
  windows: array(RecordWindowSchema),
62793
62662
  /**
@@ -62803,7 +62672,7 @@ object({
62803
62672
  * provider reporting a non-zero count also reports the schedule as not
62804
62673
  * writable.
62805
62674
  */
62806
- unreadableWindows: number(),
62675
+ unreadableWindows: number().meta(readingMeta("count")),
62807
62676
  /**
62808
62677
  * The camera is scheduled to record and has NO usable storage.
62809
62678
  *
@@ -63440,7 +63309,7 @@ var CamStreamDescriptorSchema = object({
63440
63309
  url: string().optional(),
63441
63310
  codec: string().optional(),
63442
63311
  resolution: CamStreamResolutionSchema.optional(),
63443
- fps: number().positive().optional(),
63312
+ fps: number().positive().optional().meta(readingMeta("rate")),
63444
63313
  label: string().optional(),
63445
63314
  /** Device-level features (e.g. `battery-operated`) — drives broker policy. */
63446
63315
  deviceFeatures: array(string()).optional(),
@@ -63470,18 +63339,18 @@ var StreamProfileSchema = _enum([
63470
63339
  ]);
63471
63340
  /** The current encoding config of one stream profile. */
63472
63341
  var StreamProfileConfigSchema = object({
63473
- width: number(),
63474
- height: number(),
63342
+ width: number().meta(readingMeta("none")),
63343
+ height: number().meta(readingMeta("none")),
63475
63344
  codec: _enum(["h264", "h265"]),
63476
- framerate: number(),
63477
- bitrate: number(),
63345
+ framerate: number().meta(readingMeta("rate")),
63346
+ bitrate: number().meta(readingMeta("bitrate")),
63478
63347
  bitrateMode: _enum(["vbr", "cbr"]).optional(),
63479
63348
  encoderProfile: _enum([
63480
63349
  "high",
63481
63350
  "main",
63482
63351
  "baseline"
63483
63352
  ]).optional(),
63484
- gop: number().optional(),
63353
+ gop: number().optional().meta(readingMeta("none")),
63485
63354
  /**
63486
63355
  * Whether THIS profile currently carries an audio track. READ-ONLY here.
63487
63356
  *
@@ -64124,23 +63993,13 @@ object({
64124
63993
  });
64125
63994
  DeviceType.Sensor;
64126
63995
  object({
64127
- /** Current temperature in Celsius. */
64128
- celsius: number().meta({
64129
- unit: "°C",
63996
+ /** Current temperature in Celsius. `null` when the sample was refused (D393). */
63997
+ celsius: number().nullable().meta(readingMeta("temperature", {
63998
+ precision: 1,
64130
63999
  label: "Temperature"
64131
- }),
64000
+ })),
64132
64001
  /** Ms epoch when the slice was last updated (push or poll). */
64133
- lastFetchedAt: number(),
64134
- /** Live display unit from the upstream source (e.g. HA
64135
- * `attributes.unit_of_measurement`). The UI prefers this over the
64136
- * role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
64137
- * Absent → the UI falls back to the role's canonical unit. */
64138
- unit: string().optional(),
64139
- /** Suggested decimal places for numeric display.
64140
- * Populated live from the upstream source when provided (e.g. HA
64141
- * `attributes.suggested_display_precision`). Falls back to
64142
- * auto-formatting when absent. */
64143
- precision: number().int().min(0).max(10).optional()
64002
+ lastFetchedAt: number()
64144
64003
  });
64145
64004
  DeviceType.Sensor, DeviceType.Thermostat;
64146
64005
  /**
@@ -64392,16 +64251,16 @@ object({
64392
64251
  ]),
64393
64252
  /** 0..100 battery percentage. Null when the device has no battery
64394
64253
  * reading. */
64395
- batteryLevel: number().min(0).max(100).nullable(),
64254
+ batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64396
64255
  /** Current fan-speed token (provider-verbatim). Null when unknown or
64397
64256
  * the vacuum has no speed control. */
64398
64257
  fanSpeed: string().nullable(),
64399
64258
  /** Speed tokens the hardware accepts — drives the UI selector. */
64400
64259
  availableFanSpeeds: array(string()),
64401
64260
  /** 0..100 cleaning-completion percentage of the current task, or null. */
64402
- progressPercent: number().min(0).max(100).nullable(),
64261
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64403
64262
  /** Current error code (0 / null = no error). */
64404
- errorCode: number().nullable(),
64263
+ errorCode: number().nullable().meta(readingMeta("none")),
64405
64264
  /** Human label for {@link errorCode}, or null when none / undecodable. */
64406
64265
  errorLabel: string().nullable(),
64407
64266
  /** Ms epoch when the slice was last updated. */
@@ -64440,7 +64299,7 @@ object({
64440
64299
  ]),
64441
64300
  /** 0 = fully closed, 100 = fully open. Null when the device has no
64442
64301
  * intermediate position surface. */
64443
- position: number().min(0).max(100).nullable(),
64302
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64444
64303
  /** Ms epoch when the slice was last updated. */
64445
64304
  lastChangedAt: number()
64446
64305
  });
@@ -64468,9 +64327,9 @@ object({
64468
64327
  DeviceType.Sensor;
64469
64328
  object({
64470
64329
  /** Current measured temperature. Null when not reported. */
64471
- currentTemp: number().nullable(),
64330
+ currentTemp: number().nullable().meta(readingMeta("temperature")),
64472
64331
  /** Target temperature setpoint. Null when no setpoint surface. */
64473
- targetTemp: number().nullable(),
64332
+ targetTemp: number().nullable().meta(readingMeta("temperature")),
64474
64333
  /** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
64475
64334
  * `heat_pump` / `high_demand` / `performance` / `off`). Null when the
64476
64335
  * device reports an unknown state. */
@@ -64481,9 +64340,9 @@ object({
64481
64340
  * has no away surface. */
64482
64341
  away: boolean().nullable(),
64483
64342
  /** HA `min_temp` attribute. Null when not reported. */
64484
- minTemp: number().nullable(),
64343
+ minTemp: number().nullable().meta(readingMeta("temperature")),
64485
64344
  /** HA `max_temp` attribute. Null when not reported. */
64486
- maxTemp: number().nullable(),
64345
+ maxTemp: number().nullable().meta(readingMeta("temperature")),
64487
64346
  /** Ms epoch when the slice was last updated. */
64488
64347
  lastChangedAt: number()
64489
64348
  });
@@ -64507,25 +64366,18 @@ DeviceType.WaterHeater, method(object({
64507
64366
  auth: "protected"
64508
64367
  });
64509
64368
  object({
64510
- /** Verbatim HA condition state (`sunny`, `cloudy`, `rainy`, …). Null
64511
- * when no condition has been reported yet. */
64369
+ /** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
64512
64370
  condition: string().nullable(),
64513
- /** Current temperature in the reported unit. Null when not provided. */
64514
- temperature: number().nullable(),
64515
- /** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
64516
- temperatureUnit: string().nullable(),
64371
+ /** Current temperature in Celsius. Null when not provided or refused. */
64372
+ temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
64517
64373
  /** Relative humidity (0..100). Null when not provided. */
64518
- humidity: number().min(0).max(100).nullable(),
64519
- /** Barometric pressure in the reported unit. Null when not provided. */
64520
- pressure: number().nullable(),
64521
- /** Pressure unit string (e.g. `hPa` / `inHg`). Null when not provided. */
64522
- pressureUnit: string().nullable(),
64523
- /** Wind speed in the reported unit. Null when not provided. */
64524
- windSpeed: number().nullable(),
64525
- /** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
64526
- windSpeedUnit: string().nullable(),
64374
+ humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
64375
+ /** Barometric pressure in hPa. Null when not provided or refused. */
64376
+ pressure: number().nullable().meta(readingMeta("pressure")),
64377
+ /** Wind speed in metres per second. Null when not provided or refused. */
64378
+ windSpeed: number().nullable().meta(readingMeta("speed")),
64527
64379
  /** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
64528
- windBearing: number().nullable(),
64380
+ windBearing: number().nullable().meta(readingMeta("angle")),
64529
64381
  /** Ms epoch when the slice was last updated. */
64530
64382
  lastFetchedAt: number()
64531
64383
  });
@@ -78502,7 +78354,6 @@ var SENSOR_CAP_VOCABULARY = {
78502
78354
  field: "value",
78503
78355
  quantity: t("the reading", "il valore"),
78504
78356
  label: t("Reading", "Valore"),
78505
- unitField: "unit",
78506
78357
  icon: "gauge"
78507
78358
  }]
78508
78359
  },
@@ -78638,7 +78489,7 @@ var SENSOR_CAP_VOCABULARY = {
78638
78489
  field: "temperature",
78639
78490
  quantity: t("the outdoor temperature", "la temperatura esterna"),
78640
78491
  label: t("Temperature", "Temperatura"),
78641
- unitField: "temperatureUnit",
78492
+ unit: "°C",
78642
78493
  icon: "thermometer"
78643
78494
  },
78644
78495
  {
@@ -78654,7 +78505,7 @@ var SENSOR_CAP_VOCABULARY = {
78654
78505
  field: "windSpeed",
78655
78506
  quantity: t("the wind speed", "la velocità del vento"),
78656
78507
  label: t("Wind", "Vento"),
78657
- unitField: "windSpeedUnit",
78508
+ unit: "m/s",
78658
78509
  icon: "wind"
78659
78510
  },
78660
78511
  {
@@ -78662,7 +78513,7 @@ var SENSOR_CAP_VOCABULARY = {
78662
78513
  field: "pressure",
78663
78514
  quantity: t("the air pressure", "la pressione atmosferica"),
78664
78515
  label: t("Pressure", "Pressione"),
78665
- unitField: "pressureUnit",
78516
+ unit: "hPa",
78666
78517
  icon: "gauge"
78667
78518
  }
78668
78519
  ]
@@ -81799,6 +81650,11 @@ var PulseOverrideDraftSchema = object({
81799
81650
  targetId: string().min(1),
81800
81651
  enabled: boolean(),
81801
81652
  debounceMs: number().nonnegative(),
81653
+ edges: array(_enum([
81654
+ "rising",
81655
+ "button",
81656
+ "native-event"
81657
+ ])).min(1).optional(),
81802
81658
  sources: array(OverrideSourceSchema).min(1)
81803
81659
  });
81804
81660
  var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([