@camstack/addon-smtp-nodemailer 1.2.141 → 1.2.142

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.
@@ -34,7 +34,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
34
34
  var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
35
35
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
36
36
  //#endregion
37
- //#region ../types/dist/event-category-DHXxB8t9.mjs
37
+ //#region ../types/dist/event-category-CT-hz5Rb.mjs
38
38
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
39
39
  EventCategory["SystemBoot"] = "system.boot";
40
40
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -360,6 +360,14 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
360
360
  /** The recovery edge of {@link PipelineMotionNoisy}: the motion fell back, or a detection arrived. */
361
361
  EventCategory["PipelineMotionCalm"] = "pipeline.motion-calm";
362
362
  /**
363
+ * A camera's package LLM confirm has failed closed three times in a row
364
+ * (D931, 2026-10-26). One edge per camera until
365
+ * {@link PipelinePackageLlmAnswered}.
366
+ */
367
+ EventCategory["PipelinePackageLlmSilent"] = "pipeline.package-llm-silent";
368
+ /** The recovery edge of {@link PipelinePackageLlmSilent}: a confirm returned yes or no. */
369
+ EventCategory["PipelinePackageLlmAnswered"] = "pipeline.package-llm-answered";
370
+ /**
363
371
  * Per-camera pipeline config was mutated by the orchestrator
364
372
  * (a settings change via `setAgentInferenceDevices` /
365
373
  * `setCameraStepToggle` / `setCameraStepSettings`). Orchestrator
@@ -5469,7 +5477,7 @@ var ZodIssueCode = {
5469
5477
  var ZodFirstPartyTypeKind;
5470
5478
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5471
5479
  //#endregion
5472
- //#region ../types/dist/sleep-BErAxhul.mjs
5480
+ //#region ../types/dist/sleep-bykKrTgu.mjs
5473
5481
  /**
5474
5482
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5475
5483
  * window to float samples (D455).
@@ -7956,6 +7964,99 @@ var RecordKindDeclarationSchema = object({
7956
7964
  }).strict();
7957
7965
  /** The kind names: lower-case, dash separated, short enough for an index name. */
7958
7966
  var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
7967
+ var READING_QUANTITIES = [
7968
+ "temperature",
7969
+ "pressure",
7970
+ "speed",
7971
+ "precipitation",
7972
+ "precipitation-rate",
7973
+ "length",
7974
+ "area",
7975
+ "illuminance",
7976
+ "irradiance",
7977
+ "power",
7978
+ "energy",
7979
+ "voltage",
7980
+ "current",
7981
+ "apparent-power",
7982
+ "humidity",
7983
+ "percentage",
7984
+ "concentration-volume",
7985
+ "concentration-mass",
7986
+ "concentration-ppb",
7987
+ "volume",
7988
+ "duration",
7989
+ "duration-ms",
7990
+ "information",
7991
+ "sound-level",
7992
+ "signal-level",
7993
+ "colour-temperature",
7994
+ "angle",
7995
+ "bitrate",
7996
+ "rate",
7997
+ "mass",
7998
+ "index",
7999
+ "ratio",
8000
+ "count",
8001
+ "none",
8002
+ "device"
8003
+ ];
8004
+ var ReadingQuantitySchema = _enum(READING_QUANTITIES);
8005
+ new Set(READING_QUANTITIES);
8006
+ /**
8007
+ * Stored unit symbol for a quantity. `none` means the number is dimensionless
8008
+ * (or an index / ratio / count). `device` means the cap has no single unit.
8009
+ */
8010
+ var QUANTITY_BASE = {
8011
+ temperature: "°C",
8012
+ pressure: "hPa",
8013
+ speed: "m/s",
8014
+ precipitation: "mm",
8015
+ "precipitation-rate": "mm/h",
8016
+ length: "m",
8017
+ area: "m²",
8018
+ illuminance: "lx",
8019
+ irradiance: "W/m²",
8020
+ power: "W",
8021
+ energy: "kWh",
8022
+ voltage: "V",
8023
+ current: "A",
8024
+ "apparent-power": "VA",
8025
+ humidity: "%",
8026
+ percentage: "%",
8027
+ "concentration-volume": "ppm",
8028
+ "concentration-mass": "µg/m³",
8029
+ "concentration-ppb": "ppb",
8030
+ volume: "L",
8031
+ duration: "s",
8032
+ "duration-ms": "ms",
8033
+ information: "B",
8034
+ "sound-level": "dBFS",
8035
+ "signal-level": "dBm",
8036
+ "colour-temperature": "mired",
8037
+ angle: "°",
8038
+ bitrate: "kbps",
8039
+ rate: "fps",
8040
+ mass: "kg",
8041
+ index: "none",
8042
+ ratio: "none",
8043
+ count: "none",
8044
+ none: "none",
8045
+ device: "device"
8046
+ };
8047
+ /** The stored unit, or `undefined` when the quantity has no unit symbol. */
8048
+ function baseUnitOf(quantity) {
8049
+ const symbol = QUANTITY_BASE[quantity];
8050
+ if (symbol === "none" || symbol === "device") return void 0;
8051
+ return symbol;
8052
+ }
8053
+ function readingMeta(quantity, extras) {
8054
+ return {
8055
+ quantity,
8056
+ ...extras?.precision !== void 0 ? { precision: extras.precision } : {},
8057
+ ...extras?.label !== void 0 ? { label: extras.label } : {}
8058
+ };
8059
+ }
7959
8060
  /** Shared Zod schemas used across streaming capabilities. */
