@camstack/addon-admin-ui 1.2.470 → 1.2.472

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 (139) hide show
  1. package/dist/assets/{AddonAssetIcon-CNIaymph.js → AddonAssetIcon-BoV-s8vY.js} +1 -1
  2. package/dist/assets/Addons-DLTlJq7l.js +4 -0
  3. package/dist/assets/{AdminPage-CCxxWx-6.js → AdminPage-DsAJhnoH.js} +1 -1
  4. package/dist/assets/{AdminSectionedPage-CFnkGNjR.js → AdminSectionedPage-DJx7aRbA.js} +1 -1
  5. package/dist/assets/AiProfiles-B8qFPcaJ.js +3 -0
  6. package/dist/assets/{BrokerForm-CURGwg-b.js → BrokerForm-B3-e-xSz.js} +1 -1
  7. package/dist/assets/Build-LEBDM041.js +40 -0
  8. package/dist/assets/Calendars-BFKf7hvl.js +1 -0
  9. package/dist/assets/Cluster-CES248-c.js +1 -0
  10. package/dist/assets/CompositionCreateForm-CZcZeaFU.js +1 -0
  11. package/dist/assets/{CompositionEditorPage-B8cZJ6lS.js → CompositionEditorPage-CBBOInEt.js} +1 -1
  12. package/dist/assets/Data-CGHht6N-.js +2 -0
  13. package/dist/assets/DeviceDetail-DYRNyiuG.js +2 -0
  14. package/dist/assets/{DeviceDoActionFields-C5EBbspf.js → DeviceDoActionFields-C9A5Vund.js} +1 -1
  15. package/dist/assets/{Devices-C5FXlXLu.js → Devices-BDcBpfdH.js} +1 -1
  16. package/dist/assets/{EmbedPlayerPage-CfDavHrE.js → EmbedPlayerPage-kQQSKYcm.js} +1 -1
  17. package/dist/assets/History-DHqYgtT_.js +1 -0
  18. package/dist/assets/Identity-2aB-bcpG.js +1 -0
  19. package/dist/assets/IntegrationDetail-CHeCYf51.js +1 -0
  20. package/dist/assets/{IntegrationHub-B4WSNyTn.js → IntegrationHub-CDk7OQ80.js} +1 -1
  21. package/dist/assets/Integrations-CLdAR_bC.js +1 -0
  22. package/dist/assets/Integrations-DtA-SgPz.js +1 -0
  23. package/dist/assets/{Library-C4qxlKpK.js → Library-jAJ1leN_.js} +1 -1
  24. package/dist/assets/Locations-D_Y_LS__.js +3 -0
  25. package/dist/assets/{Logs-CUoVNMCD.js → Logs-CSouKCsq.js} +1 -1
  26. package/dist/assets/{MaskShapeCanvas-CH26Mkpx.js → MaskShapeCanvas-DOTEtd0S.js} +1 -1
  27. package/dist/assets/{MotionZonesSettings-Co2CdVJJ.js → MotionZonesSettings-CvKLnfMs.js} +1 -1
  28. package/dist/assets/{MyAccess-CvOXHdv2.js → MyAccess-Dx2keRYZ.js} +1 -1
  29. package/dist/assets/{Network-H2U6fhMH.js → Network-DqeiKBG_.js} +1 -1
  30. package/dist/assets/{NodeAddonsSettingsPanel-D44nwf4S.js → NodeAddonsSettingsPanel-Bo6JkasG.js} +1 -1
  31. package/dist/assets/Notifications-BdRSwtaZ.js +1 -0
  32. package/dist/assets/Pipeline-B8BnTTB6.js +1 -0
  33. package/dist/assets/{PrivacyMaskSettings-BQfXVA03.js → PrivacyMaskSettings-B8RDIhLI.js} +1 -1
  34. package/dist/assets/Recordings-_9tze7ES.js +1 -0
  35. package/dist/assets/{ScanNetworkModal-CxEXGRiQ.js → ScanNetworkModal-D1789toC.js} +1 -1
  36. package/dist/assets/{SceneMonitorEditor-CBq0NEBB.js → SceneMonitorEditor-CidMFB_6.js} +1 -1
  37. package/dist/assets/SessionsList-BDFoAqB4.js +2 -0
  38. package/dist/assets/{Showroom-CeqaYSNl.js → Showroom-g9Vrk7ey.js} +1 -1
  39. package/dist/assets/{System-b8v_VClU.js → System-BFOcIYk6.js} +1 -1
  40. package/dist/assets/{Terminal-Du9eLqrr.js → Terminal-Dqvq98CQ.js} +3 -3
  41. package/dist/assets/{Viewer-OB641hit.js → Viewer-B6THeol3.js} +1 -1
  42. package/dist/assets/{_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-DsV-INvL.js → _virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-EdjPwsFV.js} +1 -1
  43. package/dist/assets/{addon-icons-context-D72gaFSB.js → addon-icons-context-BiqHN3N3.js} +1 -1
  44. package/dist/assets/{archive-CLjwLKJL.js → archive-CnhS48Wh.js} +1 -1
  45. package/dist/assets/{assignment-candidates-iKtFwGTX.js → assignment-candidates-BuyOLzEY.js} +1 -1
  46. package/dist/assets/{authed-media-sfY6qQfl.js → authed-media-CIJCq1KJ.js} +1 -1
  47. package/dist/assets/{bell-off-DyJQmEp9.js → bell-off-7jyxJXpT.js} +1 -1
  48. package/dist/assets/{board-model-C6mduuIJ.js → board-model-BtdRuNBI.js} +1 -1
  49. package/dist/assets/{boxes-CBemgbeZ.js → boxes-BA97gZyJ.js} +1 -1
  50. package/dist/assets/circle-slash-nVYwXt7M.js +1 -0
  51. package/dist/assets/client-DCH-eKE_.js +358 -0
  52. package/dist/assets/{cloud-upload-jgwgBoNq.js → cloud-upload-qezQXv3c.js} +1 -1
  53. package/dist/assets/crop-CFXeH_mC.js +1 -0
  54. package/dist/assets/{device-integration-icon-DrJydGCz.js → device-integration-icon-B4EZcZfv.js} +1 -1
  55. package/dist/assets/device-picker-model-C_qELDKl.js +1 -0
  56. package/dist/assets/{dist-CZavx2xe.js → dist-BlRomFG_.js} +1 -1
  57. package/dist/assets/dist-CMwJjIGP.js +358 -0
  58. package/dist/assets/{eraser-BgJNY_R7.js → eraser-DLPQtKgN.js} +1 -1
  59. package/dist/assets/event-targets-DO8Gz77i.js +2 -0
  60. package/dist/assets/{format-Chpis3_C.js → format-BYDOzxSv.js} +1 -1
  61. package/dist/assets/{hard-drive-download-BsRnY_sj.js → hard-drive-download-g_5xAcFq.js} +1 -1
  62. package/dist/assets/{hostInit-BY2HcQzH.js → hostInit-BDvZ4twh.js} +1 -1
  63. package/dist/assets/i18n-D3CF2V_j.js +1 -0
  64. package/dist/assets/{icons-Cl3Q-oKp.js → icons-PobQxRhs.js} +1 -1
  65. package/dist/assets/index-BvsP28fN.css +2 -0
  66. package/dist/assets/index-Cv22-mxY.js +4 -0
  67. package/dist/assets/{key-DraPTMtd.js → key-C9NMIy5y.js} +1 -1
  68. package/dist/assets/{key-round-JV15ywEe.js → key-round-CaHb89Wi.js} +1 -1
  69. package/dist/assets/{layers-DPTXQgwg.js → layers-Avi8-PfM.js} +1 -1
  70. package/dist/assets/{linked-devices-model-VztMI7gX.js → linked-devices-model-DgyEbI0V.js} +1 -1
  71. package/dist/assets/{list-ordered-WOVRKluR.js → list-ordered-6-NGRbR7.js} +1 -1
  72. package/dist/assets/{location-owned-usage-CMfFSpnP.js → location-owned-usage-BbuferaC.js} +1 -1
  73. package/dist/assets/{log-in-CjVCQ2Ss.js → log-in-D2R9cXBI.js} +1 -1
  74. package/dist/assets/{memory-stick-DlVywO8P.js → memory-stick-ev8VFA2q.js} +1 -1
  75. package/dist/assets/{message-square-plus-4_FR8RtR.js → message-square-plus-C0P6xmPe.js} +1 -1
  76. package/dist/assets/{mf-entry-bootstrap-0-ecf5902e.js → mf-entry-bootstrap-0-3ff2e228.js} +2 -2
  77. package/dist/assets/{mic-off-CPwart4B.js → mic-off-BdFkx2wJ.js} +1 -1
  78. package/dist/assets/{monitor-off-BhBLlCrK.js → monitor-off-DUZRNTFt.js} +1 -1
  79. package/dist/assets/{monitor-smartphone-C4dAoS1I.js → monitor-smartphone-CsrrNdMJ.js} +1 -1
  80. package/dist/assets/mount-C_qELDKl.js +1 -0
  81. package/dist/assets/{player-overlays-D78AKVl-.js → player-overlays-C6LCgH1T.js} +1 -1
  82. package/dist/assets/{recycle--kjdQYMN.js → recycle-C3C83uwv.js} +1 -1
  83. package/dist/assets/remoteEntry-DY95P5t1.js +1 -0
  84. package/dist/assets/{scan-eye-DzHaMLoc.js → scan-eye-D2LQj-_4.js} +1 -1
  85. package/dist/assets/{scissors-DDivB-hs.js → scissors-DTz32yJa.js} +1 -1
  86. package/dist/assets/{search-x-CQFS7Rh0.js → search-x-BQtbshQu.js} +1 -1
  87. package/dist/assets/{send-C9G3os3I.js → send-BfSwFs7e.js} +1 -1
  88. package/dist/assets/{settings-2-ClqCJP_D.js → settings-2-DJqkusF5.js} +1 -1
  89. package/dist/assets/{speaker-Dy-EAf8K.js → speaker-D0AxWmRG.js} +1 -1
  90. package/dist/assets/src-V3rq3t7i.js +67 -0
  91. package/dist/assets/step-inspector-model-BwAjPOgf.js +1 -0
  92. package/dist/assets/{timer-reset-DlhCZ8wp.js → timer-reset-DVs9TXU7.js} +1 -1
  93. package/dist/assets/{ungroup-DOb-9ddi.js → ungroup-vjygAxAO.js} +1 -1
  94. package/dist/assets/{unlink-C73mlmsX.js → unlink-VhqcHI2O.js} +1 -1
  95. package/dist/assets/{usb-DhychVOa.js → usb-CTNmHXln.js} +1 -1
  96. package/dist/assets/{use-scenes-feed--0VaT__E.js → use-scenes-feed-DBEhAxap.js} +1 -1
  97. package/dist/assets/{use-stationary-objects-DvVXf2ec.js → use-stationary-objects-BO1fOyUY.js} +1 -1
  98. package/dist/assets/useEventInvalidation-C_qELDKl.js +1 -0
  99. package/dist/assets/{useNodeUrlSelection-DKfaYoxL.js → useNodeUrlSelection-qvARJSiD.js} +1 -1
  100. package/dist/assets/user-round-CPpARWUk.js +1 -0
  101. package/dist/assets/{user-x-C6GTZnHw.js → user-x-BZF64XMi.js} +1 -1
  102. package/dist/assets/{video-12rcn9DI.js → video-BDVDsrHB.js} +1 -1
  103. package/dist/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-DOs9logC.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-XXht_Mtf.js} +2 -2
  104. package/dist/index.html +10 -10
  105. package/dist/server/addon.js +274 -423
  106. package/package.json +1 -1
  107. package/dist/assets/Addons-BFu9jODe.js +0 -4
  108. package/dist/assets/AiProfiles-Oxq1CrGx.js +0 -3
  109. package/dist/assets/Build-ClV9hssR.js +0 -40
  110. package/dist/assets/Calendars-7-56y5mC.js +0 -1
  111. package/dist/assets/Cluster--R890opw.js +0 -1
  112. package/dist/assets/CompositionCreateForm-BdvGldkE.js +0 -1
  113. package/dist/assets/Data-DjA5ylK5.js +0 -2
  114. package/dist/assets/DeviceDetail-CFwLMsHT.js +0 -2
  115. package/dist/assets/History-B5bG6vlI.js +0 -1
  116. package/dist/assets/Identity-DyHQGvZo.js +0 -1
  117. package/dist/assets/IntegrationDetail-BVrAcEgW.js +0 -1
  118. package/dist/assets/Integrations-BFIWnI-V.js +0 -1
  119. package/dist/assets/Integrations-goqSS2Co.js +0 -1
  120. package/dist/assets/Locations-DnHuXobL.js +0 -3
  121. package/dist/assets/Notifications-BbWZVYMp.js +0 -1
  122. package/dist/assets/Pipeline-DnMSSKKQ.js +0 -1
  123. package/dist/assets/Recordings-COnQ9TJI.js +0 -1
  124. package/dist/assets/SessionsList-oD9auhKo.js +0 -2
  125. package/dist/assets/circle-slash-D9Rnn9o4.js +0 -1
  126. package/dist/assets/client-Dg4TgH9L.js +0 -358
  127. package/dist/assets/crop-CI76MCfX.js +0 -1
  128. package/dist/assets/device-picker-model-DsmNRKVu.js +0 -1
  129. package/dist/assets/dist-CBqJveNL.js +0 -358
  130. package/dist/assets/event-targets-l8cnjyVU.js +0 -2
  131. package/dist/assets/i18n-BaJUrB-h.js +0 -1
  132. package/dist/assets/index-0CJf6Ok9.js +0 -4
  133. package/dist/assets/index-BMUVtgYi.css +0 -2
  134. package/dist/assets/mount-DsmNRKVu.js +0 -1
  135. package/dist/assets/remoteEntry-BJhQZDNB.js +0 -1
  136. package/dist/assets/src-Dh2VEGoU.js +0 -67
  137. package/dist/assets/step-inspector-model-COoD29sy.js +0 -1
  138. package/dist/assets/useEventInvalidation-DsmNRKVu.js +0 -1
  139. package/dist/assets/user-round-D9W5XNev.js +0 -1