7960
8061
  var CamProfileSchema = _enum([
7961
8062
  "high",
@@ -7983,8 +8084,8 @@ var CamStreamKindSchema = _enum([
7983
8084
  "derived"
7984
8085
  ]);
7985
8086
  var CamStreamResolutionSchema = object({
7986
- width: number().int().positive(),
7987
- height: number().int().positive()
8087
+ width: number().int().positive().meta(readingMeta("none")),
8088
+ height: number().int().positive().meta(readingMeta("none"))
7988
8089
  });
7989
8090
  var CameraStreamSchema = object({
7990
8091
  /** Stable, provider-assigned id unique within the (deviceId) scope. */
@@ -8498,6 +8599,8 @@ var CAP_NAMES = Object.freeze([
8498
8599
  "filesystem-browse",
8499
8600
  "flood",
8500
8601
  "gas",
8602
+ "group-commands",
8603
+ "group-status",
8501
8604
  "heating-valve",
8502
8605
  "humidifier",
8503
8606
  "humidity-sensor",
@@ -8627,14 +8730,6 @@ Object.freeze({
8627
8730
  {
8628
8731
  "path": "aqi",
8629
8732
  "valueType": "number"
8630
- },
8631
- {
8632
- "path": "unit",
8633
- "valueType": "string"
8634
- },
8635
- {
8636
- "path": "precision",
8637
- "valueType": "number"
8638
8733
  }
8639
8734
  ],
8640
8735
  "alarm-panel": [
@@ -8670,20 +8765,10 @@ Object.freeze({
8670
8765
  "valueType": "boolean"
8671
8766
  }
8672
8767
  ],
8673
- "ambient-light-sensor": [
8674
- {
8675
- "path": "lux",
8676
- "valueType": "number"
8677
- },
8678
- {
8679
- "path": "unit",
8680
- "valueType": "string"
8681
- },
8682
- {
8683
- "path": "precision",
8684
- "valueType": "number"
8685
- }
8686
- ],
8768
+ "ambient-light-sensor": [{
8769
+ "path": "lux",
8770
+ "valueType": "number"
8771
+ }],
8687
8772
  "audio-metrics": [
8688
8773
  {
8689
8774
  "path": "ts",
@@ -8763,6 +8848,10 @@ Object.freeze({
8763
8848
  "path": "lastUpdated",
8764
8849
  "valueType": "number"
8765
8850
  },
8851
+ {
8852
+ "path": "volts",
8853
+ "valueType": "number"
8854
+ },
8766
8855
  {
8767
8856
  "path": "lastContactAt",
8768
8857
  "valueType": "number"
@@ -9005,10 +9094,6 @@ Object.freeze({
9005
9094
  "datetime"
9006
9095
  ]
9007
9096
  },
9008
- {
9009
- "path": "unit",
9010
- "valueType": "string"
9011
- },
9012
9097
  {
9013
9098
  "path": "min",
9014
9099
  "valueType": "number"
@@ -9020,19 +9105,6 @@ Object.freeze({
9020
9105
  {
9021
9106
  "path": "step",
9022
9107
  "valueType": "number"
9023
- },
9024
- {
9025
- "path": "precision",
9026
- "valueType": "number"
9027
- },
9028
- {
9029
- "path": "format",
9030
- "valueType": "enum",
9031
- "enumValues": [
9032
- "date",
9033
- "time",
9034
- "datetime"
9035
- ]
9036
9108
  }
9037
9109
  ],
9038
9110
  "cover": [
@@ -9273,13 +9345,8 @@ Object.freeze({
9273
9345
  "path": "value",
9274
9346
  "valueType": "string"
9275
9347
  }, {
9276
- "path": "format",
9277
- "valueType": "enum",
9278
- "enumValues": [
9279
- "date",
9280
- "time",
9281
- "datetime"
9282
- ]
9348
+ "path": "timestamp",
9349
+ "valueType": "boolean"
9283
9350
  }],
9284
9351
  "event-emitter": [
9285
9352
  {
@@ -9352,6 +9419,20 @@ Object.freeze({
9352
9419
  "path": "detected",
9353
9420
  "valueType": "boolean"
9354
9421
  }],
9422
+ "group-status": [
9423
+ {
9424
+ "path": "included",
9425
+ "valueType": "number"
9426
+ },
9427
+ {
9428
+ "path": "excluded",
9429
+ "valueType": "number"
9430
+ },
9431
+ {
9432
+ "path": "total",
9433
+ "valueType": "number"
9434
+ }
9435
+ ],
9355
9436
  "heating-valve": [{
9356
9437
  "path": "position",
9357
9438
  "valueType": "number"
@@ -9386,20 +9467,10 @@ Object.freeze({
9386
9467
  "valueType": "number"
9387
9468
  }
9388
9469
  ],
9389
- "humidity-sensor": [
9390
- {
9391
- "path": "percent",
9392
- "valueType": "number"
9393
- },
9394
- {
9395
- "path": "unit",
9396
- "valueType": "string"
9397
- },
9398
- {
9399
- "path": "precision",
9400
- "valueType": "number"
9401
- }
9402
- ],
9470
+ "humidity-sensor": [{
9471
+ "path": "percent",
9472
+ "valueType": "number"
9473
+ }],
9403
9474
  "image": [{
9404
9475
  "path": "url",
9405
9476
  "valueType": "string"
@@ -9738,20 +9809,10 @@ Object.freeze({
9738
9809
  "valueType": "boolean"
9739
9810
  }
9740
9811
  ],
9741
- "numeric-sensor": [
9742
- {
9743
- "path": "value",
9744
- "valueType": "number"
9745
- },
9746
- {
9747
- "path": "unit",
9748
- "valueType": "string"
9749
- },
9750
- {
9751
- "path": "precision",
9752
- "valueType": "number"
9753
- }
9754
- ],
9812
+ "numeric-sensor": [{
9813
+ "path": "value",
9814
+ "valueType": "number"
9815
+ }],
9755
9816
  "pet-feeder": [
9756
9817
  {
9757
9818
  "path": "foodLevel",
@@ -9843,14 +9904,6 @@ Object.freeze({
9843
9904
  {
9844
9905
  "path": "powerFactor",
9845
9906
  "valueType": "number"
9846
- },
9847
- {
9848
- "path": "unit",
9849
- "valueType": "string"
9850
- },
9851
- {
9852
- "path": "precision",
9853
- "valueType": "number"
9854
9907
  }
9855
9908
  ],
9856
9909
  "presence": [
@@ -9879,20 +9932,10 @@ Object.freeze({
9879
9932
  "valueType": "number"
9880
9933
  }
9881
9934
  ],
9882
- "pressure-sensor": [
9883
- {
9884
- "path": "hpa",
9885
- "valueType": "number"
9886
- },
9887
- {
9888
- "path": "unit",
9889
- "valueType": "string"
9890
- },
9891
- {
9892
- "path": "precision",
9893
- "valueType": "number"
9894
- }
9895
- ],
9935
+ "pressure-sensor": [{
9936
+ "path": "hpa",
9937
+ "valueType": "number"
9938
+ }],
9896
9939
  "privacy-mask": [{
9897
9940
  "path": "enabled",
9898
9941
  "valueType": "boolean"
@@ -10203,20 +10246,10 @@ Object.freeze({
10203
10246
  "path": "tampered",
10204
10247
  "valueType": "boolean"
10205
10248
  }],
10206
- "temperature-sensor": [
10207
- {
10208
- "path": "celsius",
10209
- "valueType": "number"
10210
- },
10211
- {
10212
- "path": "unit",
10213
- "valueType": "string"
10214
- },
10215
- {
10216
- "path": "precision",
10217
- "valueType": "number"
10218
- }
10219
- ],
10249
+ "temperature-sensor": [{
10250
+ "path": "celsius",
10251
+ "valueType": "number"
10252
+ }],
10220
10253
  "update": [
10221
10254
  {
10222
10255
  "path": "currentVersion",
@@ -10327,10 +10360,6 @@ Object.freeze({
10327
10360
  "path": "temperature",
10328
10361
  "valueType": "number"
10329
10362
  },
10330
- {
10331
- "path": "temperatureUnit",
10332
- "valueType": "string"
10333
- },
10334
10363
  {
10335
10364
  "path": "humidity",
10336
10365
  "valueType": "number"
@@ -10339,18 +10368,10 @@ Object.freeze({
10339
10368
  "path": "pressure",
10340
10369
  "valueType": "number"
10341
10370
  },
10342
- {
10343
- "path": "pressureUnit",
10344
- "valueType": "string"
10345
- },
10346
10371
  {
10347
10372
  "path": "windSpeed",
10348
10373
  "valueType": "number"
10349
10374
  },
10350
- {
10351
- "path": "windSpeedUnit",
10352
- "valueType": "string"
10353
- },
10354
10375
  {
10355
10376
  "path": "windBearing",
10356
10377
  "valueType": "number"
@@ -10743,7 +10764,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
10743
10764
  return DeviceRole;
10744
10765
  }({});
10745
10766
  //#endregion
10746
- //#region ../types/dist/composition-kEl9mevu.mjs
10767
+ //#region ../types/dist/composition-OhpuespA.mjs
10747
10768
  /**
10748
10769
  * The ffmpeg build every CamStack node downloads, per platform and arch.
10749
10770
  *
@@ -11498,15 +11519,74 @@ function validateExpressionSource(src) {
11498
11519
  }
11499
11520
  return null;
11500
11521
  }
11501
- /** A composed device's stableId is this prefix plus the block id. */
11502
- var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
11503
- /** Longest snippet a `code` source may carry (slice 4). */
11504
- var MAX_COMPOSITION_CODE_LENGTH = 2e4;
11522
+ /**
11523
+ * A composition source identity: `(addonId, stableId)`, never a numeric device
11524
+ * id. Split out so helper schemas can name it without a cycle through
11525
+ * `composition.ts` (D659).
11526
+ */
11505
11527
  var CompositionSourceRefSchema = object({
11506
11528
  /** The owning addon; `stableId` is unique only within it. */
11507
11529
  addonId: string().min(1),
11508
11530
  stableId: string().min(1)
11509
11531
  });
11532
+ var GROUP_TEMPLATE_IDS = [
11533
+ "temperature",
11534
+ "humidity",
11535
+ "pressure",
11536
+ "illuminance",
11537
+ "power",
11538
+ "energy",
11539
+ "battery",
11540
+ "numeric",
11541
+ "motion",
11542
+ "contact",
11543
+ "leak",
11544
+ "smoke",
11545
+ "occupancy",
11546
+ "binary",
11547
+ "switch",
11548
+ "light"
11549
+ ];
11550
+ var NUMERIC_AGGREGATES = [
11551
+ "mean",
11552
+ "median",
11553
+ "min",
11554
+ "max",
11555
+ "sum"
11556
+ ];
11557
+ var BINARY_AGGREGATES = ["any", "all"];
11558
+ /** How an actuator group decides it is on. Unknown members never count as off. */
11559
+ var ACTIVE_CRITERIA = ["at-least-one", "all"];
11560
+ var AGGREGATE_REDUCERS = [
11561
+ "mean",
11562
+ "median",
11563
+ "min",
11564
+ "max",
11565
+ "sum",
11566
+ "any",
11567
+ "all"
11568
+ ];
11569
+ var GroupTemplateIdSchema = _enum(GROUP_TEMPLATE_IDS);
11570
+ var AggregateReducerSchema = _enum(AGGREGATE_REDUCERS);
11571
+ var ActiveCriterionSchema = _enum(ACTIVE_CRITERIA);
11572
+ /** One member. The field is stored so occupancy can name motion or binary. */
11573
+ var HelperMemberSchema = object({
11574
+ source: CompositionSourceRefSchema,
11575
+ cap: string().min(1),
11576
+ fieldPath: string().min(1)
11577
+ });
11578
+ var HelperSpecSchema = discriminatedUnion("kind", [object({
11579
+ kind: literal("group"),
11580
+ templateId: GroupTemplateIdSchema,
11581
+ aggregate: AggregateReducerSchema,
11582
+ members: array(HelperMemberSchema).min(1).max(64),
11583
+ /** Actuator groups only. Readings leave this unset. */
11584
+ active: ActiveCriterionSchema.optional()
11585
+ })]);
11586
+ /** A composed device's stableId is this prefix plus the block id. */
11587
+ var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
11588
+ /** Longest snippet a `code` source may carry (slice 4). */
11589
+ var MAX_COMPOSITION_CODE_LENGTH = 2e4;
11510
11590
  /** One field of one capability of one source device. */
11511
11591
  var CompositionFieldReadSchema = object({
11512
11592
  source: CompositionSourceRefSchema,
@@ -11550,7 +11630,33 @@ var CompositionCodeSourceSchema = object({
11550
11630
  var CompositionFieldSourceSchema = discriminatedUnion("kind", [
11551
11631
  CompositionFromSourceSchema,
11552
11632
  CompositionExpressionSourceSchema,
11553
- CompositionCodeSourceSchema
11633
+ CompositionCodeSourceSchema,
11634
+ object({
11635
+ kind: literal("aggregate"),
11636
+ reducer: _enum([
11637
+ "mean",
11638
+ "median",
11639
+ "min",
11640
+ "max",
11641
+ "sum",
11642
+ "any",
11643
+ "all"
11644
+ ]),
11645
+ reads: array(CompositionFieldReadSchema).min(1).max(64),
11646
+ /** `value` is the reading; the others are the member counts. */
11647
+ publish: _enum([
11648
+ "value",
11649
+ "included",
11650
+ "excluded",
11651
+ "total"
11652
+ ]),
11653
+ /** What a `value` publish does when nobody is included. Counts ignore this. */
11654
+ empty: _enum([
11655
+ "null",
11656
+ "omit",
11657
+ "offline"
11658
+ ])
11659
+ })
11554
11660
  ]);
11555
11661
  var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
11556
11662
  kind: literal("forward"),
@@ -11596,7 +11702,13 @@ var CompositionSchema = object({
11596
11702
  kind: literal("existing"),
11597
11703
  device: CompositionSourceRefSchema
11598
11704
  })]),
11599
- features: array(CompositionFeatureSchema).min(1).max(16)
11705
+ features: array(CompositionFeatureSchema).min(1).max(16),
11706
+ /**
11707
+ * Present when the block is a helper template. Absent on a free
11708
+ * composition (the room example, device 4603). The editor reads this,
11709
+ * never the compiled features.
11710
+ */
11711
+ helper: HelperSpecSchema.optional()
11600
11712
  }).superRefine((composition, ctx) => {
11601
11713
  const seen = /* @__PURE__ */ new Set();
11602
11714
  composition.features.forEach((feature, index) => {
@@ -17698,7 +17810,11 @@ var CompositionProblemSchema = object({
17698
17810
  "item-field-reserved",
17699
17811
  "target-missing",
17700
17812
  "target-unreadable",
17701
- "not-replaceable"
17813
+ "not-replaceable",
17814
+ "native-capability-replace",
17815
+ "mixed-quantity",
17816
+ "helper-mismatch",
17817
+ "member-constraint"
17702
17818
  ]),
17703
17819
  path: string(),
17704
17820
  message: string()
@@ -17719,7 +17835,8 @@ var CompositionUnavailableModeSchema = _enum([
17719
17835
  "null",
17720
17836
  "offline",
17721
17837
  "unknown",
17722
- "release"
17838
+ "release",
17839
+ "omit"
17723
17840
  ]);
17724
17841
  /**
17725
17842
  * `derived` reads a source · `clock` reads only `now`, recomputed when a sibling
@@ -17756,9 +17873,11 @@ var CompositionFieldPlanSchema = object({
17756
17873
  reads: array(CompositionFieldReadSchema)
17757
17874
  });
17758
17875
  /**
17759
- * What a feature does to its target (D663): `new` builds a capability on a new
17760
- * device; on an existing device it `add`s a capability the device lacks, or
17761
- * `replace`s named fields of one it has natively.
17876
+ * What a feature does to its target (D663, D944): `new` builds a capability on a
17877
+ * new device; on an existing device it `add`s a capability the device lacks.
17878
+ * Replacing a capability the device already serves is refused
17879
+ * (`native-capability-replace`), not planned. `replace` remains on the claim
17880
+ * wire for a claim the hub already holds.
17762
17881
  */
17763
17882
  var CompositionFeatureModeSchema = _enum([
17764
17883
  "new",
@@ -17931,6 +18050,275 @@ Object.freeze({
17931
18050
  result: ["number", "null"]
17932
18051
  }
17933
18052
  });
18053
+ /**
18054
+ * One catalog row per base reading a group helper can build (D944 §3).
18055
+ * Data, not a form default written into the block.
18056
+ */
18057
+ var NOW = {
18058
+ fieldPath: "lastFetchedAt",
18059
+ expr: "now"
18060
+ };
18061
+ var CHANGED = {
18062
+ fieldPath: "lastChangedAt",
18063
+ expr: "now"
18064
+ };
18065
+ function numeric(input) {
18066
+ return {
18067
+ ...input,
18068
+ family: "numeric",
18069
+ deviceType: DeviceType.Sensor,
18070
+ memberDeviceType: DeviceType.Sensor,
18071
+ commandCaps: [],
18072
+ baseUnit: baseUnitOf(input.quantity),
18073
+ aggregates: NUMERIC_AGGREGATES,
18074
+ accept: [{
18075
+ cap: input.outputCap,
18076
+ fieldPath: input.outputField
18077
+ }]
18078
+ };
18079
+ }
18080
+ function binary(input) {
18081
+ return {
18082
+ ...input,
18083
+ family: "binary",
18084
+ deviceType: DeviceType.Sensor,
18085
+ memberDeviceType: DeviceType.Sensor,
18086
+ commandCaps: [],
18087
+ quantity: "none",
18088
+ baseUnit: void 0,
18089
+ aggregates: BINARY_AGGREGATES,
18090
+ empty: "offline"
18091
+ };
18092
+ }
18093
+ var GROUP_TEMPLATES = [
18094
+ numeric({
18095
+ id: "temperature",
18096
+ summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
18097
+ role: DeviceRole.TemperatureSensor,
18098
+ outputCap: "temperature-sensor",
18099
+ outputField: "celsius",
18100
+ quantity: "temperature",
18101
+ defaultAggregate: "mean",
18102
+ empty: "null",
18103
+ companions: [NOW]
18104
+ }),
18105
+ numeric({
18106
+ id: "humidity",
18107
+ summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
18108
+ role: DeviceRole.HumiditySensor,
18109
+ outputCap: "humidity-sensor",
18110
+ outputField: "percent",
18111
+ quantity: "humidity",
18112
+ defaultAggregate: "mean",
18113
+ empty: "null",
18114
+ companions: [NOW]
18115
+ }),
18116
+ numeric({
18117
+ id: "pressure",
18118
+ summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
18119
+ role: DeviceRole.PressureSensor,
18120
+ outputCap: "pressure-sensor",
18121
+ outputField: "hpa",
18122
+ quantity: "pressure",
18123
+ defaultAggregate: "mean",
18124
+ empty: "null",
18125
+ companions: [NOW]
18126
+ }),
18127
+ numeric({
18128
+ id: "illuminance",
18129
+ summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
18130
+ role: DeviceRole.AmbientLightSensor,
18131
+ outputCap: "ambient-light-sensor",
18132
+ outputField: "lux",
18133
+ quantity: "illuminance",
18134
+ defaultAggregate: "mean",
18135
+ empty: "null",
18136
+ companions: [NOW]
18137
+ }),
18138
+ numeric({
18139
+ id: "power",
18140
+ summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
18141
+ role: DeviceRole.PowerSensor,
18142
+ outputCap: "power-meter",
18143
+ outputField: "watts",
18144
+ quantity: "power",
18145
+ defaultAggregate: "sum",
18146
+ empty: "omit",
18147
+ companions: [NOW]
18148
+ }),
18149
+ numeric({
18150
+ id: "energy",
18151
+ summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
18152
+ role: DeviceRole.EnergySensor,
18153
+ outputCap: "power-meter",
18154
+ outputField: "kwhTotal",
18155
+ quantity: "energy",
18156
+ defaultAggregate: "sum",
18157
+ empty: "omit",
18158
+ companions: [NOW]
18159
+ }),
18160
+ numeric({
18161
+ id: "battery",
18162
+ summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
18163
+ role: DeviceRole.BatterySensor,
18164
+ outputCap: "battery",
18165
+ outputField: "percentage",
18166
+ quantity: "percentage",
18167
+ defaultAggregate: "mean",
18168
+ empty: "null",
18169
+ companions: [
18170
+ {
18171
+ fieldPath: "charging",
18172
+ expr: "\"unknown\""
18173
+ },
18174
+ {
18175
+ fieldPath: "sleeping",
18176
+ expr: "false"
18177
+ },
18178
+ {
18179
+ fieldPath: "lastUpdated",
18180
+ expr: "now"
18181
+ }
18182
+ ]
18183
+ }),
18184
+ numeric({
18185
+ id: "numeric",
18186
+ summary: "A numeric reading in the members’ shared quantity. Mixed quantities are refused. Nobody reporting publishes null, not 0.",
18187
+ role: DeviceRole.NumericSensor,
18188
+ outputCap: "numeric-sensor",
18189
+ outputField: "value",
18190
+ quantity: "device",
18191
+ defaultAggregate: "mean",
18192
+ empty: "null",
18193
+ companions: [NOW]
18194
+ }),
18195
+ binary({
18196
+ id: "motion",
18197
+ summary: "Any or all motion. Offline and unknown members are left out, never read as off. Nobody reporting takes the device offline.",
18198
+ role: DeviceRole.MotionSensor,
18199
+ outputCap: "motion",
18200
+ outputField: "detected",
18201
+ defaultAggregate: "any",
18202
+ accept: [{
18203
+ cap: "motion",
18204
+ fieldPath: "detected"
18205
+ }],
18206
+ companions: [{
18207
+ fieldPath: "lastDetectedAt",
18208
+ expr: "null"
18209
+ }, {
18210
+ fieldPath: "autoClearAfterMs",
18211
+ expr: "null"
18212
+ }]
18213
+ }),
18214
+ binary({
18215
+ id: "contact",
18216
+ summary: "Any or all open contacts. Offline and unknown members are left out, never read as closed.",
18217
+ role: DeviceRole.ContactSensor,
18218
+ outputCap: "contact",
18219
+ outputField: "entryOpen",
18220
+ defaultAggregate: "any",
18221
+ accept: [{
18222
+ cap: "contact",
18223
+ fieldPath: "entryOpen"
18224
+ }],
18225
+ companions: [CHANGED]
18226
+ }),
18227
+ binary({
18228
+ id: "leak",
18229
+ summary: "Any or all leak detections. Offline and unknown members are left out, never read as dry.",
18230
+ role: DeviceRole.LeakSensor,
18231
+ outputCap: "flood",
18232
+ outputField: "flooded",
18233
+ defaultAggregate: "any",
18234
+ accept: [{
18235
+ cap: "flood",
18236
+ fieldPath: "flooded"
18237
+ }],
18238
+ companions: [CHANGED]
18239
+ }),
18240
+ binary({
18241
+ id: "smoke",
18242
+ summary: "Any or all smoke detections. Offline and unknown members are left out, never read as clear.",
18243
+ role: DeviceRole.SmokeSensor,
18244
+ outputCap: "smoke",
18245
+ outputField: "detected",
18246
+ defaultAggregate: "any",
18247
+ accept: [{
18248
+ cap: "smoke",
18249
+ fieldPath: "detected"
18250
+ }],
18251
+ companions: [CHANGED]
18252
+ }),
18253
+ binary({
18254
+ id: "occupancy",
18255
+ summary: "Any or all of motion or binary sensors, published as a binary. Presence zones are not aggregated. Nobody reporting is offline, not off.",
18256
+ role: DeviceRole.BinarySensor,
18257
+ outputCap: "binary",
18258
+ outputField: "on",
18259
+ defaultAggregate: "any",
18260
+ accept: [{
18261
+ cap: "motion",
18262
+ fieldPath: "detected"
18263
+ }, {
18264
+ cap: "binary",
18265
+ fieldPath: "on"
18266
+ }],
18267
+ companions: [CHANGED]
18268
+ }),
18269
+ binary({
18270
+ id: "binary",
18271
+ summary: "Any or all generic binary sensors. Offline and unknown members are left out, never read as off.",
18272
+ role: DeviceRole.BinarySensor,
18273
+ outputCap: "binary",
18274
+ outputField: "on",
18275
+ defaultAggregate: "any",
18276
+ accept: [{
18277
+ cap: "binary",
18278
+ fieldPath: "on"
18279
+ }],
18280
+ companions: [CHANGED]
18281
+ }),
18282
+ actuator({
18283
+ id: "switch",
18284
+ summary: "On when at least one switch is on, or when all of them are. Offline and unknown members are left out of that decision, never read as off, and still receive the command.",
18285
+ deviceType: DeviceType.Switch,
18286
+ outputCap: "switch",
18287
+ commandCaps: ["switch"]
18288
+ }),
18289
+ actuator({
18290
+ id: "light",
18291
+ summary: "On when at least one light is on, or when all of them are. Brightness and colour are offered only when every member has them. A command is not sent to a member that lacks it.",
18292
+ deviceType: DeviceType.Light,
18293
+ outputCap: "switch",
18294
+ commandCaps: [
18295
+ "switch",
18296
+ "brightness",
18297
+ "color"
18298
+ ]
18299
+ })
18300
+ ];
18301
+ function actuator(input) {
18302
+ return {
18303
+ ...input,
18304
+ family: "actuator",
18305
+ role: void 0,
18306
+ memberDeviceType: input.deviceType,
18307
+ outputField: "on",
18308
+ quantity: "none",
18309
+ baseUnit: void 0,
18310
+ aggregates: BINARY_AGGREGATES,
18311
+ defaultAggregate: "any",
18312
+ empty: "offline",
18313
+ accept: [{
18314
+ cap: "switch",
18315
+ fieldPath: "on"
18316
+ }],
18317
+ companions: [CHANGED]
18318
+ };
18319
+ }
18320
+ new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
18321
+ DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
17934
18322
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
17935
18323
  /**
17936
18324
  * What a composition may BUILD on a device type (G2, D676): the caps a new
@@ -19644,12 +20032,12 @@ method(object({ deviceId: number().int().nonnegative() }), DeviceAdminLinkSchema
19644
20032
  * providers detect renames by matching `uniqueId` and patch `id`
19645
20033
  * in-place so dispatch keeps working.
19646
20034
  *
19647
- * **Rendering metadata is NOT stored here.** Display fields (`unit`,
19648
- * `precision`) live in the cap STATUS SLICE so they flow live with every
19649
- * state push and are the single source of truth. The `device_class` tag is
19650
- * an HA-specific input that is consumed at the discovery boundary
19651
- * (`ha-domain-mapping`) to derive the CamStack `DeviceRole`; it is never
19652
- * stored after that point.
20035
+ * **Display unit and precision are NOT stored here.** They are a read-time
20036
+ * preference (D944). A polymorphic reading stores its `quantity` here once,
20037
+ * at adopt, and a date/time value stores `dateTimeShape` the same way —
20038
+ * neither is repeated on each sample. The `device_class` tag is an
20039
+ * HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
20040
+ * to derive the CamStack `DeviceRole`; it is never stored after that point.
19653
20041
  *
19654
20042
  * The `raw` escape hatch carries provider-specific attributes that don't
19655
20043
  * fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
@@ -19663,6 +20051,14 @@ var SourceInfoSchema = object({
19663
20051
  /** Immutable upstream identifier when available (HA `unique_id`, … ).
19664
20052
  * Used to detect rename when `id` changes. */
19665
20053
  uniqueId: string().optional(),
20054
+ /** Quantity of a polymorphic reading, written once at adopt (D944). */
20055
+ quantity: ReadingQuantitySchema.optional(),
20056
+ /** Date / time / datetime shape of a string reading, written once at adopt. */
20057
+ dateTimeShape: _enum([
20058
+ "date",
20059
+ "time",
20060
+ "datetime"
20061
+ ]).optional(),
19666
20062
  /** Provider-specific extras that don't fit the structured slots.
19667
20063
  * Whitelist what goes here per provider — do NOT dump entire HA
19668
20064
  * attribute blobs (would explode the meta JSON column). */
@@ -40575,7 +40971,8 @@ method(object({
40575
40971
  "enum"
40576
40972
  ]),
40577
40973
  enumValues: array(string()).optional(),
40578
- item: boolean().optional()
40974
+ item: boolean().optional(),
40975
+ quantity: ReadingQuantitySchema.optional()
40579
40976
  })).readonly(),
40580
40977
  itemArray: object({
40581
40978
  path: string(),
@@ -44375,8 +44772,8 @@ var MaskPolygonShapeSchema = object({
44375
44772
  /** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
44376
44773
  var MaskGridShapeSchema = object({
44377
44774
  kind: literal("grid"),
44378
- gridWidth: number(),
44379
- gridHeight: number(),
44775
+ gridWidth: number().meta(readingMeta("none")),
44776
+ gridHeight: number().meta(readingMeta("none")),
44380
44777
  cells: array(boolean())
44381
44778
  });
44382
44779
  discriminatedUnion("kind", [
@@ -44488,6 +44885,8 @@ var NcSystemEventKindSchema = _enum([
44488
44885
  "detection-blind",
44489
44886
  "motion-noisy",
44490
44887
  "motion-calm",
44888
+ "package-llm-silent",
44889
+ "package-llm-answered",
44491
44890
  "recording-stalled",
44492
44891
  "recording-healthy",
44493
44892
  "recording-degraded",
@@ -55515,6 +55914,28 @@ var PackageVersionInfoSchema = object({
55515
55914
  deprecated: string().optional(),
55516
55915
  distTags: array(string()).readonly()
55517
55916
  });
55917
+ /**
55918
+ * One published document. `ok` is markdown from the registry. `missing` means
55919
+ * the registry answered and the file was not there. `error` is a registry or
55920
+ * tarball failure — never an empty string standing in for either.
55921
+ */
55922
+ var PackageDocBodySchema = discriminatedUnion("status", [
55923
+ object({
55924
+ status: literal("ok"),
55925
+ markdown: string()
55926
+ }),
55927
+ object({ status: literal("missing") }),
55928
+ object({
55929
+ status: literal("error"),
55930
+ message: string()
55931
+ })
55932
+ ]);
55933
+ var PackageDocsSchema = object({
55934
+ name: string(),
55935
+ version: string(),
55936
+ readme: PackageDocBodySchema,
55937
+ changelog: PackageDocBodySchema
55938
+ });
55518
55939
  var SearchResultSchema = object({
55519
55940
  name: string(),
55520
55941
  version: string(),
@@ -55733,7 +56154,10 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
55733
56154
  }), object({ success: literal(true) }), {
55734
56155
  kind: "mutation",
55735
56156
  auth: "admin"
55736
- }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
56157
+ }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
56158
+ name: string().min(1),
56159
+ version: string().min(1).optional()
56160
+ }), PackageDocsSchema, { auth: "admin" }), method(object({ addonId: string() }), RestartAddonResultSchema, {
55737
56161
  kind: "mutation",
55738
56162
  auth: "admin"
55739
56163
  }), method(object({ packageName: string() }), object({ success: literal(true) }), {
@@ -55783,46 +56207,27 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
55783
56207
  }), LogStreamEntrySchema, { kind: "subscription" });
55784
56208
  object({
55785
56209
  /** Carbon dioxide concentration in ppm. */
55786
- co2Ppm: number().min(0).optional(),
56210
+ co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
55787
56211
  /** Total volatile organic compounds in ppb. */
55788
- vocPpb: number().min(0).optional(),
56212
+ vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
55789
56213
  /** Particulate matter ≤ 2.5 μm in µg/m³. */
55790
- pm25: number().min(0).optional(),
56214
+ pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
55791
56215
  /** Particulate matter ≤ 10 μm in µg/m³. */
55792
- pm10: number().min(0).optional(),
56216
+ pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
55793
56217
  /** Composite AQI value (typically 0..500). */
55794
- aqi: number().optional(),
56218
+ aqi: number().optional().meta(readingMeta("index")),
55795
56219
  /** Ms epoch when the slice was last updated. */
55796
- lastFetchedAt: number(),
55797
- /** Live display unit of the single metric this slice carries (e.g. HA
55798
- * `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
55799
- * upstream `sensor.*` entity surfaces ONE device_class, so one unit
55800
- * per slice is unambiguous. */
55801
- unit: string().optional(),
55802
- /** Suggested decimal places for numeric display.
55803
- * Populated live from the upstream source when provided (e.g. HA
55804
- * `attributes.suggested_display_precision`). Falls back to
55805
- * auto-formatting when absent. */
55806
- precision: number().int().min(0).max(10).optional()
56220
+ lastFetchedAt: number()
55807
56221
  });
55808
56222
  DeviceType.Sensor;
55809
56223
  object({
55810
- /** Current illuminance in lux (lx). */
55811
- lux: number().min(0).meta({
55812
- unit: "lx",
56224
+ /** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
56225
+ lux: number().min(0).nullable().meta(readingMeta("illuminance", {
56226
+ precision: 0,
55813
56227
  label: "Illuminance"
55814
- }),
56228
+ })),
55815
56229
  /** Ms epoch when the slice was last updated. */
55816
- lastFetchedAt: number(),
55817
- /** Live display unit from the upstream source (e.g. HA
55818
- * `attributes.unit_of_measurement`). The UI prefers this over the
55819
- * role's canonical unit. Absent → fall back to the canonical unit. */
55820
- unit: string().optional(),
55821
- /** Suggested decimal places for numeric display.
55822
- * Populated live from the upstream source when provided (e.g. HA
55823
- * `attributes.suggested_display_precision`). Falls back to
55824
- * auto-formatting when absent. */
55825
- precision: number().int().min(0).max(10).optional()
56230
+ lastFetchedAt: number()
55826
56231
  });
55827
56232
  DeviceType.Sensor;
55828
56233
  /**
@@ -55967,10 +56372,7 @@ var BatteryStatusSchema = object({
55967
56372
  * Consumers must SKIP a null rather than coerce it — `battery-band` already
55968
56373
  * declines to band a non-finite reading, which is the correct shape.
55969
56374
  */
55970
- percentage: number().min(0).max(100).nullable().meta({
55971
- unit: "%",
55972
- label: "Charge"
55973
- }),
56375
+ percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
55974
56376
  /**
55975
56377
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
55976
56378
  * Reolink-specific for the Solar Panel 2 accessory (will become
@@ -55997,7 +56399,14 @@ var BatteryStatusSchema = object({
55997
56399
  */
55998
56400
  sleeping: boolean().meta({ label: "Sleeping" }),
55999
56401
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
56000
- lastUpdated: number(),
56402
+ lastUpdated: number().meta(readingMeta("none")),
56403
+ /**
56404
+ * Battery voltage in volts, for a cell the firmware reports as voltage
56405
+ * rather than a percentage (Ecowitt capacitor packs). Absent on cameras
56406
+ * that only report charge. A second reading on this cap, not a second
56407
+ * `numeric-sensor` on the same device.
56408
+ */
56409
+ volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
56001
56410
  /**
56002
56411
  * Ms epoch of the last time the device PROVED it was reachable — a
56003
56412
  * completed firmware round-trip, an observed wake, or an inbound push
@@ -56057,7 +56466,7 @@ object({
56057
56466
  DeviceType.Sensor;
56058
56467
  object({
56059
56468
  /** Current level as 0..100 inclusive. Firmware-reported. */
56060
- percentage: number().min(0).max(100),
56469
+ percentage: number().min(0).max(100).meta(readingMeta("percentage")),
56061
56470
  /** Ms epoch of the last operator-driven change. Useful for UI freshness. */
56062
56471
  lastChangedAt: number()
56063
56472
  });
@@ -56442,21 +56851,21 @@ object({
56442
56851
  /** Single setpoint in Celsius. Used by single-target modes
56443
56852
  * (heat / cool / dry). Null when the device is in a range mode
56444
56853
  * (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
56445
- target: number().nullable(),
56854
+ target: number().nullable().meta(readingMeta("temperature")),
56446
56855
  /** Upper bound of the dual setpoint in Celsius. Populated only
56447
56856
  * in `heat_cool` mode. */
56448
- targetHigh: number().nullable(),
56857
+ targetHigh: number().nullable().meta(readingMeta("temperature")),
56449
56858
  /** Lower bound of the dual setpoint in Celsius. Populated only
56450
56859
  * in `heat_cool` mode. */
56451
- targetLow: number().nullable(),
56860
+ targetLow: number().nullable().meta(readingMeta("temperature")),
56452
56861
  /** Target relative humidity (0..100). Null when the device has no
56453
56862
  * humidity control. */
56454
- targetHumidity: number().min(0).max(100).nullable(),
56863
+ targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
56455
56864
  /** Current measured temperature in Celsius. Read-only — pushed by
56456
56865
  * the device's internal sensor. */
56457
- currentTemp: number().nullable(),
56866
+ currentTemp: number().nullable().meta(readingMeta("temperature")),
56458
56867
  /** Current measured relative humidity (0..100). Read-only. */
56459
- currentHumidity: number().min(0).max(100).nullable(),
56868
+ currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
56460
56869
  /** Vertical louver swing on/off. Null when the device has no vertical
56461
56870
  * swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
56462
56871
  swingVertical: boolean().nullable(),
@@ -56464,11 +56873,11 @@ object({
56464
56873
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
56465
56874
  swingHorizontal: boolean().nullable(),
56466
56875
  /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
56467
- minTemp: number().nullable().optional(),
56876
+ minTemp: number().nullable().optional().meta(readingMeta("temperature")),
56468
56877
  /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
56469
- maxTemp: number().nullable().optional(),
56878
+ maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
56470
56879
  /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
56471
- tempStep: number().positive().nullable().optional(),
56880
+ tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
56472
56881
  /** Ms epoch when the slice was last updated (push or command). */
56473
56882
  lastFetchedAt: number()
56474
56883
  });
@@ -56547,17 +56956,17 @@ DeviceType.Thermostat, DeviceType.Climate, method(object({
56547
56956
  * modes to expose in the picker without a slice fetch.
56548
56957
  */
56549
56958
  var RgbTripletSchema = object({
56550
- r: number().int().min(0).max(255),
56551
- g: number().int().min(0).max(255),
56552
- b: number().int().min(0).max(255)
56959
+ r: number().int().min(0).max(255).meta(readingMeta("none")),
56960
+ g: number().int().min(0).max(255).meta(readingMeta("none")),
56961
+ b: number().int().min(0).max(255).meta(readingMeta("none"))
56553
56962
  });
56554
56963
  var HsvTripletSchema = object({
56555
56964
  /** Hue in degrees, 0..360. */
56556
- h: number().min(0).max(360),
56965
+ h: number().min(0).max(360).meta(readingMeta("none")),
56557
56966
  /** Saturation as 0..100 inclusive. */
56558
- s: number().min(0).max(100),
56967
+ s: number().min(0).max(100).meta(readingMeta("none")),
56559
56968
  /** Value/brightness as 0..100 inclusive. */
56560
- v: number().min(0).max(100)
56969
+ v: number().min(0).max(100).meta(readingMeta("none"))
56561
56970
  });
56562
56971
  var ColorInputSchema = discriminatedUnion("mode", [
56563
56972
  object({
@@ -56586,7 +56995,7 @@ object({
56586
56995
  /** Populated when `mode === 'hsv'`. */
56587
56996
  hsv: HsvTripletSchema.optional(),
56588
56997
  /** Populated when `mode === 'mired'`. */
56589
- mireds: number().int().optional(),
56998
+ mireds: number().int().optional().meta(readingMeta("colour-temperature")),
56590
56999
  /** Ms epoch of the last operator-driven change. */
56591
57000
  lastChangedAt: number()
56592
57001
  });
@@ -56710,18 +57119,12 @@ _enum([
56710
57119
  "empty",
56711
57120
  "full"
56712
57121
  ]);
56713
- /** Units a remaining-life counter is reported in. Closed: the panel renders
56714
- * each. */
56715
- var ConsumableRemainingUnitSchema = _enum([
56716
- "days",
56717
- "hours",
56718
- "cycles"
56719
- ]);
56720
- /** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
56721
- * days). `value` null = unknown (D393). */
57122
+ /** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
57123
+ var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
57124
+ /** Remaining life. `value` null = unknown (D393). Duration is seconds. */
56722
57125
  var ConsumableRemainingSchema = object({
56723
- value: number().min(0).nullable(),
56724
- unit: ConsumableRemainingUnitSchema
57126
+ value: number().min(0).nullable().meta(readingMeta("device")),
57127
+ kind: ConsumableRemainingKindSchema
56725
57128
  });
56726
57129
  var ConsumablesStatusSchema = object({
56727
57130
  items: array(object({
@@ -56730,7 +57133,7 @@ var ConsumablesStatusSchema = object({
56730
57133
  /** Display name. */
56731
57134
  label: string().min(1),
56732
57135
  /** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
56733
- level: number().min(0).max(100).nullable(),
57136
+ level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
56734
57137
  /** The provider's discrete verdict when known (binary mode). */
56735
57138
  status: ConsumableStatusSchema.nullable(),
56736
57139
  /** What the item is — decides whether low or full is bad. Absent = `wear`. */
@@ -56747,7 +57150,7 @@ var ConsumablesStatusSchema = object({
56747
57150
  })),
56748
57151
  lastChangedAt: number()
56749
57152
  });
56750
- Object.values(DeviceType), [...ConsumableRemainingUnitSchema.options], [...ConsumableStatusSchema.options], [...ConsumableKindSchema.options], [...ConsumableFormSchema.options];
57153
+ Object.values(DeviceType), [...ConsumableRemainingKindSchema.options], [...ConsumableStatusSchema.options], [...ConsumableKindSchema.options], [...ConsumableFormSchema.options];
56751
57154
  Object.values(DeviceType), method(object({
56752
57155
  deviceId: number().int().nonnegative(),
56753
57156
  key: string().min(1)
@@ -56772,39 +57175,17 @@ object({
56772
57175
  /** Current value. Numeric kind → number; select/text/datetime → string.
56773
57176
  * Datetime values are ISO 8601 strings (full timestamp, date-only,
56774
57177
  * or time-only — HA accepts all three forms). */
56775
- value: union([number(), string()]),
57178
+ value: union([number().meta(readingMeta("device")), string()]),
56776
57179
  /** Acceptable values when `kind === 'select'`. Empty for other kinds. */
56777
57180
  options: array(string()),
56778
57181
  /** Ms epoch when the slice was last updated. */
56779
57182
  lastChangedAt: number(),
56780
- /** Display unit-of-measurement (e.g. '°C', '%', 'lx').
56781
- * Populated live from HA `attributes.unit_of_measurement` on each push.
56782
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
56783
- unit: string().optional(),
56784
- /** Minimum allowed value. Populated live from HA `attributes.min`.
56785
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
56786
- min: number().optional(),
56787
- /** Maximum allowed value. Populated live from HA `attributes.max`.
56788
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
56789
- max: number().optional(),
56790
- /** Step increment. Populated live from HA `attributes.step`.
56791
- * Meaningful for `kind === 'numeric'`; absent for other kinds. */
56792
- step: number().optional(),
56793
- /** Suggested decimal places for numeric display. Populated live from HA
56794
- * `attributes.suggested_display_precision`, or derived from `step` when the
56795
- * HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
56796
- * other kinds. Falls back to auto-formatting when absent. */
56797
- precision: number().int().min(0).max(10).optional(),
56798
- /** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
56799
- * 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
56800
- * (UI defaults to 'datetime'). Derived live from the device's HA domain
56801
- * (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
56802
- * `has_date`/`has_time` attributes on each push. */
56803
- format: _enum([
56804
- "date",
56805
- "time",
56806
- "datetime"
56807
- ]).optional()
57183
+ /** Minimum allowed value, in the device quantity's base unit. */
57184
+ min: number().optional().meta(readingMeta("device")),
57185
+ /** Maximum allowed value, in the device quantity's base unit. */
57186
+ max: number().optional().meta(readingMeta("device")),
57187
+ /** Step increment, in the device quantity's base unit. */
57188
+ step: number().optional().meta(readingMeta("device"))
56808
57189
  });
56809
57190
  var ControlSetValueInputSchema = discriminatedUnion("kind", [
56810
57191
  object({
@@ -56842,9 +57223,9 @@ object({
56842
57223
  ]),
56843
57224
  /** 0 = fully closed, 100 = fully open. Null when the device has
56844
57225
  * no intermediate position surface. */
56845
- position: number().min(0).max(100).nullable(),
57226
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
56846
57227
  /** 0 = closed slats, 100 = open slats. Null when no tilt support. */
56847
- tiltPosition: number().min(0).max(100).nullable(),
57228
+ tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
56848
57229
  /** Ms epoch when the slice was last updated. */
56849
57230
  lastChangedAt: number()
56850
57231
  });
@@ -56902,9 +57283,9 @@ var NormalizedRangeSchema$1 = object({
56902
57283
  object({
56903
57284
  mode: DayNightModeSchema,
56904
57285
  /** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
56905
- sensitivity: number().optional(),
57286
+ sensitivity: number().optional().meta(readingMeta("none")),
56906
57287
  /** Delay before the IR-cut filter flips, in seconds. */
56907
- switchDelaySec: number().optional(),
57288
+ switchDelaySec: number().optional().meta(readingMeta("duration")),
56908
57289
  lastFetchedAt: number()
56909
57290
  });
56910
57291
  /**
@@ -57059,11 +57440,11 @@ object({
57059
57440
  /** Feature keys of the programs the appliance currently offers — drives the UI selector. */
57060
57441
  availablePrograms: array(string()),
57061
57442
  /** Seconds remaining on the active cycle, or null when not running / not known. */
57062
- remainingSeconds: number().int().nonnegative().nullable(),
57443
+ remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
57063
57444
  /** Seconds the delayed start is set to (time until the cycle starts), or null. */
57064
- delayedStartSeconds: number().int().nonnegative().nullable(),
57445
+ delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
57065
57446
  /** Cycle completion 0..100, or null when not applicable. */
57066
- progressPercent: number().min(0).max(100).nullable(),
57447
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57067
57448
  /** True when the door is reported open; null when the door state is not known. */
57068
57449
  doorOpen: boolean().nullable(),
57069
57450
  /** True when the door is latched (locked) during a cycle; null when not reported. */
@@ -57087,7 +57468,7 @@ object({
57087
57468
  /** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
57088
57469
  machineCareDue: boolean().nullable(),
57089
57470
  /** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
57090
- machineCareRemainingRuns: number().int().nonnegative().nullable(),
57471
+ machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
57091
57472
  /** Option states: null when the appliance does not expose the option or has not said. */
57092
57473
  halfLoad: boolean().nullable(),
57093
57474
  hygienePlus: boolean().nullable(),
@@ -57102,7 +57483,7 @@ object({
57102
57483
  /** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
57103
57484
  activeEvents: array(string()),
57104
57485
  /** Numeric error code when the appliance reports one; null otherwise. */
57105
- errorCode: number().nullable(),
57486
+ errorCode: number().nullable().meta(readingMeta("none")),
57106
57487
  /** Name of the raised alert-level event while the appliance is in error; null otherwise. */
57107
57488
  errorLabel: string().nullable(),
57108
57489
  /** Ms epoch of the last value received from the appliance; 0 = never. */
@@ -57248,24 +57629,14 @@ object({
57248
57629
  /** Ms epoch of the last press. null = never observed since this provider started. */
57249
57630
  lastPressedAt: number().nullable(),
57250
57631
  /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
57251
- pressCountSinceStart: number()
57632
+ pressCountSinceStart: number().meta(readingMeta("count"))
57252
57633
  });
57253
57634
  object({
57254
57635
  deviceId: number(),
57255
57636
  timestamp: number()
57256
57637
  });
57257
57638
  DeviceType.Button, DeviceType.Camera;
57258
- /**
57259
- * Enum-state sensor — a string value picked from a finite option set.
57260
- * Drives HA `sensor` entries with `state_class: enum` (HVAC action
57261
- * states, weather conditions, contact-source channels, mode strings).
57262
- * The option set is stable across observations; it is persisted in the
57263
- * device config blob (via the control slice at adoption) and is NOT
57264
- * stored in `sourceInfo`.
57265
- */
57266
- /** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
57267
- * enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
57268
- var EnumSensorDateTimeFormatSchema = _enum([
57639
+ _enum([
57269
57640
  "date",
57270
57641
  "time",
57271
57642
  "datetime"
@@ -57273,12 +57644,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
57273
57644
  object({
57274
57645
  value: string(),
57275
57646
  /**
57276
- * Set for `DateTimeSensor`-role sensors so the UI renders the ISO `value`
57277
- * as a locale date/time/datetime; absent for plain enum sensors. HA
57278
- * `device_class=timestamp` → `'datetime'`, `device_class=date` → `'date'`,
57279
- * a time-only sensor → `'time'`.
57647
+ * Set when `value` is a timestamp rather than an enum state, so history
57648
+ * does not learn ISO strings as states. The display shape (date / time /
57649
+ * datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
57280
57650
  */
57281
- format: EnumSensorDateTimeFormatSchema.optional(),
57651
+ timestamp: boolean().optional(),
57282
57652
  /** Ms epoch when the slice was last updated. */
57283
57653
  lastFetchedAt: number()
57284
57654
  });
@@ -57297,13 +57667,13 @@ var EventFireSchema = object({
57297
57667
  deviceId: number(),
57298
57668
  eventType: string(),
57299
57669
  data: record(string(), unknown()).nullable(),
57300
- timestamp: number(),
57301
- seq: number()
57670
+ timestamp: number().meta(readingMeta("none")),
57671
+ seq: number().meta(readingMeta("count"))
57302
57672
  });
57303
57673
  object({
57304
57674
  eventTypes: array(string()),
57305
57675
  lastEvent: EventFireSchema.nullable(),
57306
- eventCountSinceStart: number()
57676
+ eventCountSinceStart: number().meta(readingMeta("count"))
57307
57677
  });
57308
57678
  DeviceType.EventEmitter;
57309
57679
  var IdentitySchema = object({
@@ -57692,10 +58062,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
57692
58062
  object({
57693
58063
  /** Active speed as 0..100 inclusive. Null when the device has no
57694
58064
  * speed surface (single-speed fan). */
57695
- percentage: number().min(0).max(100).nullable(),
58065
+ percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57696
58066
  /** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
57697
58067
  * a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
57698
- percentageStep: number().positive().optional(),
58068
+ percentageStep: number().positive().optional().meta(readingMeta("percentage")),
57699
58069
  /** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
57700
58070
  * — empty string when no preset is active or supported. */
57701
58071
  preset: string(),
@@ -57777,7 +58147,7 @@ var FeatureProbeStatusSchema = object({
57777
58147
  /** Camera/firmware model string. `null` when the firmware doesn't expose it. */
57778
58148
  model: string().nullable(),
57779
58149
  /** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
57780
- channelCount: number().nullable(),
58150
+ channelCount: number().nullable().meta(readingMeta("count")),
57781
58151
  /**
57782
58152
  * Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
57783
58153
  * completes — drivers' `getAccessoryChildren()` should treat zero as
@@ -57809,9 +58179,39 @@ object({
57809
58179
  lastChangedAt: number()
57810
58180
  });
57811
58181
  DeviceType.Sensor;
58182
+ object({
58183
+ /** One row per member that has been commanded. A later command replaces that member's row. */
58184
+ results: array(object({
58185
+ deviceId: number().int().nonnegative(),
58186
+ cap: string().min(1),
58187
+ method: string().min(1),
58188
+ ok: boolean(),
58189
+ error: string().optional(),
58190
+ /** Ms epoch of the attempt. */
58191
+ at: number()
58192
+ })) });
58193
+ DeviceType.Switch, DeviceType.Light;
58194
+ object({
58195
+ /** Members whose current value entered the aggregate. */
58196
+ included: number().int().nonnegative().meta(readingMeta("count", {
58197
+ precision: 0,
58198
+ label: "Reporting"
58199
+ })),
58200
+ /** Members left out: offline, unknown, or not a finite reading (D393). */
58201
+ excluded: number().int().nonnegative().meta(readingMeta("count", {
58202
+ precision: 0,
58203
+ label: "Excluded"
58204
+ })),
58205
+ /** `included` + `excluded`. */
58206
+ total: number().int().nonnegative().meta(readingMeta("count", {
58207
+ precision: 0,
58208
+ label: "Members"
58209
+ }))
58210
+ });
58211
+ DeviceType.Sensor, DeviceType.Switch, DeviceType.Light;
57812
58212
  object({
57813
58213
  /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
57814
- position: number().min(0).max(100).nullable(),
58214
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
57815
58215
  /** Ms epoch when the slice was last updated (push or poll). */
57816
58216
  lastFetchedAt: number()
57817
58217
  });
@@ -57820,9 +58220,9 @@ object({
57820
58220
  /** Whether the humidifier is currently on. */
57821
58221
  on: boolean(),
57822
58222
  /** Current measured relative humidity (0..100). Null when not reported. */
57823
- currentHumidity: number().min(0).max(100).nullable(),
58223
+ currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
57824
58224
  /** Target relative humidity (0..100). Null when no setpoint surface. */
57825
- targetHumidity: number().min(0).max(100).nullable(),
58225
+ targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
57826
58226
  /** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
57827
58227
  * device has no mode surface. */
57828
58228
  mode: string().nullable(),
@@ -57832,9 +58232,9 @@ object({
57832
58232
  * `idle` / `off`). Null when the device doesn't report it. */
57833
58233
  action: string().nullable(),
57834
58234
  /** HA `min_humidity` attribute. Null → UI uses 0. */
57835
- minHumidity: number().nullable(),
58235
+ minHumidity: number().nullable().meta(readingMeta("humidity")),
57836
58236
  /** HA `max_humidity` attribute. Null → UI uses 100. */
57837
- maxHumidity: number().nullable(),
58237
+ maxHumidity: number().nullable().meta(readingMeta("humidity")),
57838
58238
  /** Ms epoch when the slice was last updated. */
57839
58239
  lastChangedAt: number()
57840
58240
  });
@@ -57858,22 +58258,13 @@ DeviceType.Humidifier, method(object({
57858
58258
  auth: "protected"
57859
58259
  });
57860
58260
  object({
57861
- /** Current relative humidity, 0..100. */
57862
- percent: number().min(0).max(100).meta({
57863
- unit: "%",
58261
+ /** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
58262
+ percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
58263
+ precision: 0,
57864
58264
  label: "Humidity"
57865
- }),
58265
+ })),
57866
58266
  /** Ms epoch when the slice was last updated. */
57867
- lastFetchedAt: number(),
57868
- /** Live display unit from the upstream source (e.g. HA
57869
- * `attributes.unit_of_measurement`). The UI prefers this over the
57870
- * role's canonical unit. Absent → fall back to the canonical unit. */
57871
- unit: string().optional(),
57872
- /** Suggested decimal places for numeric display.
57873
- * Populated live from the upstream source when provided (e.g. HA
57874
- * `attributes.suggested_display_precision`). Falls back to
57875
- * auto-formatting when absent. */
57876
- precision: number().int().min(0).max(10).optional()
58267
+ lastFetchedAt: number()
57877
58268
  });
57878
58269
  DeviceType.Sensor, DeviceType.Thermostat;
57879
58270
  object({
@@ -57881,7 +58272,7 @@ object({
57881
58272
  * entity exposes no `entity_picture` (yet). */
57882
58273
  url: string().nullable(),
57883
58274
  /** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
57884
- lastUpdated: number().nullable()
58275
+ lastUpdated: number().nullable().meta(readingMeta("none"))
57885
58276
  });
57886
58277
  DeviceType.Image;
57887
58278
  /**
@@ -57937,19 +58328,19 @@ var NormalizedRangeSchema = object({
57937
58328
  });
57938
58329
  object({
57939
58330
  /** Normalized 0–100. */
57940
- brightness: number().optional(),
58331
+ brightness: number().optional().meta(readingMeta("none")),
57941
58332
  /** Normalized 0–100. */
57942
- contrast: number().optional(),
58333
+ contrast: number().optional().meta(readingMeta("none")),
57943
58334
  /** Normalized 0–100. */
57944
- saturation: number().optional(),
58335
+ saturation: number().optional().meta(readingMeta("none")),
57945
58336
  /** Normalized 0–100. */
57946
- sharpness: number().optional(),
58337
+ sharpness: number().optional().meta(readingMeta("none")),
57947
58338
  mirror: boolean().optional(),
57948
58339
  flip: boolean().optional(),
57949
58340
  rotate: ImageRotateSchema.optional(),
57950
58341
  whiteBalance: WhiteBalanceModeSchema.optional(),
57951
58342
  /** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
57952
- warmth: number().optional(),
58343
+ warmth: number().optional().meta(readingMeta("none")),
57953
58344
  exposureMode: ExposureModeSchema.optional(),
57954
58345
  backlightMode: BacklightModeSchema.optional(),
57955
58346
  lastFetchedAt: number()
@@ -58194,10 +58585,10 @@ var IntercomAbilitySchema = object({
58194
58585
  /** Codecs accepted by firmware, in preference order. Driver-authored strings. */
58195
58586
  codecs: array(string()),
58196
58587
  /** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
58197
- sampleRate: number(),
58588
+ sampleRate: number().meta(readingMeta("none")),
58198
58589
  duplex: _enum(["half", "full"]),
58199
58590
  /** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
58200
- maxBacklogMs: number()
58591
+ maxBacklogMs: number().meta(readingMeta("duration-ms"))
58201
58592
  });
58202
58593
  var IntercomStatusSchema = object({
58203
58594
  /** True while a talk session is open. */
@@ -58308,13 +58699,13 @@ object({
58308
58699
  activity: LawnMowerActivitySchema,
58309
58700
  /** 0..100 battery percentage. Null when the device has no battery
58310
58701
  * reading. */
58311
- batteryLevel: number().min(0).max(100).nullable(),
58702
+ batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58312
58703
  /** 0..100 mowing-completion percentage of the current task, or null when no
58313
58704
  * task is active / progress is unavailable. */
58314
- progressPercent: number().min(0).max(100).nullable(),
58705
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58315
58706
  /** Current device/event code (dynamic — mostly status, sometimes an error),
58316
58707
  * or null when unknown. */
58317
- currentCode: number().nullable(),
58708
+ currentCode: number().nullable().meta(readingMeta("none")),
58318
58709
  /** Human label for {@link currentCode}, or null when undecodable. */
58319
58710
  currentCodeLabel: string().nullable(),
58320
58711
  /** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
@@ -58776,7 +59167,7 @@ object({
58776
59167
  state: MediaPlayerStateSchema,
58777
59168
  /** Volume as 0..100 inclusive. Null when the device has no volume
58778
59169
  * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
58779
- volumeLevel: number().min(0).max(100).nullable(),
59170
+ volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
58780
59171
  /** Mute toggle distinct from volume=0. Null when no mute surface.
58781
59172
  * Pair with `DeviceFeature.MediaPlayerMute`. */
58782
59173
  isMuted: boolean().nullable(),
@@ -58789,10 +59180,10 @@ object({
58789
59180
  currentMedia: MediaInfoSchema.nullable(),
58790
59181
  /** Current playback position in ms. Null when not seekable or
58791
59182
  * nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
58792
- positionMs: number().int().nonnegative().nullable(),
59183
+ positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
58793
59184
  /** Total duration of the current media in ms. Null when unknown
58794
59185
  * (live stream) or nothing is playing. */
58795
- durationMs: number().int().nonnegative().nullable(),
59186
+ durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
58796
59187
  /** Shuffle toggle. Null when no shuffle surface.
58797
59188
  * Pair with `DeviceFeature.MediaPlayerShuffle`. */
58798
59189
  shuffle: boolean().nullable(),
@@ -59092,7 +59483,7 @@ object({
59092
59483
  * it, every provider seeded it at boot, and every session left it
59093
59484
  * `detected: true` after its detach.
59094
59485
  */
59095
- autoClearAfterMs: number().nullable()
59486
+ autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
59096
59487
  });
59097
59488
  /**
59098
59489
  * Payload of `motion.onMotionChanged` event + the corresponding bus
@@ -59142,7 +59533,7 @@ var MotionZoneRegionSchema = object({
59142
59533
  });
59143
59534
  object({
59144
59535
  enabled: boolean(),
59145
- sensitivity: number(),
59536
+ sensitivity: number().meta(readingMeta("none")),
59146
59537
  /** Grid region(s). Today exactly one `grid` shape. */
59147
59538
  regions: array(MotionZoneRegionSchema),
59148
59539
  lastFetchedAt: number()
@@ -59446,7 +59837,7 @@ var NavigationStatusSchema = object({
59446
59837
  following: boolean(),
59447
59838
  flash: boolean(),
59448
59839
  lightMode: NavigationLightModeSchema,
59449
- lightLevel: number().min(40).max(100),
59840
+ lightLevel: number().min(40).max(100).meta(readingMeta("none")),
59450
59841
  /** Ms epoch when the slice was last updated. */
59451
59842
  lastChangedAt: number()
59452
59843
  });
@@ -59492,13 +59883,13 @@ var NetworkLinkStatusSchema = object({
59492
59883
  * one whose reading has not landed must not be drawn at 0 %. Consumers
59493
59884
  * SKIP a null rather than coerce it.
59494
59885
  */
59495
- signalPercent: number().min(0).max(100).nullable(),
59886
+ signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
59496
59887
  /** Raw received signal strength in dBm, when the firmware reports one. */
59497
- rssiDbm: number().optional(),
59888
+ rssiDbm: number().optional().meta(readingMeta("signal-level")),
59498
59889
  /** Network name of a wireless link, when the firmware reports it. */
59499
59890
  ssid: string().optional(),
59500
59891
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
59501
- lastUpdated: number()
59892
+ lastUpdated: number().meta(readingMeta("none"))
59502
59893
  });
59503
59894
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.Light, DeviceType.Lock, DeviceType.Siren, object({
59504
59895
  deviceId: number(),
@@ -59817,7 +60208,7 @@ object({
59817
60208
  lastError: string().nullable(),
59818
60209
  /** Number of deliveries currently buffered server-side (rate-limited
59819
60210
  * notifiers may queue). 0 when the provider sends synchronously. */
59820
- queueDepth: number().int().nonnegative(),
60211
+ queueDepth: number().int().nonnegative().meta(readingMeta("count")),
59821
60212
  /** Per-feature capability matrix — authoritative source for the
59822
60213
  * compose-form field gating. */
59823
60214
  supports: NotifierSupportsSchema
@@ -59867,15 +60258,8 @@ DeviceType.Notifier, method(NotifierSendInputSchema, NotifierSendResultSchema, {
59867
60258
  acceptedAt: number()
59868
60259
  });
59869
60260
  object({
59870
- value: number(),
59871
- /** Display unit-of-measurement (e.g. 'dBm', 's', 'rpm', 'steps').
59872
- * Populated live from the upstream source on each state push.
59873
- * Absent for unitless sensors. */
59874
- unit: string().optional(),
59875
- /** Suggested decimal places for numeric display.
59876
- * Populated live from the upstream source when provided.
59877
- * Falls back to auto-formatting when absent. */
59878
- precision: number().int().min(0).max(10).optional(),
60261
+ /** Reading in the device quantity's base unit. `null` when refused (D393). */
60262
+ value: number().nullable().meta(readingMeta("device")),
59879
60263
  /** Ms epoch when the slice was last updated. */
59880
60264
  lastFetchedAt: number()
59881
60265
  });
@@ -60165,23 +60549,26 @@ object({
60165
60549
  /** Food currently in the bowl (grams). Null when the device has not
60166
60550
  * reported a reading yet. On dual-hopper models this is the combined
60167
60551
  * bowl reading; per-hopper levels live in `food1`/`food2`. */
60168
- foodLevel: number().nullable(),
60552
+ foodLevel: number().nullable().meta(readingMeta("none")),
60169
60553
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
60170
60554
  * single-hopper models. */
60171
- food1: number().nullable(),
60555
+ food1: number().nullable().meta(readingMeta("none")),
60172
60556
  /** Hopper-2 food level (grams) on dual-hopper feeders; null on
60173
60557
  * single-hopper models. */
60174
- food2: number().nullable(),
60558
+ food2: number().nullable().meta(readingMeta("none")),
60175
60559
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
60176
60560
  * (`device_class: problem`, on = low). True when the bowl is empty /
60177
60561
  * below the feeder's low threshold. */
60178
60562
  lowFood: boolean(),
60179
60563
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
60180
60564
  * device has no battery reading. */
60181
- batteryPower: number().min(0).max(100).nullable(),
60182
- /** Days of desiccant life remaining. Null when the model has no
60183
- * desiccant sensor. */
60184
- desiccantLeftDays: number().nullable(),
60565
+ batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60566
+ /**
60567
+ * Desiccant life remaining, stored in seconds (duration base, D944).
60568
+ * Providers convert from the days the feeder reports. Null when the
60569
+ * model has no desiccant sensor.
60570
+ */
60571
+ desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
60185
60572
  /** True while a feed is in progress. */
60186
60573
  feeding: boolean(),
60187
60574
  /** Decoded connectivity / power status (HA petkit device-status enum).
@@ -60192,7 +60579,7 @@ object({
60192
60579
  * with `errorCode` for consumers that want the raw integer. */
60193
60580
  error: string().nullable(),
60194
60581
  /** Raw device error code (0 / null = no error). */
60195
- errorCode: number().nullable(),
60582
+ errorCode: number().nullable().meta(readingMeta("none")),
60196
60583
  /** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
60197
60584
  isDualHopper: boolean(),
60198
60585
  /** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
@@ -60202,7 +60589,7 @@ object({
60202
60589
  /** Play a chime when dispensing. */
60203
60590
  feedSound: boolean(),
60204
60591
  /** Speaker / prompt volume level (device-scaled integer). */
60205
- volume: number(),
60592
+ volume: number().meta(readingMeta("none")),
60206
60593
  /** Ms epoch when the slice was last refreshed from the cloud. */
60207
60594
  lastFetchedAt: number()
60208
60595
  });
@@ -60639,36 +61026,19 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
60639
61026
  });
60640
61027
  object({
60641
61028
  /** Instantaneous power draw in watts. */
60642
- watts: number().optional().meta({
60643
- unit: "W",
60644
- label: "Power"
60645
- }),
61029
+ watts: number().optional().meta(readingMeta("power", { label: "Power" })),
60646
61030
  /** Cumulative energy in kilowatt-hours since the meter was reset. */
60647
- kwhTotal: number().optional().meta({
60648
- unit: "kWh",
60649
- label: "Energy"
60650
- }),
61031
+ kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
60651
61032
  /** Voltage in volts. */
60652
- volts: number().optional(),
61033
+ volts: number().optional().meta(readingMeta("voltage")),
60653
61034
  /** Current in amperes. */
60654
- amps: number().optional(),
60655
- /** Apparent power in volt-amperes (W × power-factor reciprocal). */
60656
- va: number().optional(),
61035
+ amps: number().optional().meta(readingMeta("current")),
61036
+ /** Apparent power in volt-amperes. */
61037
+ va: number().optional().meta(readingMeta("apparent-power")),
60657
61038
  /** Power factor (0..1) if reported. */
60658
- powerFactor: number().min(0).max(1).optional(),
61039
+ powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
60659
61040
  /** Ms epoch when the slice was last updated. */
60660
- lastFetchedAt: number(),
60661
- /** Live display unit of the single metric this slice carries (e.g. HA
60662
- * `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
60663
- * Each upstream `sensor.*` entity surfaces ONE device_class, so one
60664
- * unit per slice is unambiguous. The UI prefers this over the role's
60665
- * canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
60666
- unit: string().optional(),
60667
- /** Suggested decimal places for numeric display.
60668
- * Populated live from the upstream source when provided (e.g. HA
60669
- * `attributes.suggested_display_precision`). Falls back to
60670
- * auto-formatting when absent. */
60671
- precision: number().int().min(0).max(10).optional()
61041
+ lastFetchedAt: number()
60672
61042
  });
60673
61043
  DeviceType.Sensor;
60674
61044
  /**
@@ -60689,11 +61059,11 @@ DeviceType.Sensor;
60689
61059
  */
60690
61060
  var GpsLocationSchema = object({
60691
61061
  /** Latitude in decimal degrees, -90..90. */
60692
- latitude: number().min(-90).max(90),
61062
+ latitude: number().min(-90).max(90).meta(readingMeta("angle")),
60693
61063
  /** Longitude in decimal degrees, -180..180. */
60694
- longitude: number().min(-180).max(180),
61064
+ longitude: number().min(-180).max(180).meta(readingMeta("angle")),
60695
61065
  /** Reported accuracy in meters (lower = better). */
60696
- accuracyMeters: number().nonnegative()
61066
+ accuracyMeters: number().nonnegative().meta(readingMeta("length"))
60697
61067
  });
60698
61068
  object({
60699
61069
  /** `home` / `not_home` / any user-defined zone name. */
@@ -60705,25 +61075,16 @@ object({
60705
61075
  gps: GpsLocationSchema.nullable(),
60706
61076
  /** Optional battery level of the tracking device (0..100). Null
60707
61077
  * when not reported. */
60708
- batteryPercent: number().min(0).max(100).nullable(),
61078
+ batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
60709
61079
  /** Ms epoch when the slice was last updated. */
60710
61080
  lastChangedAt: number()
60711
61081
  });
60712
61082
  DeviceType.Presence;
60713
61083
  object({
60714
- /** Current pressure in hPa. */
60715
- hpa: number(),
61084
+ /** Current pressure in hPa. `null` when the sample was refused (D393). */
61085
+ hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
60716
61086
  /** Ms epoch when the slice was last updated. */
60717
- lastFetchedAt: number(),
60718
- /** Live display unit from the upstream source (e.g. HA
60719
- * `attributes.unit_of_measurement`). The UI prefers this over the
60720
- * role's canonical unit. Absent → fall back to the canonical unit. */
60721
- unit: string().optional(),
60722
- /** Suggested decimal places for numeric display.
60723
- * Populated live from the upstream source when provided (e.g. HA
60724
- * `attributes.suggested_display_precision`). Falls back to
60725
- * auto-formatting when absent. */
60726
- precision: number().int().min(0).max(10).optional()
61087
+ lastFetchedAt: number()
60727
61088
  });
60728
61089
  DeviceType.Sensor;
60729
61090
  /**
@@ -61056,8 +61417,8 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
61056
61417
  }), _void(), { kind: "mutation" });
61057
61418
  var PtzAutotrackSettingsSchema = object({
61058
61419
  targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
61059
- stopDelaySeconds: number().int().min(0).max(300),
61060
- disappearDelaySeconds: number().int().min(0).max(300)
61420
+ stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
61421
+ disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
61061
61422
  });
61062
61423
  /**
61063
61424
  * One option the camera advertises as a valid `targetType`. Drivers
@@ -61114,7 +61475,7 @@ object({
61114
61475
  /** null = unknown — never coerced to false (D393). */
61115
61476
  running: boolean().nullable(),
61116
61477
  /** Epoch ms the current boolean began; null while `running` is null. */
61117
- since: number().nullable(),
61478
+ since: number().nullable().meta(readingMeta("none")),
61118
61479
  source: RunningStateSourceSchema.meta({ label: "Source" }),
61119
61480
  phase: string().nullable(),
61120
61481
  /** null until a reading has been published (D393). */
@@ -62312,9 +62673,9 @@ var RecordTriggerSchema = _enum([
62312
62673
  */
62313
62674
  var RecordWindowSchema = object({
62314
62675
  trigger: RecordTriggerSchema,
62315
- day: number().int().min(0).max(6),
62316
- startMinute: number().int().min(0).max(1439),
62317
- endMinute: number().int().min(1).max(1440)
62676
+ day: number().int().min(0).max(6).meta(readingMeta("none")),
62677
+ startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
62678
+ endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
62318
62679
  });
62319
62680
  /** Status of one physical volume, as the camera itself describes it. */
62320
62681
  var OnboardStorageVolumeSchema = object({
@@ -62383,10 +62744,10 @@ object({
62383
62744
  /** Master "record to the card at all" switch. */
62384
62745
  enabled: boolean().nullable(),
62385
62746
  overwriteWhenFull: boolean().nullable(),
62386
- preRecordSec: number().nullable(),
62387
- postRecordSec: number().nullable(),
62747
+ preRecordSec: number().nullable().meta(readingMeta("duration")),
62748
+ postRecordSec: number().nullable().meta(readingMeta("duration")),
62388
62749
  /** Length of one recorded file, in minutes. */
62389
- segmentMinutes: number().nullable(),
62750
+ segmentMinutes: number().nullable().meta(readingMeta("none")),
62390
62751
  /** The primary track's weekly windows, flattened. */
62391
62752
  windows: array(RecordWindowSchema),
62392
62753
  /**
@@ -62402,7 +62763,7 @@ object({
62402
62763
  * provider reporting a non-zero count also reports the schedule as not
62403
62764
  * writable.
62404
62765
  */
62405
- unreadableWindows: number(),
62766
+ unreadableWindows: number().meta(readingMeta("count")),
62406
62767
  /**
62407
62768
  * The camera is scheduled to record and has NO usable storage.
62408
62769
  *
@@ -63039,7 +63400,7 @@ var CamStreamDescriptorSchema = object({
63039
63400
  url: string().optional(),
63040
63401
  codec: string().optional(),
63041
63402
  resolution: CamStreamResolutionSchema.optional(),
63042
- fps: number().positive().optional(),
63403
+ fps: number().positive().optional().meta(readingMeta("rate")),
63043
63404
  label: string().optional(),
63044
63405
  /** Device-level features (e.g. `battery-operated`) — drives broker policy. */
63045
63406
  deviceFeatures: array(string()).optional(),
@@ -63069,18 +63430,18 @@ var StreamProfileSchema = _enum([
63069
63430
  ]);
63070
63431
  /** The current encoding config of one stream profile. */
63071
63432
  var StreamProfileConfigSchema = object({
63072
- width: number(),
63073
- height: number(),
63433
+ width: number().meta(readingMeta("none")),
63434
+ height: number().meta(readingMeta("none")),
63074
63435
  codec: _enum(["h264", "h265"]),
63075
- framerate: number(),
63076
- bitrate: number(),
63436
+ framerate: number().meta(readingMeta("rate")),
63437
+ bitrate: number().meta(readingMeta("bitrate")),
63077
63438
  bitrateMode: _enum(["vbr", "cbr"]).optional(),
63078
63439
  encoderProfile: _enum([
63079
63440
  "high",
63080
63441
  "main",
63081
63442
  "baseline"
63082
63443
  ]).optional(),
63083
- gop: number().optional(),
63444
+ gop: number().optional().meta(readingMeta("none")),
63084
63445
  /**
63085
63446
  * Whether THIS profile currently carries an audio track. READ-ONLY here.
63086
63447
  *
@@ -63723,23 +64084,13 @@ object({
63723
64084
  });
63724
64085
  DeviceType.Sensor;
63725
64086
  object({
63726
- /** Current temperature in Celsius. */
63727
- celsius: number().meta({
63728
- unit: "°C",
64087
+ /** Current temperature in Celsius. `null` when the sample was refused (D393). */
64088
+ celsius: number().nullable().meta(readingMeta("temperature", {
64089
+ precision: 1,
63729
64090
  label: "Temperature"
63730
- }),
64091
+ })),
63731
64092
  /** Ms epoch when the slice was last updated (push or poll). */
63732
- lastFetchedAt: number(),
63733
- /** Live display unit from the upstream source (e.g. HA
63734
- * `attributes.unit_of_measurement`). The UI prefers this over the
63735
- * role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
63736
- * Absent → the UI falls back to the role's canonical unit. */
63737
- unit: string().optional(),
63738
- /** Suggested decimal places for numeric display.
63739
- * Populated live from the upstream source when provided (e.g. HA
63740
- * `attributes.suggested_display_precision`). Falls back to
63741
- * auto-formatting when absent. */
63742
- precision: number().int().min(0).max(10).optional()
64093
+ lastFetchedAt: number()
63743
64094
  });
63744
64095
  DeviceType.Sensor, DeviceType.Thermostat;
63745
64096
  /**
@@ -63991,16 +64342,16 @@ object({
63991
64342
  ]),
63992
64343
  /** 0..100 battery percentage. Null when the device has no battery
63993
64344
  * reading. */
63994
- batteryLevel: number().min(0).max(100).nullable(),
64345
+ batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
63995
64346
  /** Current fan-speed token (provider-verbatim). Null when unknown or
63996
64347
  * the vacuum has no speed control. */
63997
64348
  fanSpeed: string().nullable(),
63998
64349
  /** Speed tokens the hardware accepts — drives the UI selector. */
63999
64350
  availableFanSpeeds: array(string()),
64000
64351
  /** 0..100 cleaning-completion percentage of the current task, or null. */
64001
- progressPercent: number().min(0).max(100).nullable(),
64352
+ progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64002
64353
  /** Current error code (0 / null = no error). */
64003
- errorCode: number().nullable(),
64354
+ errorCode: number().nullable().meta(readingMeta("none")),
64004
64355
  /** Human label for {@link errorCode}, or null when none / undecodable. */
64005
64356
  errorLabel: string().nullable(),
64006
64357
  /** Ms epoch when the slice was last updated. */
@@ -64039,7 +64390,7 @@ object({
64039
64390
  ]),
64040
64391
  /** 0 = fully closed, 100 = fully open. Null when the device has no
64041
64392
  * intermediate position surface. */
64042
- position: number().min(0).max(100).nullable(),
64393
+ position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
64043
64394
  /** Ms epoch when the slice was last updated. */
64044
64395
  lastChangedAt: number()
64045
64396
  });
@@ -64067,9 +64418,9 @@ object({
64067
64418
  DeviceType.Sensor;
64068
64419
  object({
64069
64420
  /** Current measured temperature. Null when not reported. */
64070
- currentTemp: number().nullable(),
64421
+ currentTemp: number().nullable().meta(readingMeta("temperature")),
64071
64422
  /** Target temperature setpoint. Null when no setpoint surface. */
64072
- targetTemp: number().nullable(),
64423
+ targetTemp: number().nullable().meta(readingMeta("temperature")),
64073
64424
  /** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
64074
64425
  * `heat_pump` / `high_demand` / `performance` / `off`). Null when the
64075
64426
  * device reports an unknown state. */
@@ -64080,9 +64431,9 @@ object({
64080
64431
  * has no away surface. */
64081
64432
  away: boolean().nullable(),
64082
64433
  /** HA `min_temp` attribute. Null when not reported. */
64083
- minTemp: number().nullable(),
64434
+ minTemp: number().nullable().meta(readingMeta("temperature")),
64084
64435
  /** HA `max_temp` attribute. Null when not reported. */
64085
- maxTemp: number().nullable(),
64436
+ maxTemp: number().nullable().meta(readingMeta("temperature")),
64086
64437
  /** Ms epoch when the slice was last updated. */
64087
64438
  lastChangedAt: number()
64088
64439
  });
@@ -64106,25 +64457,18 @@ DeviceType.WaterHeater, method(object({
64106
64457
  auth: "protected"
64107
64458
  });
64108
64459
  object({
64109
- /** Verbatim HA condition state (`sunny`, `cloudy`, `rainy`, …). Null
64110
- * when no condition has been reported yet. */
64460
+ /** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
64111
64461
  condition: string().nullable(),
64112
- /** Current temperature in the reported unit. Null when not provided. */
64113
- temperature: number().nullable(),
64114
- /** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
64115
- temperatureUnit: string().nullable(),
64462
+ /** Current temperature in Celsius. Null when not provided or refused. */
64463
+ temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
64116
64464
  /** Relative humidity (0..100). Null when not provided. */
64117
- humidity: number().min(0).max(100).nullable(),
64118
- /** Barometric pressure in the reported unit. Null when not provided. */
64119
- pressure: number().nullable(),
64120
- /** Pressure unit string (e.g. `hPa` / `inHg`). Null when not provided. */
64121
- pressureUnit: string().nullable(),
64122
- /** Wind speed in the reported unit. Null when not provided. */
64123
- windSpeed: number().nullable(),
64124
- /** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
64125
- windSpeedUnit: string().nullable(),
64465
+ humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
64466
+ /** Barometric pressure in hPa. Null when not provided or refused. */
64467
+ pressure: number().nullable().meta(readingMeta("pressure")),
64468
+ /** Wind speed in metres per second. Null when not provided or refused. */
64469
+ windSpeed: number().nullable().meta(readingMeta("speed")),
64126
64470
  /** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
64127
- windBearing: number().nullable(),
64471
+ windBearing: number().nullable().meta(readingMeta("angle")),
64128
64472
  /** Ms epoch when the slice was last updated. */
64129
64473
  lastFetchedAt: number()
64130
64474
  });
@@ -65036,6 +65380,13 @@ Object.freeze({
65036
65380
  access: "view",
65037
65381
  role: "admin"
65038
65382
  },
65383
+ "addons.getPackageDocs": {
65384
+ capName: "addons",
65385
+ capScope: "system",
65386
+ addonId: null,
65387
+ access: "view",
65388
+ role: "admin"
65389
+ },
65039
65390
  "addons.getVersions": {
65040
65391
  capName: "addons",
65041
65392
  capScope: "system",
@@ -68144,6 +68495,20 @@ Object.freeze({
68144
68495
  access: "view",
68145
68496
  role: "viewer"
68146
68497
  },
68498
+ "groupCommands.getStatus": {
68499
+ capName: "group-commands",
68500
+ capScope: "device",
68501
+ addonId: null,
68502
+ access: "view",
68503
+ role: "viewer"
68504
+ },
68505
+ "groupStatus.getStatus": {
68506
+ capName: "group-status",
68507
+ capScope: "device",
68508
+ addonId: null,
68509
+ access: "view",
68510
+ role: "viewer"
68511
+ },
68147
68512
  "heatingValve.getStatus": {
68148
68513
  capName: "heating-valve",
68149
68514
  capScope: "device",
@@ -77966,6 +78331,37 @@ var SENSOR_CAP_VOCABULARY = {
77966
78331
  icon: "flame"
77967
78332
  }]
77968
78333
  },
78334
+ "group-commands": {
78335
+ scope: "device",
78336
+ noun: t("Group command", "Comando di gruppo")
78337
+ },
78338
+ "group-status": {
78339
+ scope: "device",
78340
+ noun: t("Group", "Gruppo"),
78341
+ state: [
78342
+ {
78343
+ kind: "numeric",
78344
+ field: "included",
78345
+ quantity: t("the members reporting", "i membri che rispondono"),
78346
+ label: t("Reporting", "Rispondono"),
78347
+ icon: "users"
78348
+ },
78349
+ {
78350
+ kind: "numeric",
78351
+ field: "excluded",
78352
+ quantity: t("the members left out", "i membri esclusi"),
78353
+ label: t("Excluded", "Esclusi"),
78354
+ icon: "users"
78355
+ },
78356
+ {
78357
+ kind: "numeric",
78358
+ field: "total",
78359
+ quantity: t("the member count", "il numero di membri"),
78360
+ label: t("Members", "Membri"),
78361
+ icon: "sigma"
78362
+ }
78363
+ ]
78364
+ },
77969
78365
  "humidity-sensor": {
77970
78366
  scope: "device",
77971
78367
  noun: t("Humidity", "Umidità"),
@@ -77999,7 +78395,6 @@ var SENSOR_CAP_VOCABULARY = {
77999
78395
  field: "value",
78000
78396
  quantity: t("the reading", "il valore"),
78001
78397
  label: t("Reading", "Valore"),
78002
- unitField: "unit",
78003
78398
  icon: "gauge"
78004
78399
  }]
78005
78400
  },
@@ -78135,7 +78530,7 @@ var SENSOR_CAP_VOCABULARY = {
78135
78530
  field: "temperature",
78136
78531
  quantity: t("the outdoor temperature", "la temperatura esterna"),
78137
78532
  label: t("Temperature", "Temperatura"),
78138
- unitField: "temperatureUnit",
78533
+ unit: "°C",
78139
78534
  icon: "thermometer"
78140
78535
  },
78141
78536
  {
@@ -78151,7 +78546,7 @@ var SENSOR_CAP_VOCABULARY = {
78151
78546
  field: "windSpeed",
78152
78547
  quantity: t("the wind speed", "la velocità del vento"),
78153
78548
  label: t("Wind", "Vento"),
78154
- unitField: "windSpeedUnit",
78549
+ unit: "m/s",
78155
78550
  icon: "wind"
78156
78551
  },
78157
78552
  {
@@ -78159,7 +78554,7 @@ var SENSOR_CAP_VOCABULARY = {
78159
78554
  field: "pressure",
78160
78555
  quantity: t("the air pressure", "la pressione atmosferica"),
78161
78556
  label: t("Pressure", "Pressione"),
78162
- unitField: "pressureUnit",
78557
+ unit: "hPa",
78163
78558
  icon: "gauge"
78164
78559
  }
78165
78560
  ]
@@ -79432,6 +79827,16 @@ Object.freeze({
79432
79827
  form: "single",
79433
79828
  optional: false
79434
79829
  }],
79830
+ "groupCommands.getStatus": [{
79831
+ name: "deviceId",
79832
+ form: "single",
79833
+ optional: false
79834
+ }],
79835
+ "groupStatus.getStatus": [{
79836
+ name: "deviceId",
79837
+ form: "single",
79838
+ optional: false
79839
+ }],
79435
79840
  "heatingValve.getStatus": [{
79436
79841
  name: "deviceId",
79437
79842
  form: "single",
@@ -81416,6 +81821,8 @@ new Map(Object.entries({
81416
81821
  "detection-blind": "device-events",
81417
81822
  "motion-noisy": "device-events",
81418
81823
  "motion-calm": "device-events",
81824
+ "package-llm-silent": "device-events",
81825
+ "package-llm-answered": "device-events",
81419
81826
  "recording-stalled": "device-events",
81420
81827
  "recording-healthy": "device-events",
81421
81828
  "recording-degraded": "device-events",
@@ -81499,6 +81906,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
81499
81906
  label: "Motion without a subject",
81500
81907
  kinds: ["motion-noisy", "motion-calm"]
81501
81908
  },
81909
+ {
81910
+ id: "package-llm",
81911
+ family: "device-events",
81912
+ label: "Package confirm",
81913
+ kinds: ["package-llm-silent", "package-llm-answered"]
81914
+ },
81502
81915
  {
81503
81916
  id: "recording",
81504
81917
  family: "device-events",
@@ -81680,6 +82093,8 @@ var DEVICE_EVENTS = [
81680
82093
  "detection-blind",
81681
82094
  "motion-noisy",
81682
82095
  "motion-calm",
82096
+ "package-llm-silent",
82097
+ "package-llm-answered",
81683
82098
  "recording-stalled",
81684
82099
  "recording-healthy",
81685
82100
  "recording-degraded",
@@ -81948,6 +82363,15 @@ new Map([
81948
82363
  snapshotLabel: "Cameras with constant motion now",
81949
82364
  snapshotSection: null
81950
82365
  },
82366
+ {
82367
+ id: "package-llm-confirm",
82368
+ family: "device-events",
82369
+ label: "Package confirm (not answering / answering again)",
82370
+ bad: ["package-llm-silent"],
82371
+ recovery: ["package-llm-answered"],
82372
+ snapshotLabel: "Cameras whose package confirm is not answering",
82373
+ snapshotSection: null
82374
+ },
81951
82375
  {
81952
82376
  id: "recording-health",
81953
82377
  family: "device-events",