@@ -5435,7 +5435,7 @@ var ZodIssueCode = {
5435
5435
  var ZodFirstPartyTypeKind;
5436
5436
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5437
5437
  //#endregion
5438
- //#region ../types/dist/sleep-BErAxhul.mjs
5438
+ //#region ../types/dist/sleep-CARd6wZF.mjs
5439
5439
  /**
5440
5440
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5441
5441
  * window to float samples (D455).
@@ -7922,6 +7922,52 @@ var RecordKindDeclarationSchema = object({
7922
7922
  }).strict();
7923
7923
  /** The kind names: lower-case, dash separated, short enough for an index name. */
7924
7924
  var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
7925
+ var READING_QUANTITIES = [
7926
+ "temperature",
7927
+ "pressure",
7928
+ "speed",
7929
+ "precipitation",
7930
+ "precipitation-rate",
7931
+ "length",
7932
+ "area",
7933
+ "illuminance",
7934
+ "irradiance",
7935
+ "power",
7936
+ "energy",
7937
+ "voltage",
7938
+ "current",
7939
+ "apparent-power",
7940
+ "humidity",
7941
+ "percentage",
7942
+ "concentration-volume",
7943
+ "concentration-mass",
7944
+ "concentration-ppb",
7945
+ "volume",
7946
+ "duration",
7947
+ "duration-ms",
7948
+ "information",
7949
+ "sound-level",
7950
+ "signal-level",
7951
+ "colour-temperature",
7952
+ "angle",
7953
+ "bitrate",
7954
+ "rate",
7955
+ "mass",
7956
+ "index",
7957
+ "ratio",
7958
+ "count",
7959
+ "none",
7960
+ "device"
7961
+ ];
7962
+ var ReadingQuantitySchema = _enum(READING_QUANTITIES);
7963
+ new Set(READING_QUANTITIES);
7964
+ function readingMeta(quantity, extras) {
7965
+ return {
7966
+ quantity,
7967
+ ...extras?.precision !== void 0 ? { precision: extras.precision } : {},
7968
+ ...extras?.label !== void 0 ? { label: extras.label } : {}
7969
+ };
7970
+ }
7925
7971
  /** Shared Zod schemas used across streaming capabilities. */
7926
7972
  var CamProfileSchema = _enum([
7927
7973
  "high",
@@ -7949,8 +7995,8 @@ var CamStreamKindSchema = _enum([
7949
7995
  "derived"
7950
7996
  ]);
7951
7997
  var CamStreamResolutionSchema = object({
7952
- width: number().int().positive(),
7953
- height: number().int().positive()
7998
+ width: number().int().positive().meta(readingMeta("none")),
7999
+ height: number().int().positive().meta(readingMeta("none"))
7954
8000
  });
7955
8001
  var CameraStreamSchema = object({
7956
8002
  /** Stable, provider-assigned id unique within the (deviceId) scope. */
@@ -8611,14 +8657,6 @@ Object.freeze({
8611
8657
  {
8612
8658
  "path": "aqi",
8613
8659
  "valueType": "number"
8614
- },
8615
- {
8616
- "path": "unit",
8617
- "valueType": "string"
8618
- },
8619
- {
8620
- "path": "precision",
8621
- "valueType": "number"
8622
8660
  }
8623
8661
  ],
8624
8662
  "alarm-panel": [
@@ -8654,20 +8692,10 @@ Object.freeze({
8654
8692
  "valueType": "boolean"
8655
8693
  }
8656
8694
  ],
8657
- "ambient-light-sensor": [
8658
- {
8659
- "path": "lux",
8660
- "valueType": "number"
8661
- },
8662
- {
8663
- "path": "unit",
8664
- "valueType": "string"
8665
- },
8666
- {
8667
- "path": "precision",
8668
- "valueType": "number"
8669
- }
8670
- ],
8695
+ "ambient-light-sensor": [{
8696
+ "path": "lux",
8697
+ "valueType": "number"
8698
+ }],
8671
8699
  "audio-metrics": [
8672
8700
  {
8673
8701
  "path": "ts",
@@ -8747,6 +8775,10 @@ Object.freeze({
8747
8775
  "path": "lastUpdated",
8748
8776
  "valueType": "number"
8749
8777
  },
8778
+ {
8779
+ "path": "volts",
8780
+ "valueType": "number"
8781
+ },
8750
8782
  {
8751
8783
  "path": "lastContactAt",
8752
8784
  "valueType": "number"
@@ -8989,10 +9021,6 @@ Object.freeze({
8989
9021
  "datetime"
8990
9022
  ]
8991
9023
  },
8992
- {
8993
- "path": "unit",
8994
- "valueType": "string"
8995
- },
8996
9024
  {
8997
9025
  "path": "min",
8998
9026
  "valueType": "number"
@@ -9004,19 +9032,6 @@ Object.freeze({
9004
9032
  {
9005
9033
  "path": "step",
9006
9034
  "valueType": "number"
9007
- },
9008
- {
9009
- "path": "precision",
9010
- "valueType": "number"
9011
- },
9012
- {
9013
- "path": "format",
9014
- "valueType": "enum",
9015
- "enumValues": [
9016
- "date",
9017
- "time",
9018
- "datetime"
9019
- ]
9020
9035
  }
9021
9036
  ],
9022
9037
  "cover": [
@@ -9257,13 +9272,8 @@ Object.freeze({
9257
9272
  "path": "value",
9258
9273
  "valueType": "string"
9259
9274
  }, {
9260
- "path": "format",
9261
- "valueType": "enum",
9262
- "enumValues": [
9263
- "date",
9264
- "time",
9265
- "datetime"
9266
- ]
9275
+ "path": "timestamp",
9276
+ "valueType": "boolean"
9267
9277
  }],
9268
9278
  "event-emitter": [
9269
9279
  {
@@ -9370,20 +9380,10 @@ Object.freeze({
9370
9380
  "valueType": "number"
9371
9381
  }
9372
9382
  ],
9373
- "humidity-sensor": [
9374
- {
9375
- "path": "percent",
9376
- "valueType": "number"
9377
- },
9378
- {
9379
- "path": "unit",
9380
- "valueType": "string"
9381
- },
9382
- {
9383
- "path": "precision",
9384
- "valueType": "number"
9385
- }
9386
- ],
9383
+ "humidity-sensor": [{
9384
+ "path": "percent",
9385
+ "valueType": "number"
9386
+ }],
9387
9387
  "image": [{
9388
9388
  "path": "url",
9389
9389
  "valueType": "string"
@@ -9722,20 +9722,10 @@ Object.freeze({
9722
9722
  "valueType": "boolean"
9723
9723
  }
9724
9724
  ],
9725
- "numeric-sensor": [
9726
- {
9727
- "path": "value",
9728
- "valueType": "number"
9729
- },
9730
- {
9731
- "path": "unit",
9732
- "valueType": "string"
9733
- },
9734
- {
9735
- "path": "precision",
9736
- "valueType": "number"
9737
- }
9738
- ],
9725
+ "numeric-sensor": [{
9726
+ "path": "value",
9727
+ "valueType": "number"
9728
+ }],
9739
9729
  "pet-feeder": [
9740
9730
  {
9741
9731
  "path": "foodLevel",
@@ -9827,14 +9817,6 @@ Object.freeze({
9827
9817
  {
9828
9818
  "path": "powerFactor",
9829
9819
  "valueType": "number"
9830
- },
9831
- {
9832
- "path": "unit",
9833
- "valueType": "string"
9834
- },
9835
- {
9836
- "path": "precision",
9837
- "valueType": "number"
9838
9820
  }
9839
9821
  ],
9840
9822
  "presence": [
@@ -9863,20 +9845,10 @@ Object.freeze({
9863
9845
  "valueType": "number"
9864
9846
  }
9865
9847
  ],
9866
- "pressure-sensor": [
9867
- {
9868
- "path": "hpa",
9869
- "valueType": "number"
9870
- },
9871
- {
9872
- "path": "unit",
9873
- "valueType": "string"
9874
- },
9875
- {
9876
- "path": "precision",
9877
- "valueType": "number"
9878
- }
9879
- ],
9848
+ "pressure-sensor": [{
9849
+ "path": "hpa",
9850
+ "valueType": "number"
9851
+ }],
9880
9852
  "privacy-mask": [{
9881
9853
  "path": "enabled",
9882
9854
  "valueType": "boolean"
@@ -10187,20 +10159,10 @@ Object.freeze({
10187
10159
  "path": "tampered",
10188
10160
  "valueType": "boolean"
10189
10161
  }],
10190
- "temperature-sensor": [
10191
- {
10192
- "path": "celsius",
10193
- "valueType": "number"
10194
- },
10195
- {
10196
- "path": "unit",
10197
- "valueType": "string"
10198
- },
10199
- {
10200
- "path": "precision",
10201
- "valueType": "number"
10202
- }
10203
- ],
10162
+ "temperature-sensor": [{
10163
+ "path": "celsius",
10164
+ "valueType": "number"
10165
+ }],
10204
10166
  "update": [
10205
10167
  {
10206
10168
  "path": "currentVersion",
@@ -10311,10 +10273,6 @@ Object.freeze({
10311
10273
  "path": "temperature",
10312
10274
  "valueType": "number"
10313
10275
  },
10314
- {
10315
- "path": "temperatureUnit",
10316
- "valueType": "string"
10317
- },
10318
10276
  {
10319
10277
  "path": "humidity",
10320
10278
  "valueType": "number"
@@ -10323,18 +10281,10 @@ Object.freeze({
10323
10281
  "path": "pressure",
10324
10282
  "valueType": "number"
10325
10283
  },
10326
- {
10327
- "path": "pressureUnit",
10328
- "valueType": "string"
10329
- },
10330
10284
  {
10331
10285
  "path": "windSpeed",
10332
10286
  "valueType": "number"
10333
10287
  },
10334
- {
10335
- "path": "windSpeedUnit",
10336
- "valueType": "string"
10337
- },
10338
10288
  {
10339
10289
  "path": "windBearing",
10340
10290
  "valueType": "number"
@@ -17682,7 +17632,8 @@ var CompositionProblemSchema = object({
17682
17632
  "item-field-reserved",
17683
17633
  "target-missing",
17684
17634
  "target-unreadable",
17685
- "not-replaceable"
17635
+ "not-replaceable",
17636
+ "native-capability-replace"
17686
17637
  ]),
17687
17638
  path: string(),
17688
17639
  message: string()
@@ -17740,9 +17691,11 @@ var CompositionFieldPlanSchema = object({
17740
17691
  reads: array(CompositionFieldReadSchema)
17741
17692
  });
17742
17693
  /**
17743
- * What a feature does to its target (D663): `new` builds a capability on a new
17744
- * device; on an existing device it `add`s a capability the device lacks, or
17745
- * `replace`s named fields of one it has natively.
17694
+ * What a feature does to its target (D663, D944): `new` builds a capability on a
17695
+ * new device; on an existing device it `add`s a capability the device lacks.
17696
+ * Replacing a capability the device already serves is refused
17697
+ * (`native-capability-replace`), not planned. `replace` remains on the claim
17698
+ * wire for a claim the hub already holds.
17746
17699
  */
17747
17700
  var CompositionFeatureModeSchema = _enum([
17748
17701
  "new",
@@ -19628,12 +19581,12 @@ method(object({ deviceId: number().int().nonnegative() }), DeviceAdminLinkSchema
19628
19581
  * providers detect renames by matching `uniqueId` and patch `id`
19629
19582
  * in-place so dispatch keeps working.
19630
19583
  *
19631
- * **Rendering metadata is NOT stored here.** Display fields (`unit`,
19632
- * `precision`) live in the cap STATUS SLICE so they flow live with every
19633
- * state push and are the single source of truth. The `device_class` tag is
19634
- * an HA-specific input that is consumed at the discovery boundary
19635
- * (`ha-domain-mapping`) to derive the CamStack `DeviceRole`; it is never
19636
- * stored after that point.
19584
+ * **Display unit and precision are NOT stored here.** They are a read-time
19585
+ * preference (D944). A polymorphic reading stores its `quantity` here once,
19586
+ * at adopt, and a date/time value stores `dateTimeShape` the same way —
19587
+ * neither is repeated on each sample. The `device_class` tag is an
19588
+ * HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
19589
+ * to derive the CamStack `DeviceRole`; it is never stored after that point.
19637
19590
  *
19638
19591
  * The `raw` escape hatch carries provider-specific attributes that don't
19639
19592
  * fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
@@ -19647,6 +19600,14 @@ var SourceInfoSchema = object({
19647
19600
  /** Immutable upstream identifier when available (HA `unique_id`, … ).
19648
19601
  * Used to detect rename when `id` changes. */
19649
19602
  uniqueId: string().optional(),
19603
+ /** Quantity of a polymorphic reading, written once at adopt (D944). */
19604
+ quantity: ReadingQuantitySchema.optional(),
19605
+ /** Date / time / datetime shape of a string reading, written once at adopt. */
19606
+ dateTimeShape: _enum([
19607
+ "date",
19608
+ "time",
19609
+ "datetime"
19610
+ ]).optional(),
19650
19611
  /** Provider-specific extras that don't fit the structured slots.
19651
19612
  * Whitelist what goes here per provider — do NOT dump entire HA
19652
19613
  * attribute blobs (would explode the meta JSON column). */
@@ -40559,7 +40520,8 @@ method(object({
40559
40520
  "enum"
40560
40521
  ]),
40561
40522
  enumValues: array(string()).optional(),
40562
- item: boolean().optional()
40523
+ item: boolean().optional(),
40524
+ quantity: ReadingQuantitySchema.optional()
40563
40525
  })).readonly(),
40564
40526
  itemArray: object({
40565
40527
  path: string(),
@@ -44359,8 +44321,8 @@ var MaskPolygonShapeSchema = object({
44359
44321
  /** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
44360
44322
  var MaskGridShapeSchema = object({
44361
44323
  kind: literal("grid"),
44362
- gridWidth: number(),
44363
- gridHeight: number(),
44324
+ gridWidth: number().meta(readingMeta("none")),
44325
+ gridHeight: number().meta(readingMeta("none")),
44364
44326
  cells: array(boolean())
44365
44327
  });
44366
44328
  discriminatedUnion("kind", [
@@ -55754,46 +55716,27 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
55754
55716
  }), LogStreamEntrySchema, { kind: "subscription" });
55755
55717
  object({
55756
55718
  /** Carbon dioxide concentration in ppm. */
55757
- co2Ppm: number().min(0).optional(),
55719
+ co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
55758
55720
  /** Total volatile organic compounds in ppb. */
55759
- vocPpb: number().min(0).optional(),
55721
+ vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
55760
55722
  /** Particulate matter ≤ 2.5 μm in µg/m³. */
55761
- pm25: number().min(0).optional(),
55723
+ pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
55762
55724
  /** Particulate matter ≤ 10 μm in µg/m³. */
55763
- pm10: number().min(0).optional(),
55725
+ pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
55764
55726
  /** Composite AQI value (typically 0..500). */
55765
- aqi: number().optional(),
55727
+ aqi: number().optional().meta(readingMeta("index")),
55766
55728
  /** Ms epoch when the slice was last updated. */
55767
- lastFetchedAt: number(),
55768
- /** Live display unit of the single metric this slice carries (e.g. HA
55769
- * `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
55770
- * upstream `sensor.*` entity surfaces ONE device_class, so one unit
55771
- * per slice is unambiguous. */
55772
- unit: string().optional(),
55773
- /** Suggested decimal places for numeric display.
55774
- * Populated live from the upstream source when provided (e.g. HA
55775
- * `attributes.suggested_display_precision`). Falls back to
55776
- * auto-formatting when absent. */
55777
- precision: number().int().min(0).max(10).optional()
55729
+ lastFetchedAt: number()
55778
55730
  });
55779
55731
  DeviceType.Sensor;
55780
55732
  object({
55781
- /** Current illuminance in lux (lx). */
55782
- lux: number().min(0).meta({
55783
- unit: "lx",
55733
+ /** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
55734
+ lux: number().min(0).nullable().meta(readingMeta("illuminance", {
55735
+ precision: 0,
55784
55736
  label: "Illuminance"
55785
- }),
55737
+ })),
55786
55738
  /** Ms epoch when the slice was last updated. */
55787
- lastFetchedAt: number(),
55788
- /** Live display unit from the upstream source (e.g. HA
55789
- * `attributes.unit_of_measurement`). The UI prefers this over the
55790
- * role's canonical unit. Absent → fall back to the canonical unit. */
55791
- unit: string().optional(),
55792
- /** Suggested decimal places for numeric display.
55793
- * Populated live from the upstream source when provided (e.g. HA
55794
- * `attributes.suggested_display_precision`). Falls back to
55795
- * auto-formatting when absent. */
55796
- precision: number().int().min(0).max(10).optional()
55739
+ lastFetchedAt: number()
55797
55740
  });
55798
55741
  DeviceType.Sensor;
55799
55742
  /**
@@ -55938,10 +55881,7 @@ var BatteryStatusSchema = object({
55938
55881
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
55939
55882
  * declines to band a non-finite reading, which is the correct shape.
55940
55883
  */
55941
- percentage: number().min(0).max(100).nullable().meta({
55942
- unit: "%",
55943
- label: "Charge"
55944
- }),
55884
+ percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
55945
55885
  /**
55946
55886
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
55947
55887
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -55968,7 +55908,14 @@ var BatteryStatusSchema = object({
55968
55908
  */
55969
55909
  sleeping: boolean().meta({ label: "Sleeping" }),
55970
55910
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
55971
- lastUpdated: number(),
55911
+ lastUpdated: number().meta(readingMeta("none")),
55912
+ /**
55913
+ * Battery voltage in volts, for a cell the firmware reports as voltage
55914
+ * rather than a percentage (Ecowitt capacitor packs). Absent on cameras
55915
+ * that only report charge. A second reading on this cap, not a second
55916
+ * `numeric-sensor` on the same device.
55917
+ */
55918
+ volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
55972
55919
  /**
55973
55920
  * Ms epoch of the last time the device PROVED it was reachable — a
55974
55921
  * completed firmware round-trip, an observed wake, or an inbound push
@@ -56028,7 +55975,7 @@ object({
56028
55975
  DeviceType.Sensor;
56029
55976
  object({
56030
55977
  /** Current level as 0..100 inclusive. Firmware-reported. */
56031
- percentage: number().min(0).max(100),
55978
+ percentage: number().min(0).max(100).meta(readingMeta("percentage")),
56032
55979
  /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
56033
55980
  lastChangedAt: number()
56034
55981
  });
@@ -56413,21 +56360,21 @@ object({
56413
56360
  /** Single setpoint in Celsius. Used by single-target modes
56414
56361
  * (heat / cool / dry). Null when the device is in a range mode
56415
56362
  * (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
56416
- target: number().nullable(),
56363
+ target: number().nullable().meta(readingMeta("temperature")),
56417
56364
  /** Upper bound of the dual setpoint in Celsius. Populated only
56418
56365
  * in `heat_cool` mode. */
56419
- targetHigh: number().nullable(),
56366
+ targetHigh: number().nullable().meta(readingMeta("temperature")),
56420
56367
  /** Lower bound of the dual setpoint in Celsius. Populated only
56421
56368
  * in `heat_cool` mode. */
56422
- targetLow: number().nullable(),
56369
+ targetLow: number().nullable().meta(readingMeta("temperature")),
56423
56370
  /** Target relative humidity (0..100). Null when the device has no
56424
56371
  * humidity control. */
56425
- targetHumidity: number().min(0).max(100).nullable(),
56372
+ targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
56426
56373
  /** Current measured temperature in Celsius. Read-only — pushed by
56427
56374
  * the device's internal sensor. */
56428
- currentTemp: number().nullable(),
56375
+ currentTemp: number().nullable().meta(readingMeta("temperature")),
56429
56376
  /** Current measured relative humidity (0..100). Read-only. */
56430
- currentHumidity: number().min(0).max(100).nullable(),
56377
+ currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
56431
56378
  /** Vertical louver swing on/off. Null when the device has no vertical
56432
56379
  * swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
56433
56380
  swingVertical: boolean().nullable(),
@@ -56435,11 +56382,11 @@ object({
56435
56382
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
56436
56383
  swingHorizontal: boolean().nullable(),
56437
56384
  /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
56438
- minTemp: number().nullable().optional(),
56385
+ minTemp: number().nullable().optional().meta(readingMeta("temperature")),
56439
56386
  /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
56440
- maxTemp: number().nullable().optional(),
56387
+ maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
56441
56388
  /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
56442
- tempStep: number().positive().nullable().optional(),
56389
+ tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
56443
56390
  /** Ms epoch when the slice was last updated (push or command). */
56444
56391
  lastFetchedAt: number()
56445
56392
  });
@@ -56518,17 +56465,17 @@ DeviceType.Thermostat, DeviceType.Climate, method(object({
56518
56465
  * modes to expose in the picker without a slice fetch.
56519
56466
  */
56520
56467
  var RgbTripletSchema = object({
56521
- r: number().int().min(0).max(255),
56522
- g: number().int().min(0).max(255),
56523
- b: number().int().min(0).max(255)
56468
+ r: number().int().min(0).max(255).meta(readingMeta("none")),
56469
+ g: number().int().min(0).max(255).meta(readingMeta("none")),
56470
+ b: number().int().min(0).max(255).meta(readingMeta("none"))
56524
56471
  });
56525
56472
  var HsvTripletSchema = object({
56526
56473
  /** Hue in degrees, 0..360. */
56527
- h: number().min(0).max(360),
56474
+ h: number().min(0).max(360).meta(readingMeta("none")),
56528
56475
  /** Saturation as 0..100 inclusive. */
56529
- s: number().min(0).max(100),
56476
+ s: number().min(0).max(100).meta(readingMeta("none")),
56530
56477
  /** Value/brightness as 0..100 inclusive. */
56531
- v: number().min(0).max(100)
56478
+ v: number().min(0).max(100).meta(readingMeta("none"))
56532
56479
  });
56533
56480
  var ColorInputSchema = discriminatedUnion("mode", [
56534
56481
  object({
@@ -56557,7 +56504,7 @@ object({
56557
56504
  /** Populated when `mode === 'hsv'`. */
56558
56505
  hsv: HsvTripletSchema.optional(),
56559
56506
  /** Populated when `mode === 'mired'`. */
56560
- mireds: number().int().optional(),
56507
+ mireds: number().int().optional().meta(readingMeta("colour-temperature")),
56561
56508
  /** Ms epoch of the last operator-driven change. */
56562
56509
  lastChangedAt: number()
56563
56510
  });
@@ -56681,18 +56628,12 @@ _enum([
56681
56628
  "empty",
56682
56629
  "full"
56683
56630
  ]);
56684
- /** Units a remaining-life counter is reported in. Closed: the panel renders
56685
- * each. */
56686
- var ConsumableRemainingUnitSchema = _enum([
56687
- "days",
56688
- "hours",
56689
- "cycles"
56690
- ]);
56691
- /** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
56692
- * days). `value` null = unknown (D393). */
56631
+ /** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
56632
+ var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
56633
+ /** Remaining life. `value` null = unknown (D393). Duration is seconds. */
56693
56634
  var ConsumableRemainingSchema = object({
56694
- value: number().min(0).nullable(),
56695
- unit: ConsumableRemainingUnitSchema
56635
+ value: number().min(0).nullable().meta(readingMeta("device")),
56636
+ kind: ConsumableRemainingKindSchema
56696
56637
  });
56697
56638
  var ConsumablesStatusSchema = object({
56698
56639
  items: array(object({
@@ -56701,7 +56642,7 @@ var ConsumablesStatusSchema = object({
56701
56642
  /** Display name. */
56702
56643
  label: string().min(1),
56703
56644
  /** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
56704
- level: number().min(0).max(100).nullable(),
56645
+ level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
56705
56646
  /** The provider's discrete verdict when known (binary mode). */
56706
56647
  status: ConsumableStatusSchema.nullable(),
56707
56648
  /** What the item is — decides whether low or full is bad. Absent = `wear`. */
@@ -56718,7 +56659,7 @@ var ConsumablesStatusSchema = object({
56718
56659
  })),
56719
56660
  lastChangedAt: number()
56720
56661
  });
56721
- Object.values(DeviceType), [...ConsumableRemainingUnitSchema.options], [...ConsumableStatusSchema.options], [...ConsumableKindSchema.options], [...ConsumableFormSchema.options];
56662
+ Object.values(DeviceType), [...ConsumableRemainingKindSchema.options], [...ConsumableStatusSchema.options], [...ConsumableKindSchema.options], [...ConsumableFormSchema.options];
56722
56663
  Object.values(DeviceType), method(object({
56723
56664
  deviceId: number().int().nonnegative(),
56724
56665
  key: string().min(1)
@@ -56743,39 +56684,17 @@ object({
56743
56684
  /** Current value. Numeric kind → number; select/text/datetime → string.
56744
56685
  * Datetime values are ISO 8601 strings (full timestamp, date-only,
56745
56686
  * or time-only — HA accepts all three forms). */
56746
- value: union([number(), string()]),
56687
+ value: union([number().meta(readingMeta("device")), string()]),
56747
56688
  /** Acceptable values when `kind === 'select'`. Empty for other kinds. */
56748
56689
  options: array(string()),
56749
56690
  /** Ms epoch when the slice was last updated. */
56750
56691
  lastChangedAt: number(),
56751
- /** Display unit-of-measurement (e.g. '°C', '%', 'lx').
56752
- * Populated live from HA `attributes.unit_of_measurement` on each push.
56753
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
56754
- unit: string().optional(),
56755
- /** Minimum allowed value. Populated live from HA `attributes.min`.
56756
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
56757
- min: number().optional(),
56758
- /** Maximum allowed value. Populated live from HA `attributes.max`.
56759
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
56760
- max: number().optional(),
56761
- /** Step increment. Populated live from HA `attributes.step`.
56762
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
56763
- step: number().optional(),
56764
- /** Suggested decimal places for numeric display. Populated live from HA
56765
- * `attributes.suggested_display_precision`, or derived from `step` when the
56766
- * HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
56767
- * other kinds. Falls back to auto-formatting when absent. */
56768
- precision: number().int().min(0).max(10).optional(),
56769
- /** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
56770
- * 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
56771
- * (UI defaults to 'datetime'). Derived live from the device's HA domain
56772
- * (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
56773
- * `has_date`/`has_time` attributes on each push. */
56774
- format: _enum([
56775
- "date",
56776
- "time",
56777
- "datetime"
56778
- ]).optional()
56692
+ /** Minimum allowed value, in the device quantity's base unit. */
56693
+ min: number().optional().meta(readingMeta("device")),
56694
+ /** Maximum allowed value, in the device quantity's base unit. */
56695
+ max: number().optional().meta(readingMeta("device")),
56696
+ /** Step increment, in the device quantity's base unit. */
56697
+ step: number().optional().meta(readingMeta("device"))
56779
56698
  });
56780
56699
  var ControlSetValueInputSchema = discriminatedUnion("kind", [
56781
56700
  object({
@@ -56813,9 +56732,9 @@ object({
56813
56732
  ]),
56814
56733
  /** 0 = fully closed, 100 = fully open. Null when the device has
56815
56734
  * no intermediate position surface. */
56816
- position: number().min(0).max(100).nullable(),
56735
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
56817
56736
  /** 0 = closed slats, 100 = open slats. Null when no tilt support. */
56818
- tiltPosition: number().min(0).max(100).nullable(),
56737
+ tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
56819
56738
  /** Ms epoch when the slice was last updated. */
56820
56739
  lastChangedAt: number()
56821
56740
  });
@@ -56873,9 +56792,9 @@ var NormalizedRangeSchema$1 = object({
56873
56792
  object({
56874
56793
  mode: DayNightModeSchema,
56875
56794
  /** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
56876
- sensitivity: number().optional(),
56795
+ sensitivity: number().optional().meta(readingMeta("none")),
56877
56796
  /** Delay before the IR-cut filter flips, in seconds. */
56878
- switchDelaySec: number().optional(),
56797
+ switchDelaySec: number().optional().meta(readingMeta("duration")),
56879
56798
  lastFetchedAt: number()
56880
56799
  });
56881
56800
  /**
@@ -57030,11 +56949,11 @@ object({
57030
56949
  /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
57031
56950
  availablePrograms: array(string()),
57032
56951
  /** Seconds remaining on the active cycle, or null when not running / not known. */
57033
- remainingSeconds: number().int().nonnegative().nullable(),
56952
+ remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
57034
56953
  /** Seconds the delayed start is set to (time until the cycle starts), or null. */
57035
- delayedStartSeconds: number().int().nonnegative().nullable(),
56954
+ delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
57036
56955
  /** Cycle completion 0..100, or null when not applicable. */
57037
- progressPercent: number().min(0).max(100).nullable(),
56956
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57038
56957
  /** True when the door is reported open; null when the door state is not known. */
57039
56958
  doorOpen: boolean().nullable(),
57040
56959
  /** True when the door is latched (locked) during a cycle; null when not reported. */
@@ -57058,7 +56977,7 @@ object({
57058
56977
  /** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
57059
56978
  machineCareDue: boolean().nullable(),
57060
56979
  /** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
57061
- machineCareRemainingRuns: number().int().nonnegative().nullable(),
56980
+ machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
57062
56981
  /** Option states: null when the appliance does not expose the option or has not said. */
57063
56982
  halfLoad: boolean().nullable(),
57064
56983
  hygienePlus: boolean().nullable(),
@@ -57073,7 +56992,7 @@ object({
57073
56992
  /** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
57074
56993
  activeEvents: array(string()),
57075
56994
  /** Numeric error code when the appliance reports one; null otherwise. */
57076
- errorCode: number().nullable(),
56995
+ errorCode: number().nullable().meta(readingMeta("none")),
57077
56996
  /** Name of the raised alert-level event while the appliance is in error; null otherwise. */
57078
56997
  errorLabel: string().nullable(),
57079
56998
  /** Ms epoch of the last value received from the appliance; 0 = never. */
@@ -57219,24 +57138,14 @@ object({
57219
57138
  /** Ms epoch of the last press. null = never observed since this provider started. */
57220
57139
  lastPressedAt: number().nullable(),
57221
57140
  /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
57222
- pressCountSinceStart: number()
57141
+ pressCountSinceStart: number().meta(readingMeta("count"))
57223
57142
  });
57224
57143
  object({
57225
57144
  deviceId: number(),
57226
57145
  timestamp: number()
57227
57146
  });
57228
57147
  DeviceType.Button, DeviceType.Camera;
57229
- /**
57230
- * Enum-state sensor — a string value picked from a finite option set.
57231
- * Drives HA `sensor` entries with `state_class: enum` (HVAC action
57232
- * states, weather conditions, contact-source channels, mode strings).
57233
- * The option set is stable across observations; it is persisted in the
57234
- * device config blob (via the control slice at adoption) and is NOT
57235
- * stored in `sourceInfo`.
57236
- */
57237
- /** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
57238
- * enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
57239
- var EnumSensorDateTimeFormatSchema = _enum([
57148
+ _enum([
57240
57149
  "date",
57241
57150
  "time",
57242
57151
  "datetime"
@@ -57244,12 +57153,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
57244
57153
  object({
57245
57154
  value: string(),
57246
57155
  /**
57247
- * Set for `DateTimeSensor`-role sensors so the UI renders the ISO `value`
57248
- * as a locale date/time/datetime; absent for plain enum sensors. HA
57249
- * `device_class=timestamp` → `'datetime'`, `device_class=date` → `'date'`,
57250
- * a time-only sensor → `'time'`.
57156
+ * Set when `value` is a timestamp rather than an enum state, so history
57157
+ * does not learn ISO strings as states. The display shape (date / time /
57158
+ * datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
57251
57159
  */
57252
- format: EnumSensorDateTimeFormatSchema.optional(),
57160
+ timestamp: boolean().optional(),
57253
57161
  /** Ms epoch when the slice was last updated. */
57254
57162
  lastFetchedAt: number()
57255
57163
  });
@@ -57268,13 +57176,13 @@ var EventFireSchema = object({
57268
57176
  deviceId: number(),
57269
57177
  eventType: string(),
57270
57178
  data: record(string(), unknown()).nullable(),
57271
- timestamp: number(),
57272
- seq: number()
57179
+ timestamp: number().meta(readingMeta("none")),
57180
+ seq: number().meta(readingMeta("count"))
57273
57181
  });
57274
57182
  object({
57275
57183
  eventTypes: array(string()),
57276
57184
  lastEvent: EventFireSchema.nullable(),
57277
- eventCountSinceStart: number()
57185
+ eventCountSinceStart: number().meta(readingMeta("count"))
57278
57186
  });
57279
57187
  DeviceType.EventEmitter;
57280
57188
  var IdentitySchema = object({
@@ -57663,10 +57571,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
57663
57571
  object({
57664
57572
  /** Active speed as 0..100 inclusive. Null when the device has no
57665
57573
  * speed surface (single-speed fan). */
57666
- percentage: number().min(0).max(100).nullable(),
57574
+ percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57667
57575
  /** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
57668
57576
  * a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
57669
- percentageStep: number().positive().optional(),
57577
+ percentageStep: number().positive().optional().meta(readingMeta("percentage")),
57670
57578
  /** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
57671
57579
  * — empty string when no preset is active or supported. */
57672
57580
  preset: string(),
@@ -57748,7 +57656,7 @@ var FeatureProbeStatusSchema = object({
57748
57656
  /** Camera/firmware model string. `null` when the firmware doesn't expose it. */
57749
57657
  model: string().nullable(),
57750
57658
  /** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
57751
- channelCount: number().nullable(),
57659
+ channelCount: number().nullable().meta(readingMeta("count")),
57752
57660
  /**
57753
57661
  * Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
57754
57662
  * completes — drivers' `getAccessoryChildren()` should treat zero as
@@ -57782,7 +57690,7 @@ object({
57782
57690
  DeviceType.Sensor;
57783
57691
  object({
57784
57692
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
57785
- position: number().min(0).max(100).nullable(),
57693
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57786
57694
  /** Ms epoch when the slice was last updated (push or poll). */
57787
57695
  lastFetchedAt: number()
57788
57696
  });
@@ -57791,9 +57699,9 @@ object({
57791
57699
  /** Whether the humidifier is currently on. */
57792
57700
  on: boolean(),
57793
57701
  /** Current measured relative humidity (0..100). Null when not reported. */
57794
- currentHumidity: number().min(0).max(100).nullable(),
57702
+ currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
57795
57703
  /** Target relative humidity (0..100). Null when no setpoint surface. */
57796
- targetHumidity: number().min(0).max(100).nullable(),
57704
+ targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
57797
57705
  /** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
57798
57706
  * device has no mode surface. */
57799
57707
  mode: string().nullable(),
@@ -57803,9 +57711,9 @@ object({
57803
57711
  * `idle` / `off`). Null when the device doesn't report it. */
57804
57712
  action: string().nullable(),
57805
57713
  /** HA `min_humidity` attribute. Null → UI uses 0. */
57806
- minHumidity: number().nullable(),
57714
+ minHumidity: number().nullable().meta(readingMeta("humidity")),
57807
57715
  /** HA `max_humidity` attribute. Null → UI uses 100. */
57808
- maxHumidity: number().nullable(),
57716
+ maxHumidity: number().nullable().meta(readingMeta("humidity")),
57809
57717
  /** Ms epoch when the slice was last updated. */
57810
57718
  lastChangedAt: number()
57811
57719
  });
@@ -57829,22 +57737,13 @@ DeviceType.Humidifier, method(object({
57829
57737
  auth: "protected"
57830
57738
  });
57831
57739
  object({
57832
- /** Current relative humidity, 0..100. */
57833
- percent: number().min(0).max(100).meta({
57834
- unit: "%",
57740
+ /** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
57741
+ percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
57742
+ precision: 0,
57835
57743
  label: "Humidity"
57836
- }),
57744
+ })),
57837
57745
  /** Ms epoch when the slice was last updated. */
57838
- lastFetchedAt: number(),
57839
- /** Live display unit from the upstream source (e.g. HA
57840
- * `attributes.unit_of_measurement`). The UI prefers this over the
57841
- * role's canonical unit. Absent → fall back to the canonical unit. */
57842
- unit: string().optional(),
57843
- /** Suggested decimal places for numeric display.
57844
- * Populated live from the upstream source when provided (e.g. HA
57845
- * `attributes.suggested_display_precision`). Falls back to
57846
- * auto-formatting when absent. */
57847
- precision: number().int().min(0).max(10).optional()
57746
+ lastFetchedAt: number()
57848
57747
  });
57849
57748
  DeviceType.Sensor, DeviceType.Thermostat;
57850
57749
  object({
@@ -57852,7 +57751,7 @@ object({
57852
57751
  * entity exposes no `entity_picture` (yet). */
57853
57752
  url: string().nullable(),
57854
57753
  /** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
57855
- lastUpdated: number().nullable()
57754
+ lastUpdated: number().nullable().meta(readingMeta("none"))
57856
57755
  });
57857
57756
  DeviceType.Image;
57858
57757
  /**
@@ -57908,19 +57807,19 @@ var NormalizedRangeSchema = object({
57908
57807
  });
57909
57808
  object({
57910
57809
  /** Normalized 0–100. */
57911
- brightness: number().optional(),
57810
+ brightness: number().optional().meta(readingMeta("none")),
57912
57811
  /** Normalized 0–100. */
57913
- contrast: number().optional(),
57812
+ contrast: number().optional().meta(readingMeta("none")),
57914
57813
  /** Normalized 0–100. */
57915
- saturation: number().optional(),
57814
+ saturation: number().optional().meta(readingMeta("none")),
57916
57815
  /** Normalized 0–100. */
57917
- sharpness: number().optional(),
57816
+ sharpness: number().optional().meta(readingMeta("none")),
57918
57817
  mirror: boolean().optional(),
57919
57818
  flip: boolean().optional(),
57920
57819
  rotate: ImageRotateSchema.optional(),
57921
57820
  whiteBalance: WhiteBalanceModeSchema.optional(),
57922
57821
  /** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
57923
- warmth: number().optional(),
57822
+ warmth: number().optional().meta(readingMeta("none")),
57924
57823
  exposureMode: ExposureModeSchema.optional(),
57925
57824
  backlightMode: BacklightModeSchema.optional(),
57926
57825
  lastFetchedAt: number()
@@ -58165,10 +58064,10 @@ var IntercomAbilitySchema = object({
58165
58064
  /** Codecs accepted by firmware, in preference order. Driver-authored strings. */
58166
58065
  codecs: array(string()),
58167
58066
  /** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
58168
- sampleRate: number(),
58067
+ sampleRate: number().meta(readingMeta("none")),
58169
58068
  duplex: _enum(["half", "full"]),
58170
58069
  /** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
58171
- maxBacklogMs: number()
58070
+ maxBacklogMs: number().meta(readingMeta("duration-ms"))
58172
58071
  });
58173
58072
  var IntercomStatusSchema = object({
58174
58073
  /** True while a talk session is open. */
@@ -58279,13 +58178,13 @@ object({
58279
58178
  activity: LawnMowerActivitySchema,
58280
58179
  /** 0..100 battery percentage. Null when the device has no battery
58281
58180
  * reading. */
58282
- batteryLevel: number().min(0).max(100).nullable(),
58181
+ batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58283
58182
  /** 0..100 mowing-completion percentage of the current task, or null when no
58284
58183
  * task is active / progress is unavailable. */
58285
- progressPercent: number().min(0).max(100).nullable(),
58184
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58286
58185
  /** Current device/event code (dynamic — mostly status, sometimes an error),
58287
58186
  * or null when unknown. */
58288
- currentCode: number().nullable(),
58187
+ currentCode: number().nullable().meta(readingMeta("none")),
58289
58188
  /** Human label for {@link currentCode}, or null when undecodable. */
58290
58189
  currentCodeLabel: string().nullable(),
58291
58190
  /** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
@@ -58747,7 +58646,7 @@ object({
58747
58646
  state: MediaPlayerStateSchema,
58748
58647
  /** Volume as 0..100 inclusive. Null when the device has no volume
58749
58648
  * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
58750
- volumeLevel: number().min(0).max(100).nullable(),
58649
+ volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58751
58650
  /** Mute toggle distinct from volume=0. Null when no mute surface.
58752
58651
  * Pair with `DeviceFeature.MediaPlayerMute`. */
58753
58652
  isMuted: boolean().nullable(),
@@ -58760,10 +58659,10 @@ object({
58760
58659
  currentMedia: MediaInfoSchema.nullable(),
58761
58660
  /** Current playback position in ms. Null when not seekable or
58762
58661
  * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
58763
- positionMs: number().int().nonnegative().nullable(),
58662
+ positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
58764
58663
  /** Total duration of the current media in ms. Null when unknown
58765
58664
  * (live stream) or nothing is playing. */
58766
- durationMs: number().int().nonnegative().nullable(),
58665
+ durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
58767
58666
  /** Shuffle toggle. Null when no shuffle surface.
58768
58667
  * Pair with `DeviceFeature.MediaPlayerShuffle`. */
58769
58668
  shuffle: boolean().nullable(),
@@ -59063,7 +58962,7 @@ object({
59063
58962
  * it, every provider seeded it at boot, and every session left it
59064
58963
  * `detected: true` after its detach.
59065
58964
  */
59066
- autoClearAfterMs: number().nullable()
58965
+ autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
59067
58966
  });
59068
58967
  /**
59069
58968
  * Payload of `motion.onMotionChanged` event + the corresponding bus
@@ -59113,7 +59012,7 @@ var MotionZoneRegionSchema = object({
59113
59012
  });
59114
59013
  object({
59115
59014
  enabled: boolean(),
59116
- sensitivity: number(),
59015
+ sensitivity: number().meta(readingMeta("none")),
59117
59016
  /** Grid region(s). Today exactly one `grid` shape. */
59118
59017
  regions: array(MotionZoneRegionSchema),
59119
59018
  lastFetchedAt: number()
@@ -59417,7 +59316,7 @@ var NavigationStatusSchema = object({
59417
59316
  following: boolean(),
59418
59317
  flash: boolean(),
59419
59318
  lightMode: NavigationLightModeSchema,
59420
- lightLevel: number().min(40).max(100),
59319
+ lightLevel: number().min(40).max(100).meta(readingMeta("none")),
59421
59320
  /** Ms epoch when the slice was last updated. */
59422
59321
  lastChangedAt: number()
59423
59322
  });
@@ -59463,13 +59362,13 @@ var NetworkLinkStatusSchema = object({
59463
59362
  * one whose reading has not landed must not be drawn at 0 %. Consumers
59464
59363
  * SKIP a null rather than coerce it.
59465
59364
  */
59466
- signalPercent: number().min(0).max(100).nullable(),
59365
+ signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59467
59366
  /** Raw received signal strength in dBm, when the firmware reports one. */
59468
- rssiDbm: number().optional(),
59367
+ rssiDbm: number().optional().meta(readingMeta("signal-level")),
59469
59368
  /** Network name of a wireless link, when the firmware reports it. */
59470
59369
  ssid: string().optional(),
59471
59370
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
59472
- lastUpdated: number()
59371
+ lastUpdated: number().meta(readingMeta("none"))
59473
59372
  });
59474
59373
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.Light, DeviceType.Lock, DeviceType.Siren, object({
59475
59374
  deviceId: number(),
@@ -59788,7 +59687,7 @@ object({
59788
59687
  lastError: string().nullable(),
59789
59688
  /** Number of deliveries currently buffered server-side (rate-limited
59790
59689
  * notifiers may queue). 0 when the provider sends synchronously. */
59791
- queueDepth: number().int().nonnegative(),
59690
+ queueDepth: number().int().nonnegative().meta(readingMeta("count")),
59792
59691
  /** Per-feature capability matrix — authoritative source for the
59793
59692
  * compose-form field gating. */
59794
59693
  supports: NotifierSupportsSchema
@@ -59838,15 +59737,8 @@ DeviceType.Notifier, method(NotifierSendInputSchema, NotifierSendResultSchema, {
59838
59737
  acceptedAt: number()
59839
59738
  });
59840
59739
  object({
59841
- value: number(),
59842
- /** Display unit-of-measurement (e.g. 'dBm', 's', 'rpm', 'steps').
59843
- * Populated live from the upstream source on each state push.
59844
- * Absent for unitless sensors. */
59845
- unit: string().optional(),
59846
- /** Suggested decimal places for numeric display.
59847
- * Populated live from the upstream source when provided.
59848
- * Falls back to auto-formatting when absent. */
59849
- precision: number().int().min(0).max(10).optional(),
59740
+ /** Reading in the device quantity's base unit. `null` when refused (D393). */
59741
+ value: number().nullable().meta(readingMeta("device")),
59850
59742
  /** Ms epoch when the slice was last updated. */
59851
59743
  lastFetchedAt: number()
59852
59744
  });
@@ -60136,23 +60028,26 @@ object({
60136
60028
  /** Food currently in the bowl (grams). Null when the device has not
60137
60029
  * reported a reading yet. On dual-hopper models this is the combined
60138
60030
  * bowl reading; per-hopper levels live in `food1`/`food2`. */
60139
- foodLevel: number().nullable(),
60031
+ foodLevel: number().nullable().meta(readingMeta("none")),
60140
60032
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
60141
60033
  * single-hopper models. */
60142
- food1: number().nullable(),
60034
+ food1: number().nullable().meta(readingMeta("none")),
60143
60035
  /** Hopper-2 food level (grams) on dual-hopper feeders; null on
60144
60036
  * single-hopper models. */
60145
- food2: number().nullable(),
60037
+ food2: number().nullable().meta(readingMeta("none")),
60146
60038
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
60147
60039
  * (`device_class: problem`, on = low). True when the bowl is empty /
60148
60040
  * below the feeder's low threshold. */
60149
60041
  lowFood: boolean(),
60150
60042
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
60151
60043
  * device has no battery reading. */
60152
- batteryPower: number().min(0).max(100).nullable(),
60153
- /** Days of desiccant life remaining. Null when the model has no
60154
- * desiccant sensor. */
60155
- desiccantLeftDays: number().nullable(),
60044
+ batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60045
+ /**
60046
+ * Desiccant life remaining, stored in seconds (duration base, D944).
60047
+ * Providers convert from the days the feeder reports. Null when the
60048
+ * model has no desiccant sensor.
60049
+ */
60050
+ desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
60156
60051
  /** True while a feed is in progress. */
60157
60052
  feeding: boolean(),
60158
60053
  /** Decoded connectivity / power status (HA petkit device-status enum).
@@ -60163,7 +60058,7 @@ object({
60163
60058
  * with `errorCode` for consumers that want the raw integer. */
60164
60059
  error: string().nullable(),
60165
60060
  /** Raw device error code (0 / null = no error). */
60166
- errorCode: number().nullable(),
60061
+ errorCode: number().nullable().meta(readingMeta("none")),
60167
60062
  /** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
60168
60063
  isDualHopper: boolean(),
60169
60064
  /** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
@@ -60173,7 +60068,7 @@ object({
60173
60068
  /** Play a chime when dispensing. */
60174
60069
  feedSound: boolean(),
60175
60070
  /** Speaker / prompt volume level (device-scaled integer). */
60176
- volume: number(),
60071
+ volume: number().meta(readingMeta("none")),
60177
60072
  /** Ms epoch when the slice was last refreshed from the cloud. */
60178
60073
  lastFetchedAt: number()
60179
60074
  });
@@ -60610,36 +60505,19 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
60610
60505
  });
60611
60506
  object({
60612
60507
  /** Instantaneous power draw in watts. */
60613
- watts: number().optional().meta({
60614
- unit: "W",
60615
- label: "Power"
60616
- }),
60508
+ watts: number().optional().meta(readingMeta("power", { label: "Power" })),
60617
60509
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
60618
- kwhTotal: number().optional().meta({
60619
- unit: "kWh",
60620
- label: "Energy"
60621
- }),
60510
+ kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
60622
60511
  /** Voltage in volts. */
60623
- volts: number().optional(),
60512
+ volts: number().optional().meta(readingMeta("voltage")),
60624
60513
  /** Current in amperes. */
60625
- amps: number().optional(),
60626
- /** Apparent power in volt-amperes (W × power-factor reciprocal). */
60627
- va: number().optional(),
60514
+ amps: number().optional().meta(readingMeta("current")),
60515
+ /** Apparent power in volt-amperes. */
60516
+ va: number().optional().meta(readingMeta("apparent-power")),
60628
60517
  /** Power factor (0..1) if reported. */
60629
- powerFactor: number().min(0).max(1).optional(),
60518
+ powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
60630
60519
  /** Ms epoch when the slice was last updated. */
60631
- lastFetchedAt: number(),
60632
- /** Live display unit of the single metric this slice carries (e.g. HA
60633
- * `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
60634
- * Each upstream `sensor.*` entity surfaces ONE device_class, so one
60635
- * unit per slice is unambiguous. The UI prefers this over the role's
60636
- * canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
60637
- unit: string().optional(),
60638
- /** Suggested decimal places for numeric display.
60639
- * Populated live from the upstream source when provided (e.g. HA
60640
- * `attributes.suggested_display_precision`). Falls back to
60641
- * auto-formatting when absent. */
60642
- precision: number().int().min(0).max(10).optional()
60520
+ lastFetchedAt: number()
60643
60521
  });
60644
60522
  DeviceType.Sensor;
60645
60523
  /**
@@ -60660,11 +60538,11 @@ DeviceType.Sensor;
60660
60538
  */
60661
60539
  var GpsLocationSchema = object({
60662
60540
  /** Latitude in decimal degrees, -90..90. */
60663
- latitude: number().min(-90).max(90),
60541
+ latitude: number().min(-90).max(90).meta(readingMeta("angle")),
60664
60542
  /** Longitude in decimal degrees, -180..180. */
60665
- longitude: number().min(-180).max(180),
60543
+ longitude: number().min(-180).max(180).meta(readingMeta("angle")),
60666
60544
  /** Reported accuracy in meters (lower = better). */
60667
- accuracyMeters: number().nonnegative()
60545
+ accuracyMeters: number().nonnegative().meta(readingMeta("length"))
60668
60546
  });
60669
60547
  object({
60670
60548
  /** `home` / `not_home` / any user-defined zone name. */
@@ -60676,25 +60554,16 @@ object({
60676
60554
  gps: GpsLocationSchema.nullable(),
60677
60555
  /** Optional battery level of the tracking device (0..100). Null
60678
60556
  * when not reported. */
60679
- batteryPercent: number().min(0).max(100).nullable(),
60557
+ batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60680
60558
  /** Ms epoch when the slice was last updated. */
60681
60559
  lastChangedAt: number()
60682
60560
  });
60683
60561
  DeviceType.Presence;
60684
60562
  object({
60685
- /** Current pressure in hPa. */
60686
- hpa: number(),
60563
+ /** Current pressure in hPa. `null` when the sample was refused (D393). */
60564
+ hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
60687
60565
  /** Ms epoch when the slice was last updated. */
60688
- lastFetchedAt: number(),
60689
- /** Live display unit from the upstream source (e.g. HA
60690
- * `attributes.unit_of_measurement`). The UI prefers this over the
60691
- * role's canonical unit. Absent → fall back to the canonical unit. */
60692
- unit: string().optional(),
60693
- /** Suggested decimal places for numeric display.
60694
- * Populated live from the upstream source when provided (e.g. HA
60695
- * `attributes.suggested_display_precision`). Falls back to
60696
- * auto-formatting when absent. */
60697
- precision: number().int().min(0).max(10).optional()
60566
+ lastFetchedAt: number()
60698
60567
  });
60699
60568
  DeviceType.Sensor;
60700
60569
  /**
@@ -61027,8 +60896,8 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
61027
60896
  }), _void(), { kind: "mutation" });
61028
60897
  var PtzAutotrackSettingsSchema = object({
61029
60898
  targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
61030
- stopDelaySeconds: number().int().min(0).max(300),
61031
- disappearDelaySeconds: number().int().min(0).max(300)
60899
+ stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
60900
+ disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
61032
60901
  });
61033
60902
  /**
61034
60903
  * One option the camera advertises as a valid `targetType`. Drivers
@@ -61085,7 +60954,7 @@ object({
61085
60954
  /** null = unknown — never coerced to false (D393). */
61086
60955
  running: boolean().nullable(),
61087
60956
  /** Epoch ms the current boolean began; null while `running` is null. */
61088
- since: number().nullable(),
60957
+ since: number().nullable().meta(readingMeta("none")),
61089
60958
  source: RunningStateSourceSchema.meta({ label: "Source" }),
61090
60959
  phase: string().nullable(),
61091
60960
  /** null until a reading has been published (D393). */
@@ -62283,9 +62152,9 @@ var RecordTriggerSchema = _enum([
62283
62152
  */
62284
62153
  var RecordWindowSchema = object({
62285
62154
  trigger: RecordTriggerSchema,
62286
- day: number().int().min(0).max(6),
62287
- startMinute: number().int().min(0).max(1439),
62288
- endMinute: number().int().min(1).max(1440)
62155
+ day: number().int().min(0).max(6).meta(readingMeta("none")),
62156
+ startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
62157
+ endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
62289
62158
  });
62290
62159
  /** Status of one physical volume, as the camera itself describes it. */
62291
62160
  var OnboardStorageVolumeSchema = object({
@@ -62354,10 +62223,10 @@ object({
62354
62223
  /** Master "record to the card at all" switch. */
62355
62224
  enabled: boolean().nullable(),
62356
62225
  overwriteWhenFull: boolean().nullable(),
62357
- preRecordSec: number().nullable(),
62358
- postRecordSec: number().nullable(),
62226
+ preRecordSec: number().nullable().meta(readingMeta("duration")),
62227
+ postRecordSec: number().nullable().meta(readingMeta("duration")),
62359
62228
  /** Length of one recorded file, in minutes. */
62360
- segmentMinutes: number().nullable(),
62229
+ segmentMinutes: number().nullable().meta(readingMeta("none")),
62361
62230
  /** The primary track's weekly windows, flattened. */
62362
62231
  windows: array(RecordWindowSchema),
62363
62232
  /**
@@ -62373,7 +62242,7 @@ object({
62373
62242
  * provider reporting a non-zero count also reports the schedule as not
62374
62243
  * writable.
62375
62244
  */
62376
- unreadableWindows: number(),
62245
+ unreadableWindows: number().meta(readingMeta("count")),
62377
62246
  /**
62378
62247
  * The camera is scheduled to record and has NO usable storage.
62379
62248
  *
@@ -63010,7 +62879,7 @@ var CamStreamDescriptorSchema = object({
63010
62879
  url: string().optional(),
63011
62880
  codec: string().optional(),
63012
62881
  resolution: CamStreamResolutionSchema.optional(),
63013
- fps: number().positive().optional(),
62882
+ fps: number().positive().optional().meta(readingMeta("rate")),
63014
62883
  label: string().optional(),
63015
62884
  /** Device-level features (e.g. `battery-operated`) — drives broker policy. */
63016
62885
  deviceFeatures: array(string()).optional(),
@@ -63040,18 +62909,18 @@ var StreamProfileSchema = _enum([
63040
62909
  ]);
63041
62910
  /** The current encoding config of one stream profile. */
63042
62911
  var StreamProfileConfigSchema = object({
63043
- width: number(),
63044
- height: number(),
62912
+ width: number().meta(readingMeta("none")),
62913
+ height: number().meta(readingMeta("none")),
63045
62914
  codec: _enum(["h264", "h265"]),
63046
- framerate: number(),
63047
- bitrate: number(),
62915
+ framerate: number().meta(readingMeta("rate")),
62916
+ bitrate: number().meta(readingMeta("bitrate")),
63048
62917
  bitrateMode: _enum(["vbr", "cbr"]).optional(),
63049
62918
  encoderProfile: _enum([
63050
62919
  "high",
63051
62920
  "main",
63052
62921
  "baseline"
63053
62922
  ]).optional(),
63054
- gop: number().optional(),
62923
+ gop: number().optional().meta(readingMeta("none")),
63055
62924
  /**
63056
62925
  * Whether THIS profile currently carries an audio track. READ-ONLY here.
63057
62926
  *
@@ -63694,23 +63563,13 @@ object({
63694
63563
  });
63695
63564
  DeviceType.Sensor;
63696
63565
  object({
63697
- /** Current temperature in Celsius. */
63698
- celsius: number().meta({
63699
- unit: "°C",
63566
+ /** Current temperature in Celsius. `null` when the sample was refused (D393). */
63567
+ celsius: number().nullable().meta(readingMeta("temperature", {
63568
+ precision: 1,
63700
63569
  label: "Temperature"
63701
- }),
63570
+ })),
63702
63571
  /** Ms epoch when the slice was last updated (push or poll). */
63703
- lastFetchedAt: number(),
63704
- /** Live display unit from the upstream source (e.g. HA
63705
- * `attributes.unit_of_measurement`). The UI prefers this over the
63706
- * role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
63707
- * Absent → the UI falls back to the role's canonical unit. */
63708
- unit: string().optional(),
63709
- /** Suggested decimal places for numeric display.
63710
- * Populated live from the upstream source when provided (e.g. HA
63711
- * `attributes.suggested_display_precision`). Falls back to
63712
- * auto-formatting when absent. */
63713
- precision: number().int().min(0).max(10).optional()
63572
+ lastFetchedAt: number()
63714
63573
  });
63715
63574
  DeviceType.Sensor, DeviceType.Thermostat;
63716
63575
  /**
@@ -63962,16 +63821,16 @@ object({
63962
63821
  ]),
63963
63822
  /** 0..100 battery percentage. Null when the device has no battery
63964
63823
  * reading. */
63965
- batteryLevel: number().min(0).max(100).nullable(),
63824
+ batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
63966
63825
  /** Current fan-speed token (provider-verbatim). Null when unknown or
63967
63826
  * the vacuum has no speed control. */
63968
63827
  fanSpeed: string().nullable(),
63969
63828
  /** Speed tokens the hardware accepts — drives the UI selector. */
63970
63829
  availableFanSpeeds: array(string()),
63971
63830
  /** 0..100 cleaning-completion percentage of the current task, or null. */
63972
- progressPercent: number().min(0).max(100).nullable(),
63831
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
63973
63832
  /** Current error code (0 / null = no error). */
63974
- errorCode: number().nullable(),
63833
+ errorCode: number().nullable().meta(readingMeta("none")),
63975
63834
  /** Human label for {@link errorCode}, or null when none / undecodable. */
63976
63835
  errorLabel: string().nullable(),
63977
63836
  /** Ms epoch when the slice was last updated. */
@@ -64010,7 +63869,7 @@ object({
64010
63869
  ]),
64011
63870
  /** 0 = fully closed, 100 = fully open. Null when the device has no
64012
63871
  * intermediate position surface. */
64013
- position: number().min(0).max(100).nullable(),
63872
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64014
63873
  /** Ms epoch when the slice was last updated. */
64015
63874
  lastChangedAt: number()
64016
63875
  });
@@ -64038,9 +63897,9 @@ object({
64038
63897
  DeviceType.Sensor;
64039
63898
  object({
64040
63899
  /** Current measured temperature. Null when not reported. */
64041
- currentTemp: number().nullable(),
63900
+ currentTemp: number().nullable().meta(readingMeta("temperature")),
64042
63901
  /** Target temperature setpoint. Null when no setpoint surface. */
64043
- targetTemp: number().nullable(),
63902
+ targetTemp: number().nullable().meta(readingMeta("temperature")),
64044
63903
  /** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
64045
63904
  * `heat_pump` / `high_demand` / `performance` / `off`). Null when the
64046
63905
  * device reports an unknown state. */
@@ -64051,9 +63910,9 @@ object({
64051
63910
  * has no away surface. */
64052
63911
  away: boolean().nullable(),
64053
63912
  /** HA `min_temp` attribute. Null when not reported. */
64054
- minTemp: number().nullable(),
63913
+ minTemp: number().nullable().meta(readingMeta("temperature")),
64055
63914
  /** HA `max_temp` attribute. Null when not reported. */
64056
- maxTemp: number().nullable(),
63915
+ maxTemp: number().nullable().meta(readingMeta("temperature")),
64057
63916
  /** Ms epoch when the slice was last updated. */
64058
63917
  lastChangedAt: number()
64059
63918
  });
@@ -64077,25 +63936,18 @@ DeviceType.WaterHeater, method(object({
64077
63936
  auth: "protected"
64078
63937
  });
64079
63938
  object({
64080
- /** Verbatim HA condition state (`sunny`, `cloudy`, `rainy`, …). Null
64081
- * when no condition has been reported yet. */
63939
+ /** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
64082
63940
  condition: string().nullable(),
64083
- /** Current temperature in the reported unit. Null when not provided. */
64084
- temperature: number().nullable(),
64085
- /** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
64086
- temperatureUnit: string().nullable(),
63941
+ /** Current temperature in Celsius. Null when not provided or refused. */
63942
+ temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
64087
63943
  /** Relative humidity (0..100). Null when not provided. */
64088
- humidity: number().min(0).max(100).nullable(),
64089
- /** Barometric pressure in the reported unit. Null when not provided. */
64090
- pressure: number().nullable(),
64091
- /** Pressure unit string (e.g. `hPa` / `inHg`). Null when not provided. */
64092
- pressureUnit: string().nullable(),
64093
- /** Wind speed in the reported unit. Null when not provided. */
64094
- windSpeed: number().nullable(),
64095
- /** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
64096
- windSpeedUnit: string().nullable(),
63944
+ humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
63945
+ /** Barometric pressure in hPa. Null when not provided or refused. */
63946
+ pressure: number().nullable().meta(readingMeta("pressure")),
63947
+ /** Wind speed in metres per second. Null when not provided or refused. */
63948
+ windSpeed: number().nullable().meta(readingMeta("speed")),
64097
63949
  /** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
64098
- windBearing: number().nullable(),
63950
+ windBearing: number().nullable().meta(readingMeta("angle")),
64099
63951
  /** Ms epoch when the slice was last updated. */
64100
63952
  lastFetchedAt: number()
64101
63953
  });
@@ -77970,7 +77822,6 @@ var SENSOR_CAP_VOCABULARY = {
77970
77822
  field: "value",
77971
77823
  quantity: t("the reading", "il valore"),
77972
77824
  label: t("Reading", "Valore"),
77973
- unitField: "unit",
77974
77825
  icon: "gauge"
77975
77826
  }]
77976
77827
  },
@@ -78106,7 +77957,7 @@ var SENSOR_CAP_VOCABULARY = {
78106
77957
  field: "temperature",
78107
77958
  quantity: t("the outdoor temperature", "la temperatura esterna"),
78108
77959
  label: t("Temperature", "Temperatura"),
78109
- unitField: "temperatureUnit",
77960
+ unit: "°C",
78110
77961
  icon: "thermometer"
78111
77962
  },
78112
77963
  {
@@ -78122,7 +77973,7 @@ var SENSOR_CAP_VOCABULARY = {
78122
77973
  field: "windSpeed",
78123
77974
  quantity: t("the wind speed", "la velocità del vento"),
78124
77975
  label: t("Wind", "Vento"),
78125
- unitField: "windSpeedUnit",
77976
+ unit: "m/s",
78126
77977
  icon: "wind"
78127
77978
  },
78128
77979
  {
@@ -78130,7 +77981,7 @@ var SENSOR_CAP_VOCABULARY = {
78130
77981
  field: "pressure",
78131
77982
  quantity: t("the air pressure", "la pressione atmosferica"),
78132
77983
  label: t("Pressure", "Pressione"),
78133
- unitField: "pressureUnit",
77984
+ unit: "hPa",
78134
77985
  icon: "gauge"
78135
77986
  }
78136
77987
  ]
@@ -82516,7 +82367,7 @@ var AdminUIAddon = class extends BaseAddon {
82516
82367
  capability: adminUiCapability,
82517
82368
  provider: {
82518
82369
  getStaticDir: async () => ({ staticDir: resolveAdminUiDistDir() }),
82519
- getVersion: async () => ({ version: "1.2.470" })
82370
+ getVersion: async () => ({ version: "1.2.472" })
82520
82371
  }
82521
82372
  }];
82522
82373
  }