@camstack/addon-provider-homematic 1.2.156 → 1.2.158
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.
- package/dist/addon.js +956 -509
- package/dist/addon.mjs +956 -509
- package/package.json +7 -2
package/dist/addon.js
CHANGED
|
@@ -5576,7 +5576,7 @@ var ZodIssueCode = {
|
|
|
5576
5576
|
var ZodFirstPartyTypeKind;
|
|
5577
5577
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5578
5578
|
//#endregion
|
|
5579
|
-
//#region ../types/dist/sleep-
|
|
5579
|
+
//#region ../types/dist/sleep-CARd6wZF.mjs
|
|
5580
5580
|
/**
|
|
5581
5581
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5582
5582
|
* window to float samples (D455).
|
|
@@ -8318,6 +8318,99 @@ function defineEntryKind(name, spec) {
|
|
|
8318
8318
|
if (defined === void 0) throw new DeclarationRefusedError(`entry kind "${name}" was not built`);
|
|
8319
8319
|
return defined;
|
|
8320
8320
|
}
|
|
8321
|
+
var READING_QUANTITIES = [
|
|
8322
|
+
"temperature",
|
|
8323
|
+
"pressure",
|
|
8324
|
+
"speed",
|
|
8325
|
+
"precipitation",
|
|
8326
|
+
"precipitation-rate",
|
|
8327
|
+
"length",
|
|
8328
|
+
"area",
|
|
8329
|
+
"illuminance",
|
|
8330
|
+
"irradiance",
|
|
8331
|
+
"power",
|
|
8332
|
+
"energy",
|
|
8333
|
+
"voltage",
|
|
8334
|
+
"current",
|
|
8335
|
+
"apparent-power",
|
|
8336
|
+
"humidity",
|
|
8337
|
+
"percentage",
|
|
8338
|
+
"concentration-volume",
|
|
8339
|
+
"concentration-mass",
|
|
8340
|
+
"concentration-ppb",
|
|
8341
|
+
"volume",
|
|
8342
|
+
"duration",
|
|
8343
|
+
"duration-ms",
|
|
8344
|
+
"information",
|
|
8345
|
+
"sound-level",
|
|
8346
|
+
"signal-level",
|
|
8347
|
+
"colour-temperature",
|
|
8348
|
+
"angle",
|
|
8349
|
+
"bitrate",
|
|
8350
|
+
"rate",
|
|
8351
|
+
"mass",
|
|
8352
|
+
"index",
|
|
8353
|
+
"ratio",
|
|
8354
|
+
"count",
|
|
8355
|
+
"none",
|
|
8356
|
+
"device"
|
|
8357
|
+
];
|
|
8358
|
+
var ReadingQuantitySchema = _enum(READING_QUANTITIES);
|
|
8359
|
+
new Set(READING_QUANTITIES);
|
|
8360
|
+
/**
|
|
8361
|
+
* Stored unit symbol for a quantity. `none` means the number is dimensionless
|
|
8362
|
+
* (or an index / ratio / count). `device` means the cap has no single unit.
|
|
8363
|
+
*/
|
|
8364
|
+
var QUANTITY_BASE = {
|
|
8365
|
+
temperature: "°C",
|
|
8366
|
+
pressure: "hPa",
|
|
8367
|
+
speed: "m/s",
|
|
8368
|
+
precipitation: "mm",
|
|
8369
|
+
"precipitation-rate": "mm/h",
|
|
8370
|
+
length: "m",
|
|
8371
|
+
area: "m²",
|
|
8372
|
+
illuminance: "lx",
|
|
8373
|
+
irradiance: "W/m²",
|
|
8374
|
+
power: "W",
|
|
8375
|
+
energy: "kWh",
|
|
8376
|
+
voltage: "V",
|
|
8377
|
+
current: "A",
|
|
8378
|
+
"apparent-power": "VA",
|
|
8379
|
+
humidity: "%",
|
|
8380
|
+
percentage: "%",
|
|
8381
|
+
"concentration-volume": "ppm",
|
|
8382
|
+
"concentration-mass": "µg/m³",
|
|
8383
|
+
"concentration-ppb": "ppb",
|
|
8384
|
+
volume: "L",
|
|
8385
|
+
duration: "s",
|
|
8386
|
+
"duration-ms": "ms",
|
|
8387
|
+
information: "B",
|
|
8388
|
+
"sound-level": "dBFS",
|
|
8389
|
+
"signal-level": "dBm",
|
|
8390
|
+
"colour-temperature": "mired",
|
|
8391
|
+
angle: "°",
|
|
8392
|
+
bitrate: "kbps",
|
|
8393
|
+
rate: "fps",
|
|
8394
|
+
mass: "kg",
|
|
8395
|
+
index: "none",
|
|
8396
|
+
ratio: "none",
|
|
8397
|
+
count: "none",
|
|
8398
|
+
none: "none",
|
|
8399
|
+
device: "device"
|
|
8400
|
+
};
|
|
8401
|
+
/** The stored unit, or `undefined` when the quantity has no unit symbol. */
|
|
8402
|
+
function baseUnitOf(quantity) {
|
|
8403
|
+
const symbol = QUANTITY_BASE[quantity];
|
|
8404
|
+
if (symbol === "none" || symbol === "device") return void 0;
|
|
8405
|
+
return symbol;
|
|
8406
|
+
}
|
|
8407
|
+
function readingMeta(quantity, extras) {
|
|
8408
|
+
return {
|
|
8409
|
+
quantity,
|
|
8410
|
+
...extras?.precision !== void 0 ? { precision: extras.precision } : {},
|
|
8411
|
+
...extras?.label !== void 0 ? { label: extras.label } : {}
|
|
8412
|
+
};
|
|
8413
|
+
}
|
|
8321
8414
|
/** Shared Zod schemas used across streaming capabilities. */
|
|
8322
8415
|
var CamProfileSchema = _enum([
|
|
8323
8416
|
"high",
|
|
@@ -8345,8 +8438,8 @@ var CamStreamKindSchema = _enum([
|
|
|
8345
8438
|
"derived"
|
|
8346
8439
|
]);
|
|
8347
8440
|
var CamStreamResolutionSchema = object({
|
|
8348
|
-
width: number().int().positive(),
|
|
8349
|
-
height: number().int().positive()
|
|
8441
|
+
width: number().int().positive().meta(readingMeta("none")),
|
|
8442
|
+
height: number().int().positive().meta(readingMeta("none"))
|
|
8350
8443
|
});
|
|
8351
8444
|
var CameraStreamSchema = object({
|
|
8352
8445
|
/** Stable, provider-assigned id unique within the (deviceId) scope. */
|
|
@@ -8993,14 +9086,6 @@ Object.freeze({
|
|
|
8993
9086
|
{
|
|
8994
9087
|
"path": "aqi",
|
|
8995
9088
|
"valueType": "number"
|
|
8996
|
-
},
|
|
8997
|
-
{
|
|
8998
|
-
"path": "unit",
|
|
8999
|
-
"valueType": "string"
|
|
9000
|
-
},
|
|
9001
|
-
{
|
|
9002
|
-
"path": "precision",
|
|
9003
|
-
"valueType": "number"
|
|
9004
9089
|
}
|
|
9005
9090
|
],
|
|
9006
9091
|
"alarm-panel": [
|
|
@@ -9036,20 +9121,10 @@ Object.freeze({
|
|
|
9036
9121
|
"valueType": "boolean"
|
|
9037
9122
|
}
|
|
9038
9123
|
],
|
|
9039
|
-
"ambient-light-sensor": [
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
},
|
|
9044
|
-
{
|
|
9045
|
-
"path": "unit",
|
|
9046
|
-
"valueType": "string"
|
|
9047
|
-
},
|
|
9048
|
-
{
|
|
9049
|
-
"path": "precision",
|
|
9050
|
-
"valueType": "number"
|
|
9051
|
-
}
|
|
9052
|
-
],
|
|
9124
|
+
"ambient-light-sensor": [{
|
|
9125
|
+
"path": "lux",
|
|
9126
|
+
"valueType": "number"
|
|
9127
|
+
}],
|
|
9053
9128
|
"audio-metrics": [
|
|
9054
9129
|
{
|
|
9055
9130
|
"path": "ts",
|
|
@@ -9129,6 +9204,10 @@ Object.freeze({
|
|
|
9129
9204
|
"path": "lastUpdated",
|
|
9130
9205
|
"valueType": "number"
|
|
9131
9206
|
},
|
|
9207
|
+
{
|
|
9208
|
+
"path": "volts",
|
|
9209
|
+
"valueType": "number"
|
|
9210
|
+
},
|
|
9132
9211
|
{
|
|
9133
9212
|
"path": "lastContactAt",
|
|
9134
9213
|
"valueType": "number"
|
|
@@ -9371,10 +9450,6 @@ Object.freeze({
|
|
|
9371
9450
|
"datetime"
|
|
9372
9451
|
]
|
|
9373
9452
|
},
|
|
9374
|
-
{
|
|
9375
|
-
"path": "unit",
|
|
9376
|
-
"valueType": "string"
|
|
9377
|
-
},
|
|
9378
9453
|
{
|
|
9379
9454
|
"path": "min",
|
|
9380
9455
|
"valueType": "number"
|
|
@@ -9386,19 +9461,6 @@ Object.freeze({
|
|
|
9386
9461
|
{
|
|
9387
9462
|
"path": "step",
|
|
9388
9463
|
"valueType": "number"
|
|
9389
|
-
},
|
|
9390
|
-
{
|
|
9391
|
-
"path": "precision",
|
|
9392
|
-
"valueType": "number"
|
|
9393
|
-
},
|
|
9394
|
-
{
|
|
9395
|
-
"path": "format",
|
|
9396
|
-
"valueType": "enum",
|
|
9397
|
-
"enumValues": [
|
|
9398
|
-
"date",
|
|
9399
|
-
"time",
|
|
9400
|
-
"datetime"
|
|
9401
|
-
]
|
|
9402
9464
|
}
|
|
9403
9465
|
],
|
|
9404
9466
|
"cover": [
|
|
@@ -9639,13 +9701,8 @@ Object.freeze({
|
|
|
9639
9701
|
"path": "value",
|
|
9640
9702
|
"valueType": "string"
|
|
9641
9703
|
}, {
|
|
9642
|
-
"path": "
|
|
9643
|
-
"valueType": "
|
|
9644
|
-
"enumValues": [
|
|
9645
|
-
"date",
|
|
9646
|
-
"time",
|
|
9647
|
-
"datetime"
|
|
9648
|
-
]
|
|
9704
|
+
"path": "timestamp",
|
|
9705
|
+
"valueType": "boolean"
|
|
9649
9706
|
}],
|
|
9650
9707
|
"event-emitter": [
|
|
9651
9708
|
{
|
|
@@ -9752,20 +9809,10 @@ Object.freeze({
|
|
|
9752
9809
|
"valueType": "number"
|
|
9753
9810
|
}
|
|
9754
9811
|
],
|
|
9755
|
-
"humidity-sensor": [
|
|
9756
|
-
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
},
|
|
9760
|
-
{
|
|
9761
|
-
"path": "unit",
|
|
9762
|
-
"valueType": "string"
|
|
9763
|
-
},
|
|
9764
|
-
{
|
|
9765
|
-
"path": "precision",
|
|
9766
|
-
"valueType": "number"
|
|
9767
|
-
}
|
|
9768
|
-
],
|
|
9812
|
+
"humidity-sensor": [{
|
|
9813
|
+
"path": "percent",
|
|
9814
|
+
"valueType": "number"
|
|
9815
|
+
}],
|
|
9769
9816
|
"image": [{
|
|
9770
9817
|
"path": "url",
|
|
9771
9818
|
"valueType": "string"
|
|
@@ -10104,20 +10151,10 @@ Object.freeze({
|
|
|
10104
10151
|
"valueType": "boolean"
|
|
10105
10152
|
}
|
|
10106
10153
|
],
|
|
10107
|
-
"numeric-sensor": [
|
|
10108
|
-
|
|
10109
|
-
|
|
10110
|
-
|
|
10111
|
-
},
|
|
10112
|
-
{
|
|
10113
|
-
"path": "unit",
|
|
10114
|
-
"valueType": "string"
|
|
10115
|
-
},
|
|
10116
|
-
{
|
|
10117
|
-
"path": "precision",
|
|
10118
|
-
"valueType": "number"
|
|
10119
|
-
}
|
|
10120
|
-
],
|
|
10154
|
+
"numeric-sensor": [{
|
|
10155
|
+
"path": "value",
|
|
10156
|
+
"valueType": "number"
|
|
10157
|
+
}],
|
|
10121
10158
|
"pet-feeder": [
|
|
10122
10159
|
{
|
|
10123
10160
|
"path": "foodLevel",
|
|
@@ -10209,14 +10246,6 @@ Object.freeze({
|
|
|
10209
10246
|
{
|
|
10210
10247
|
"path": "powerFactor",
|
|
10211
10248
|
"valueType": "number"
|
|
10212
|
-
},
|
|
10213
|
-
{
|
|
10214
|
-
"path": "unit",
|
|
10215
|
-
"valueType": "string"
|
|
10216
|
-
},
|
|
10217
|
-
{
|
|
10218
|
-
"path": "precision",
|
|
10219
|
-
"valueType": "number"
|
|
10220
10249
|
}
|
|
10221
10250
|
],
|
|
10222
10251
|
"presence": [
|
|
@@ -10245,20 +10274,10 @@ Object.freeze({
|
|
|
10245
10274
|
"valueType": "number"
|
|
10246
10275
|
}
|
|
10247
10276
|
],
|
|
10248
|
-
"pressure-sensor": [
|
|
10249
|
-
|
|
10250
|
-
|
|
10251
|
-
|
|
10252
|
-
},
|
|
10253
|
-
{
|
|
10254
|
-
"path": "unit",
|
|
10255
|
-
"valueType": "string"
|
|
10256
|
-
},
|
|
10257
|
-
{
|
|
10258
|
-
"path": "precision",
|
|
10259
|
-
"valueType": "number"
|
|
10260
|
-
}
|
|
10261
|
-
],
|
|
10277
|
+
"pressure-sensor": [{
|
|
10278
|
+
"path": "hpa",
|
|
10279
|
+
"valueType": "number"
|
|
10280
|
+
}],
|
|
10262
10281
|
"privacy-mask": [{
|
|
10263
10282
|
"path": "enabled",
|
|
10264
10283
|
"valueType": "boolean"
|
|
@@ -10569,20 +10588,10 @@ Object.freeze({
|
|
|
10569
10588
|
"path": "tampered",
|
|
10570
10589
|
"valueType": "boolean"
|
|
10571
10590
|
}],
|
|
10572
|
-
"temperature-sensor": [
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
|
|
10576
|
-
},
|
|
10577
|
-
{
|
|
10578
|
-
"path": "unit",
|
|
10579
|
-
"valueType": "string"
|
|
10580
|
-
},
|
|
10581
|
-
{
|
|
10582
|
-
"path": "precision",
|
|
10583
|
-
"valueType": "number"
|
|
10584
|
-
}
|
|
10585
|
-
],
|
|
10591
|
+
"temperature-sensor": [{
|
|
10592
|
+
"path": "celsius",
|
|
10593
|
+
"valueType": "number"
|
|
10594
|
+
}],
|
|
10586
10595
|
"update": [
|
|
10587
10596
|
{
|
|
10588
10597
|
"path": "currentVersion",
|
|
@@ -10693,10 +10702,6 @@ Object.freeze({
|
|
|
10693
10702
|
"path": "temperature",
|
|
10694
10703
|
"valueType": "number"
|
|
10695
10704
|
},
|
|
10696
|
-
{
|
|
10697
|
-
"path": "temperatureUnit",
|
|
10698
|
-
"valueType": "string"
|
|
10699
|
-
},
|
|
10700
10705
|
{
|
|
10701
10706
|
"path": "humidity",
|
|
10702
10707
|
"valueType": "number"
|
|
@@ -10705,18 +10710,10 @@ Object.freeze({
|
|
|
10705
10710
|
"path": "pressure",
|
|
10706
10711
|
"valueType": "number"
|
|
10707
10712
|
},
|
|
10708
|
-
{
|
|
10709
|
-
"path": "pressureUnit",
|
|
10710
|
-
"valueType": "string"
|
|
10711
|
-
},
|
|
10712
10713
|
{
|
|
10713
10714
|
"path": "windSpeed",
|
|
10714
10715
|
"valueType": "number"
|
|
10715
10716
|
},
|
|
10716
|
-
{
|
|
10717
|
-
"path": "windSpeedUnit",
|
|
10718
|
-
"valueType": "string"
|
|
10719
|
-
},
|
|
10720
10717
|
{
|
|
10721
10718
|
"path": "windBearing",
|
|
10722
10719
|
"valueType": "number"
|
|
@@ -18229,7 +18226,8 @@ var CompositionProblemSchema = object({
|
|
|
18229
18226
|
"item-field-reserved",
|
|
18230
18227
|
"target-missing",
|
|
18231
18228
|
"target-unreadable",
|
|
18232
|
-
"not-replaceable"
|
|
18229
|
+
"not-replaceable",
|
|
18230
|
+
"native-capability-replace"
|
|
18233
18231
|
]),
|
|
18234
18232
|
path: string(),
|
|
18235
18233
|
message: string()
|
|
@@ -18287,9 +18285,11 @@ var CompositionFieldPlanSchema = object({
|
|
|
18287
18285
|
reads: array(CompositionFieldReadSchema)
|
|
18288
18286
|
});
|
|
18289
18287
|
/**
|
|
18290
|
-
* What a feature does to its target (D663): `new` builds a capability on a
|
|
18291
|
-
* device; on an existing device it `add`s a capability the device lacks
|
|
18292
|
-
*
|
|
18288
|
+
* What a feature does to its target (D663, D944): `new` builds a capability on a
|
|
18289
|
+
* new device; on an existing device it `add`s a capability the device lacks.
|
|
18290
|
+
* Replacing a capability the device already serves is refused
|
|
18291
|
+
* (`native-capability-replace`), not planned. `replace` remains on the claim
|
|
18292
|
+
* wire for a claim the hub already holds.
|
|
18293
18293
|
*/
|
|
18294
18294
|
var CompositionFeatureModeSchema = _enum([
|
|
18295
18295
|
"new",
|
|
@@ -20288,12 +20288,12 @@ function buildDeviceAdminUrl(parts) {
|
|
|
20288
20288
|
* providers detect renames by matching `uniqueId` and patch `id`
|
|
20289
20289
|
* in-place so dispatch keeps working.
|
|
20290
20290
|
*
|
|
20291
|
-
* **
|
|
20292
|
-
*
|
|
20293
|
-
*
|
|
20294
|
-
*
|
|
20295
|
-
* (`ha-domain-mapping`)
|
|
20296
|
-
* stored after that point.
|
|
20291
|
+
* **Display unit and precision are NOT stored here.** They are a read-time
|
|
20292
|
+
* preference (D944). A polymorphic reading stores its `quantity` here once,
|
|
20293
|
+
* at adopt, and a date/time value stores `dateTimeShape` the same way —
|
|
20294
|
+
* neither is repeated on each sample. The `device_class` tag is an
|
|
20295
|
+
* HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
|
|
20296
|
+
* to derive the CamStack `DeviceRole`; it is never stored after that point.
|
|
20297
20297
|
*
|
|
20298
20298
|
* The `raw` escape hatch carries provider-specific attributes that don't
|
|
20299
20299
|
* fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
|
|
@@ -20307,6 +20307,14 @@ var SourceInfoSchema = object({
|
|
|
20307
20307
|
/** Immutable upstream identifier when available (HA `unique_id`, … ).
|
|
20308
20308
|
* Used to detect rename when `id` changes. */
|
|
20309
20309
|
uniqueId: string().optional(),
|
|
20310
|
+
/** Quantity of a polymorphic reading, written once at adopt (D944). */
|
|
20311
|
+
quantity: ReadingQuantitySchema.optional(),
|
|
20312
|
+
/** Date / time / datetime shape of a string reading, written once at adopt. */
|
|
20313
|
+
dateTimeShape: _enum([
|
|
20314
|
+
"date",
|
|
20315
|
+
"time",
|
|
20316
|
+
"datetime"
|
|
20317
|
+
]).optional(),
|
|
20310
20318
|
/** Provider-specific extras that don't fit the structured slots.
|
|
20311
20319
|
* Whitelist what goes here per provider — do NOT dump entire HA
|
|
20312
20320
|
* attribute blobs (would explode the meta JSON column). */
|
|
@@ -20352,6 +20360,80 @@ function mergeSourceInfo(prev, patch) {
|
|
|
20352
20360
|
return next;
|
|
20353
20361
|
}
|
|
20354
20362
|
/**
|
|
20363
|
+
* Coerce common unit-of-measurement string variants to a canonical form
|
|
20364
|
+
* so the UI's display templates can rely on stable strings. Pure
|
|
20365
|
+
* passthrough for unknown units — kept conservative on purpose, the goal
|
|
20366
|
+
* is to normalise the long tail of HA `unit_of_measurement` values that
|
|
20367
|
+
* have multiple historical spellings (`'°C'` vs `'C'` vs `'celsius'`).
|
|
20368
|
+
*
|
|
20369
|
+
* Extend the map as we encounter real-world data. Returning the raw
|
|
20370
|
+
* input for unknown values is intentional: providers should never
|
|
20371
|
+
* lose information silently here.
|
|
20372
|
+
*/
|
|
20373
|
+
function normalizeUnit(raw) {
|
|
20374
|
+
if (raw == null) return void 0;
|
|
20375
|
+
const trimmed = raw.trim();
|
|
20376
|
+
if (trimmed === "") return void 0;
|
|
20377
|
+
const lower = trimmed.toLowerCase();
|
|
20378
|
+
if (lower === "°c" || lower === "c" || lower === "celsius") return "°C";
|
|
20379
|
+
if (lower === "°f" || lower === "f" || lower === "fahrenheit") return "°F";
|
|
20380
|
+
if (lower === "k" || lower === "kelvin") return "K";
|
|
20381
|
+
if (lower === "%" || lower === "percent" || lower === "percentage") return "%";
|
|
20382
|
+
if (lower === "lx" || lower === "lux") return "lx";
|
|
20383
|
+
if (lower === "hpa" || lower === "hectopascal") return "hPa";
|
|
20384
|
+
if (lower === "kpa") return "kPa";
|
|
20385
|
+
if (lower === "pa") return "Pa";
|
|
20386
|
+
if (lower === "mbar" || lower === "millibar") return "mbar";
|
|
20387
|
+
if (lower === "w" || lower === "watt") return "W";
|
|
20388
|
+
if (lower === "kw" || lower === "kilowatt") return "kW";
|
|
20389
|
+
if (lower === "wh" || lower === "watt-hour") return "Wh";
|
|
20390
|
+
if (lower === "kwh" || lower === "kilowatt-hour") return "kWh";
|
|
20391
|
+
if (lower === "v" || lower === "volt") return "V";
|
|
20392
|
+
if (lower === "a" || lower === "amp" || lower === "ampere") return "A";
|
|
20393
|
+
if (lower === "ma" || lower === "milliampere") return "mA";
|
|
20394
|
+
if (lower === "va") return "VA";
|
|
20395
|
+
if (lower === "ppm") return "ppm";
|
|
20396
|
+
if (lower === "ppb") return "ppb";
|
|
20397
|
+
if (lower === "µg/m³" || lower === "ug/m3" || lower === "µg/m3") return "µg/m³";
|
|
20398
|
+
if (lower === "km/h" || lower === "kph") return "km/h";
|
|
20399
|
+
if (lower === "mph") return "mph";
|
|
20400
|
+
if (lower === "m/s") return "m/s";
|
|
20401
|
+
if (lower === "m") return "m";
|
|
20402
|
+
if (lower === "m²" || lower === "m2") return "m²";
|
|
20403
|
+
if (lower === "mm") return "mm";
|
|
20404
|
+
if (lower === "cm") return "cm";
|
|
20405
|
+
if (lower === "km") return "km";
|
|
20406
|
+
if (lower === "in" || lower === "inch" || lower === "inches") return "in";
|
|
20407
|
+
if (lower === "ft" || lower === "foot" || lower === "feet") return "ft";
|
|
20408
|
+
if (lower === "kg") return "kg";
|
|
20409
|
+
if (lower === "g") return "g";
|
|
20410
|
+
if (lower === "l" || lower === "liter" || lower === "litre" || lower === "liters" || lower === "litres") return "L";
|
|
20411
|
+
if (lower === "ml") return "mL";
|
|
20412
|
+
if (lower === "gal" || lower === "gallon" || lower === "gallons") return "gal";
|
|
20413
|
+
if (lower === "s" || lower === "sec" || lower === "second" || lower === "seconds") return "s";
|
|
20414
|
+
if (lower === "ms") return "ms";
|
|
20415
|
+
if (lower === "min" || lower === "minute" || lower === "minutes") return "min";
|
|
20416
|
+
if (lower === "h" || lower === "hr" || lower === "hour" || lower === "hours") return "h";
|
|
20417
|
+
if (lower === "d" || lower === "day" || lower === "days") return "d";
|
|
20418
|
+
if (lower === "b" || lower === "byte" || lower === "bytes") return "B";
|
|
20419
|
+
if (lower === "kib") return "KiB";
|
|
20420
|
+
if (lower === "mib") return "MiB";
|
|
20421
|
+
if (lower === "gib") return "GiB";
|
|
20422
|
+
if (lower === "tib") return "TiB";
|
|
20423
|
+
if (lower === "kb") return "kB";
|
|
20424
|
+
if (lower === "mb") return "MB";
|
|
20425
|
+
if (lower === "gb") return "GB";
|
|
20426
|
+
if (lower === "dbm") return "dBm";
|
|
20427
|
+
if (lower === "dbfs") return "dBFS";
|
|
20428
|
+
if (lower === "inhg") return "inHg";
|
|
20429
|
+
if (lower === "mm/h") return "mm/h";
|
|
20430
|
+
if (lower === "w/m²" || lower === "w/m2") return "W/m²";
|
|
20431
|
+
if (lower === "°" || lower === "deg" || lower === "degree" || lower === "degrees") return "°";
|
|
20432
|
+
if (lower === "kbps") return "kbps";
|
|
20433
|
+
if (lower === "fps") return "fps";
|
|
20434
|
+
return trimmed;
|
|
20435
|
+
}
|
|
20436
|
+
/**
|
|
20355
20437
|
* Key under which `SourceInfo` lives inside `DeviceMeta.metadata`. The
|
|
20356
20438
|
* blob is a free-form `Record<string, unknown>` shared with hardware-
|
|
20357
20439
|
* identity fields (`manufacturer`, `model`, `firmware`, …); SourceInfo
|
|
@@ -41428,7 +41510,8 @@ method(object({
|
|
|
41428
41510
|
"enum"
|
|
41429
41511
|
]),
|
|
41430
41512
|
enumValues: array(string()).optional(),
|
|
41431
|
-
item: boolean().optional()
|
|
41513
|
+
item: boolean().optional(),
|
|
41514
|
+
quantity: ReadingQuantitySchema.optional()
|
|
41432
41515
|
})).readonly(),
|
|
41433
41516
|
itemArray: object({
|
|
41434
41517
|
path: string(),
|
|
@@ -45272,8 +45355,8 @@ var MaskPolygonShapeSchema = object({
|
|
|
45272
45355
|
/** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
|
|
45273
45356
|
var MaskGridShapeSchema = object({
|
|
45274
45357
|
kind: literal("grid"),
|
|
45275
|
-
gridWidth: number(),
|
|
45276
|
-
gridHeight: number(),
|
|
45358
|
+
gridWidth: number().meta(readingMeta("none")),
|
|
45359
|
+
gridHeight: number().meta(readingMeta("none")),
|
|
45277
45360
|
cells: array(boolean())
|
|
45278
45361
|
});
|
|
45279
45362
|
discriminatedUnion("kind", [
|
|
@@ -56753,27 +56836,17 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
56753
56836
|
*/
|
|
56754
56837
|
var AirQualitySensorStatusSchema = object({
|
|
56755
56838
|
/** Carbon dioxide concentration in ppm. */
|
|
56756
|
-
co2Ppm: number().min(0).optional(),
|
|
56839
|
+
co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
56757
56840
|
/** Total volatile organic compounds in ppb. */
|
|
56758
|
-
vocPpb: number().min(0).optional(),
|
|
56841
|
+
vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
56759
56842
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
56760
|
-
pm25: number().min(0).optional(),
|
|
56843
|
+
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56761
56844
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
56762
|
-
pm10: number().min(0).optional(),
|
|
56845
|
+
pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56763
56846
|
/** Composite AQI value (typically 0..500). */
|
|
56764
|
-
aqi: number().optional(),
|
|
56847
|
+
aqi: number().optional().meta(readingMeta("index")),
|
|
56765
56848
|
/** Ms epoch when the slice was last updated. */
|
|
56766
|
-
lastFetchedAt: number()
|
|
56767
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
56768
|
-
* `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
|
|
56769
|
-
* upstream `sensor.*` entity surfaces ONE device_class, so one unit
|
|
56770
|
-
* per slice is unambiguous. */
|
|
56771
|
-
unit: string().optional(),
|
|
56772
|
-
/** Suggested decimal places for numeric display.
|
|
56773
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
56774
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
56775
|
-
* auto-formatting when absent. */
|
|
56776
|
-
precision: number().int().min(0).max(10).optional()
|
|
56849
|
+
lastFetchedAt: number()
|
|
56777
56850
|
});
|
|
56778
56851
|
var airQualitySensorCapability = {
|
|
56779
56852
|
name: "air-quality-sensor",
|
|
@@ -56810,12 +56883,12 @@ var airQualitySensorCapability = {
|
|
|
56810
56883
|
},
|
|
56811
56884
|
pm25: {
|
|
56812
56885
|
kind: "gauge",
|
|
56813
|
-
unit: "
|
|
56886
|
+
unit: "µg/m³",
|
|
56814
56887
|
deadband: 1
|
|
56815
56888
|
},
|
|
56816
56889
|
pm10: {
|
|
56817
56890
|
kind: "gauge",
|
|
56818
|
-
unit: "
|
|
56891
|
+
unit: "µg/m³",
|
|
56819
56892
|
deadband: 1
|
|
56820
56893
|
},
|
|
56821
56894
|
aqi: {
|
|
@@ -56833,22 +56906,13 @@ var airQualitySensorCapability = {
|
|
|
56833
56906
|
* entries with `device_class: illuminance`.
|
|
56834
56907
|
*/
|
|
56835
56908
|
var AmbientLightSensorStatusSchema = object({
|
|
56836
|
-
/** Current illuminance in lux (lx). */
|
|
56837
|
-
lux: number().min(0).meta({
|
|
56838
|
-
|
|
56909
|
+
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
56910
|
+
lux: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
56911
|
+
precision: 0,
|
|
56839
56912
|
label: "Illuminance"
|
|
56840
|
-
}),
|
|
56913
|
+
})),
|
|
56841
56914
|
/** Ms epoch when the slice was last updated. */
|
|
56842
|
-
lastFetchedAt: number()
|
|
56843
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
56844
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
56845
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
56846
|
-
unit: string().optional(),
|
|
56847
|
-
/** Suggested decimal places for numeric display.
|
|
56848
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
56849
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
56850
|
-
* auto-formatting when absent. */
|
|
56851
|
-
precision: number().int().min(0).max(10).optional()
|
|
56915
|
+
lastFetchedAt: number()
|
|
56852
56916
|
});
|
|
56853
56917
|
var ambientLightSensorCapability = {
|
|
56854
56918
|
name: "ambient-light-sensor",
|
|
@@ -56880,12 +56944,7 @@ var ambientLightSensorCapability = {
|
|
|
56880
56944
|
} } },
|
|
56881
56945
|
/** Clock fields: written, but excluded from the compare that decides
|
|
56882
56946
|
* whether persisting is worth a SQLite commit. */
|
|
56883
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
56884
|
-
customize: {
|
|
56885
|
-
version: 1,
|
|
56886
|
-
advanced: "derive",
|
|
56887
|
-
targets: []
|
|
56888
|
-
}
|
|
56947
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
56889
56948
|
};
|
|
56890
56949
|
/**
|
|
56891
56950
|
* Per-class audio metrics aggregated over a sliding window.
|
|
@@ -57112,10 +57171,7 @@ var BatteryStatusSchema = object({
|
|
|
57112
57171
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
57113
57172
|
* declines to band a non-finite reading, which is the correct shape.
|
|
57114
57173
|
*/
|
|
57115
|
-
percentage: number().min(0).max(100).nullable().meta({
|
|
57116
|
-
unit: "%",
|
|
57117
|
-
label: "Charge"
|
|
57118
|
-
}),
|
|
57174
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
57119
57175
|
/**
|
|
57120
57176
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
57121
57177
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -57142,7 +57198,14 @@ var BatteryStatusSchema = object({
|
|
|
57142
57198
|
*/
|
|
57143
57199
|
sleeping: boolean().meta({ label: "Sleeping" }),
|
|
57144
57200
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
57145
|
-
lastUpdated: number(),
|
|
57201
|
+
lastUpdated: number().meta(readingMeta("none")),
|
|
57202
|
+
/**
|
|
57203
|
+
* Battery voltage in volts, for a cell the firmware reports as voltage
|
|
57204
|
+
* rather than a percentage (Ecowitt capacitor packs). Absent on cameras
|
|
57205
|
+
* that only report charge. A second reading on this cap, not a second
|
|
57206
|
+
* `numeric-sensor` on the same device.
|
|
57207
|
+
*/
|
|
57208
|
+
volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
57146
57209
|
/**
|
|
57147
57210
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
57148
57211
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -57194,7 +57257,6 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
57194
57257
|
/** Generic Customize target for the percentage reading (D936). */
|
|
57195
57258
|
var batteryCustomize = {
|
|
57196
57259
|
version: 1,
|
|
57197
|
-
advanced: "derive",
|
|
57198
57260
|
targets: [{
|
|
57199
57261
|
id: "percentage",
|
|
57200
57262
|
cardinality: "scalar",
|
|
@@ -57203,7 +57265,7 @@ var batteryCustomize = {
|
|
|
57203
57265
|
stateField: "percentage"
|
|
57204
57266
|
},
|
|
57205
57267
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
57206
|
-
apply: "add
|
|
57268
|
+
apply: "add",
|
|
57207
57269
|
path: "percentage",
|
|
57208
57270
|
sources: [{
|
|
57209
57271
|
kind: "field",
|
|
@@ -57324,10 +57386,16 @@ onStatusChanged: { data: object({
|
|
|
57324
57386
|
/**
|
|
57325
57387
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
57326
57388
|
*/
|
|
57327
|
-
history: { fields: {
|
|
57328
|
-
|
|
57329
|
-
|
|
57330
|
-
|
|
57389
|
+
history: { fields: {
|
|
57390
|
+
percentage: {
|
|
57391
|
+
kind: "gauge",
|
|
57392
|
+
unit: "%"
|
|
57393
|
+
},
|
|
57394
|
+
volts: {
|
|
57395
|
+
kind: "gauge",
|
|
57396
|
+
unit: "V"
|
|
57397
|
+
}
|
|
57398
|
+
} },
|
|
57331
57399
|
customize: batteryCustomize,
|
|
57332
57400
|
/** Clock fields: written, but excluded from the compare that decides
|
|
57333
57401
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -57374,12 +57442,7 @@ var binaryCapability = {
|
|
|
57374
57442
|
history: { fields: { on: { kind: "boolean" } } },
|
|
57375
57443
|
/** Clock fields: written, but excluded from the compare that decides
|
|
57376
57444
|
* whether persisting is worth a SQLite commit. */
|
|
57377
|
-
volatileStateFields: ["lastChangedAt"]
|
|
57378
|
-
customize: {
|
|
57379
|
-
version: 1,
|
|
57380
|
-
advanced: "derive",
|
|
57381
|
-
targets: []
|
|
57382
|
-
}
|
|
57445
|
+
volatileStateFields: ["lastChangedAt"]
|
|
57383
57446
|
};
|
|
57384
57447
|
/**
|
|
57385
57448
|
* Dimmable-light brightness control. Co-exists with `switch` on the
|
|
@@ -57395,7 +57458,7 @@ var binaryCapability = {
|
|
|
57395
57458
|
*/
|
|
57396
57459
|
var BrightnessStatusSchema = object({
|
|
57397
57460
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
57398
|
-
percentage: number().min(0).max(100),
|
|
57461
|
+
percentage: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
57399
57462
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
57400
57463
|
lastChangedAt: number()
|
|
57401
57464
|
});
|
|
@@ -57849,21 +57912,21 @@ var ClimateControlStatusSchema = object({
|
|
|
57849
57912
|
/** Single setpoint in Celsius. Used by single-target modes
|
|
57850
57913
|
* (heat / cool / dry). Null when the device is in a range mode
|
|
57851
57914
|
* (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
|
|
57852
|
-
target: number().nullable(),
|
|
57915
|
+
target: number().nullable().meta(readingMeta("temperature")),
|
|
57853
57916
|
/** Upper bound of the dual setpoint in Celsius. Populated only
|
|
57854
57917
|
* in `heat_cool` mode. */
|
|
57855
|
-
targetHigh: number().nullable(),
|
|
57918
|
+
targetHigh: number().nullable().meta(readingMeta("temperature")),
|
|
57856
57919
|
/** Lower bound of the dual setpoint in Celsius. Populated only
|
|
57857
57920
|
* in `heat_cool` mode. */
|
|
57858
|
-
targetLow: number().nullable(),
|
|
57921
|
+
targetLow: number().nullable().meta(readingMeta("temperature")),
|
|
57859
57922
|
/** Target relative humidity (0..100). Null when the device has no
|
|
57860
57923
|
* humidity control. */
|
|
57861
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
57924
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
57862
57925
|
/** Current measured temperature in Celsius. Read-only — pushed by
|
|
57863
57926
|
* the device's internal sensor. */
|
|
57864
|
-
currentTemp: number().nullable(),
|
|
57927
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
57865
57928
|
/** Current measured relative humidity (0..100). Read-only. */
|
|
57866
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
57929
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
57867
57930
|
/** Vertical louver swing on/off. Null when the device has no vertical
|
|
57868
57931
|
* swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
|
|
57869
57932
|
swingVertical: boolean().nullable(),
|
|
@@ -57871,11 +57934,11 @@ var ClimateControlStatusSchema = object({
|
|
|
57871
57934
|
* horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
|
|
57872
57935
|
swingHorizontal: boolean().nullable(),
|
|
57873
57936
|
/** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
57874
|
-
minTemp: number().nullable().optional(),
|
|
57937
|
+
minTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
57875
57938
|
/** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
57876
|
-
maxTemp: number().nullable().optional(),
|
|
57939
|
+
maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
57877
57940
|
/** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
|
|
57878
|
-
tempStep: number().positive().nullable().optional(),
|
|
57941
|
+
tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
|
|
57879
57942
|
/** Ms epoch when the slice was last updated (push or command). */
|
|
57880
57943
|
lastFetchedAt: number()
|
|
57881
57944
|
});
|
|
@@ -58017,17 +58080,17 @@ var climateControlCapability = {
|
|
|
58017
58080
|
* modes to expose in the picker without a slice fetch.
|
|
58018
58081
|
*/
|
|
58019
58082
|
var RgbTripletSchema = object({
|
|
58020
|
-
r: number().int().min(0).max(255),
|
|
58021
|
-
g: number().int().min(0).max(255),
|
|
58022
|
-
b: number().int().min(0).max(255)
|
|
58083
|
+
r: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
58084
|
+
g: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
58085
|
+
b: number().int().min(0).max(255).meta(readingMeta("none"))
|
|
58023
58086
|
});
|
|
58024
58087
|
var HsvTripletSchema = object({
|
|
58025
58088
|
/** Hue in degrees, 0..360. */
|
|
58026
|
-
h: number().min(0).max(360),
|
|
58089
|
+
h: number().min(0).max(360).meta(readingMeta("none")),
|
|
58027
58090
|
/** Saturation as 0..100 inclusive. */
|
|
58028
|
-
s: number().min(0).max(100),
|
|
58091
|
+
s: number().min(0).max(100).meta(readingMeta("none")),
|
|
58029
58092
|
/** Value/brightness as 0..100 inclusive. */
|
|
58030
|
-
v: number().min(0).max(100)
|
|
58093
|
+
v: number().min(0).max(100).meta(readingMeta("none"))
|
|
58031
58094
|
});
|
|
58032
58095
|
var ColorInputSchema = discriminatedUnion("mode", [
|
|
58033
58096
|
object({
|
|
@@ -58056,7 +58119,7 @@ var ColorStatusSchema = object({
|
|
|
58056
58119
|
/** Populated when `mode === 'hsv'`. */
|
|
58057
58120
|
hsv: HsvTripletSchema.optional(),
|
|
58058
58121
|
/** Populated when `mode === 'mired'`. */
|
|
58059
|
-
mireds: number().int().optional(),
|
|
58122
|
+
mireds: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
58060
58123
|
/** Ms epoch of the last operator-driven change. */
|
|
58061
58124
|
lastChangedAt: number()
|
|
58062
58125
|
});
|
|
@@ -58251,18 +58314,12 @@ _enum([
|
|
|
58251
58314
|
"empty",
|
|
58252
58315
|
"full"
|
|
58253
58316
|
]);
|
|
58254
|
-
/**
|
|
58255
|
-
|
|
58256
|
-
|
|
58257
|
-
"days",
|
|
58258
|
-
"hours",
|
|
58259
|
-
"cycles"
|
|
58260
|
-
]);
|
|
58261
|
-
/** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
|
|
58262
|
-
* days). `value` null = unknown (D393). */
|
|
58317
|
+
/** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
|
|
58318
|
+
var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
|
|
58319
|
+
/** Remaining life. `value` null = unknown (D393). Duration is seconds. */
|
|
58263
58320
|
var ConsumableRemainingSchema = object({
|
|
58264
|
-
value: number().min(0).nullable(),
|
|
58265
|
-
|
|
58321
|
+
value: number().min(0).nullable().meta(readingMeta("device")),
|
|
58322
|
+
kind: ConsumableRemainingKindSchema
|
|
58266
58323
|
});
|
|
58267
58324
|
/** A single consumable item. Either a continuous `level` (remaining
|
|
58268
58325
|
* life %, or fill % for a `supply` / `waste` item) or a discrete `status`
|
|
@@ -58275,7 +58332,7 @@ var ConsumableItemSchema = object({
|
|
|
58275
58332
|
/** Display name. */
|
|
58276
58333
|
label: string().min(1),
|
|
58277
58334
|
/** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
|
|
58278
|
-
level: number().min(0).max(100).nullable(),
|
|
58335
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58279
58336
|
/** The provider's discrete verdict when known (binary mode). */
|
|
58280
58337
|
status: ConsumableStatusSchema.nullable(),
|
|
58281
58338
|
/** What the item is — decides whether low or full is bad. Absent = `wear`. */
|
|
@@ -58357,8 +58414,8 @@ var consumablesCustomize = {
|
|
|
58357
58414
|
transforms: [{ kind: "identity" }]
|
|
58358
58415
|
}],
|
|
58359
58416
|
literals: [{
|
|
58360
|
-
path: "remaining.
|
|
58361
|
-
values: [...
|
|
58417
|
+
path: "remaining.kind",
|
|
58418
|
+
values: [...ConsumableRemainingKindSchema.options]
|
|
58362
58419
|
}]
|
|
58363
58420
|
},
|
|
58364
58421
|
{
|
|
@@ -58487,7 +58544,6 @@ reset: method(object({
|
|
|
58487
58544
|
},
|
|
58488
58545
|
"remaining.value": {
|
|
58489
58546
|
kind: "gauge",
|
|
58490
|
-
unitFrom: "remaining.unit",
|
|
58491
58547
|
deadband: 1,
|
|
58492
58548
|
displayName: "Remaining"
|
|
58493
58549
|
}
|
|
@@ -58557,10 +58613,9 @@ var contactCapability = {
|
|
|
58557
58613
|
* (empty array for the other kinds). It lives in the slice because
|
|
58558
58614
|
* HA `select.*` entities may update their options at runtime.
|
|
58559
58615
|
*
|
|
58560
|
-
*
|
|
58561
|
-
*
|
|
58562
|
-
*
|
|
58563
|
-
* push — consistent with `options` and the sensor-unit approach.
|
|
58616
|
+
* Numeric `min` / `max` / `step` live in the slice and share the device
|
|
58617
|
+
* quantity (`SourceInfo.quantity`, D944). Display precision is a read-time
|
|
58618
|
+
* preference. A datetime control's shape is `SourceInfo.dateTimeShape`.
|
|
58564
58619
|
*/
|
|
58565
58620
|
var ControlStatusSchema = object({
|
|
58566
58621
|
kind: _enum([
|
|
@@ -58572,39 +58627,17 @@ var ControlStatusSchema = object({
|
|
|
58572
58627
|
/** Current value. Numeric kind → number; select/text/datetime → string.
|
|
58573
58628
|
* Datetime values are ISO 8601 strings (full timestamp, date-only,
|
|
58574
58629
|
* or time-only — HA accepts all three forms). */
|
|
58575
|
-
value: union([number(), string()]),
|
|
58630
|
+
value: union([number().meta(readingMeta("device")), string()]),
|
|
58576
58631
|
/** Acceptable values when `kind === 'select'`. Empty for other kinds. */
|
|
58577
58632
|
options: array(string()),
|
|
58578
58633
|
/** Ms epoch when the slice was last updated. */
|
|
58579
58634
|
lastChangedAt: number(),
|
|
58580
|
-
/**
|
|
58581
|
-
|
|
58582
|
-
|
|
58583
|
-
|
|
58584
|
-
/**
|
|
58585
|
-
|
|
58586
|
-
min: number().optional(),
|
|
58587
|
-
/** Maximum allowed value. Populated live from HA `attributes.max`.
|
|
58588
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58589
|
-
max: number().optional(),
|
|
58590
|
-
/** Step increment. Populated live from HA `attributes.step`.
|
|
58591
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58592
|
-
step: number().optional(),
|
|
58593
|
-
/** Suggested decimal places for numeric display. Populated live from HA
|
|
58594
|
-
* `attributes.suggested_display_precision`, or derived from `step` when the
|
|
58595
|
-
* HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
|
|
58596
|
-
* other kinds. Falls back to auto-formatting when absent. */
|
|
58597
|
-
precision: number().int().min(0).max(10).optional(),
|
|
58598
|
-
/** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
|
|
58599
|
-
* 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
|
|
58600
|
-
* (UI defaults to 'datetime'). Derived live from the device's HA domain
|
|
58601
|
-
* (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
|
|
58602
|
-
* `has_date`/`has_time` attributes on each push. */
|
|
58603
|
-
format: _enum([
|
|
58604
|
-
"date",
|
|
58605
|
-
"time",
|
|
58606
|
-
"datetime"
|
|
58607
|
-
]).optional()
|
|
58635
|
+
/** Minimum allowed value, in the device quantity's base unit. */
|
|
58636
|
+
min: number().optional().meta(readingMeta("device")),
|
|
58637
|
+
/** Maximum allowed value, in the device quantity's base unit. */
|
|
58638
|
+
max: number().optional().meta(readingMeta("device")),
|
|
58639
|
+
/** Step increment, in the device quantity's base unit. */
|
|
58640
|
+
step: number().optional().meta(readingMeta("device"))
|
|
58608
58641
|
});
|
|
58609
58642
|
var ControlSetValueInputSchema = discriminatedUnion("kind", [
|
|
58610
58643
|
object({
|
|
@@ -58670,9 +58703,9 @@ var CoverStatusSchema = object({
|
|
|
58670
58703
|
]),
|
|
58671
58704
|
/** 0 = fully closed, 100 = fully open. Null when the device has
|
|
58672
58705
|
* no intermediate position surface. */
|
|
58673
|
-
position: number().min(0).max(100).nullable(),
|
|
58706
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58674
58707
|
/** 0 = closed slats, 100 = open slats. Null when no tilt support. */
|
|
58675
|
-
tiltPosition: number().min(0).max(100).nullable(),
|
|
58708
|
+
tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58676
58709
|
/** Ms epoch when the slice was last updated. */
|
|
58677
58710
|
lastChangedAt: number()
|
|
58678
58711
|
});
|
|
@@ -58775,9 +58808,9 @@ var NormalizedRangeSchema$1 = object({
|
|
|
58775
58808
|
var DayNightStatusSchema = object({
|
|
58776
58809
|
mode: DayNightModeSchema,
|
|
58777
58810
|
/** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
|
|
58778
|
-
sensitivity: number().optional(),
|
|
58811
|
+
sensitivity: number().optional().meta(readingMeta("none")),
|
|
58779
58812
|
/** Delay before the IR-cut filter flips, in seconds. */
|
|
58780
|
-
switchDelaySec: number().optional(),
|
|
58813
|
+
switchDelaySec: number().optional().meta(readingMeta("duration")),
|
|
58781
58814
|
lastFetchedAt: number()
|
|
58782
58815
|
});
|
|
58783
58816
|
/**
|
|
@@ -58995,11 +59028,11 @@ var DishwasherStatusSchema = object({
|
|
|
58995
59028
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
58996
59029
|
availablePrograms: array(string()),
|
|
58997
59030
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
58998
|
-
remainingSeconds: number().int().nonnegative().nullable(),
|
|
59031
|
+
remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
58999
59032
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
59000
|
-
delayedStartSeconds: number().int().nonnegative().nullable(),
|
|
59033
|
+
delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
59001
59034
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
59002
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
59035
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59003
59036
|
/** True when the door is reported open; null when the door state is not known. */
|
|
59004
59037
|
doorOpen: boolean().nullable(),
|
|
59005
59038
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -59023,7 +59056,7 @@ var DishwasherStatusSchema = object({
|
|
|
59023
59056
|
/** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
|
|
59024
59057
|
machineCareDue: boolean().nullable(),
|
|
59025
59058
|
/** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
|
|
59026
|
-
machineCareRemainingRuns: number().int().nonnegative().nullable(),
|
|
59059
|
+
machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
|
|
59027
59060
|
/** Option states: null when the appliance does not expose the option or has not said. */
|
|
59028
59061
|
halfLoad: boolean().nullable(),
|
|
59029
59062
|
hygienePlus: boolean().nullable(),
|
|
@@ -59038,7 +59071,7 @@ var DishwasherStatusSchema = object({
|
|
|
59038
59071
|
/** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
|
|
59039
59072
|
activeEvents: array(string()),
|
|
59040
59073
|
/** Numeric error code when the appliance reports one; null otherwise. */
|
|
59041
|
-
errorCode: number().nullable(),
|
|
59074
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
59042
59075
|
/** Name of the raised alert-level event while the appliance is in error; null otherwise. */
|
|
59043
59076
|
errorLabel: string().nullable(),
|
|
59044
59077
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
@@ -59250,6 +59283,7 @@ var doorbellCustomize = {
|
|
|
59250
59283
|
applyAuthority: { kind: "composition-claim" },
|
|
59251
59284
|
eventName: "onPressed",
|
|
59252
59285
|
edges: ["rising"],
|
|
59286
|
+
defaultEdge: "rising",
|
|
59253
59287
|
timestampPath: "lastPressedAt",
|
|
59254
59288
|
counterPath: "pressCountSinceStart",
|
|
59255
59289
|
highWater: "composer-advance",
|
|
@@ -59268,7 +59302,7 @@ var DoorbellStatusSchema = object({
|
|
|
59268
59302
|
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
59269
59303
|
lastPressedAt: number().nullable(),
|
|
59270
59304
|
/** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
|
|
59271
|
-
pressCountSinceStart: number()
|
|
59305
|
+
pressCountSinceStart: number().meta(readingMeta("count"))
|
|
59272
59306
|
});
|
|
59273
59307
|
var DoorbellPressEventSchema = object({
|
|
59274
59308
|
deviceId: number(),
|
|
@@ -59333,17 +59367,7 @@ onPressed: {
|
|
|
59333
59367
|
history: "none",
|
|
59334
59368
|
customize: doorbellCustomize
|
|
59335
59369
|
};
|
|
59336
|
-
|
|
59337
|
-
* Enum-state sensor — a string value picked from a finite option set.
|
|
59338
|
-
* Drives HA `sensor` entries with `state_class: enum` (HVAC action
|
|
59339
|
-
* states, weather conditions, contact-source channels, mode strings).
|
|
59340
|
-
* The option set is stable across observations; it is persisted in the
|
|
59341
|
-
* device config blob (via the control slice at adoption) and is NOT
|
|
59342
|
-
* stored in `sourceInfo`.
|
|
59343
|
-
*/
|
|
59344
|
-
/** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
|
|
59345
|
-
* enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
|
|
59346
|
-
var EnumSensorDateTimeFormatSchema = _enum([
|
|
59370
|
+
_enum([
|
|
59347
59371
|
"date",
|
|
59348
59372
|
"time",
|
|
59349
59373
|
"datetime"
|
|
@@ -59351,12 +59375,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
|
|
|
59351
59375
|
var EnumSensorStatusSchema = object({
|
|
59352
59376
|
value: string(),
|
|
59353
59377
|
/**
|
|
59354
|
-
* Set
|
|
59355
|
-
* as
|
|
59356
|
-
*
|
|
59357
|
-
* a time-only sensor → `'time'`.
|
|
59378
|
+
* Set when `value` is a timestamp rather than an enum state, so history
|
|
59379
|
+
* does not learn ISO strings as states. The display shape (date / time /
|
|
59380
|
+
* datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
|
|
59358
59381
|
*/
|
|
59359
|
-
|
|
59382
|
+
timestamp: boolean().optional(),
|
|
59360
59383
|
/** Ms epoch when the slice was last updated. */
|
|
59361
59384
|
lastFetchedAt: number()
|
|
59362
59385
|
});
|
|
@@ -59383,11 +59406,11 @@ var enumSensorCapability = {
|
|
|
59383
59406
|
* History (D850): the value as an `enum` series (an appliance's program or
|
|
59384
59407
|
* operation state, an HVAC action). The option set is per device, so the
|
|
59385
59408
|
* value list is LEARNED as values are first seen (bounded). A sensor with a
|
|
59386
|
-
* `
|
|
59409
|
+
* `timestamp` carries a clock, not a state, and is not recorded.
|
|
59387
59410
|
*/
|
|
59388
59411
|
history: { fields: { value: {
|
|
59389
59412
|
kind: "enum",
|
|
59390
|
-
skipWhenSet: "
|
|
59413
|
+
skipWhenSet: "timestamp"
|
|
59391
59414
|
} } },
|
|
59392
59415
|
/** Clock fields: written, but excluded from the compare that decides
|
|
59393
59416
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -59407,13 +59430,13 @@ var EventFireSchema = object({
|
|
|
59407
59430
|
deviceId: number(),
|
|
59408
59431
|
eventType: string(),
|
|
59409
59432
|
data: record(string(), unknown()).nullable(),
|
|
59410
|
-
timestamp: number(),
|
|
59411
|
-
seq: number()
|
|
59433
|
+
timestamp: number().meta(readingMeta("none")),
|
|
59434
|
+
seq: number().meta(readingMeta("count"))
|
|
59412
59435
|
});
|
|
59413
59436
|
var EventEmitterStatusSchema = object({
|
|
59414
59437
|
eventTypes: array(string()),
|
|
59415
59438
|
lastEvent: EventFireSchema.nullable(),
|
|
59416
|
-
eventCountSinceStart: number()
|
|
59439
|
+
eventCountSinceStart: number().meta(readingMeta("count"))
|
|
59417
59440
|
});
|
|
59418
59441
|
var eventEmitterCapability = {
|
|
59419
59442
|
name: "event-emitter",
|
|
@@ -59826,10 +59849,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
59826
59849
|
var FanControlStatusSchema = object({
|
|
59827
59850
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
59828
59851
|
* speed surface (single-speed fan). */
|
|
59829
|
-
percentage: number().min(0).max(100).nullable(),
|
|
59852
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59830
59853
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
59831
59854
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
59832
|
-
percentageStep: number().positive().optional(),
|
|
59855
|
+
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
59833
59856
|
/** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
|
|
59834
59857
|
* — empty string when no preset is active or supported. */
|
|
59835
59858
|
preset: string(),
|
|
@@ -59950,7 +59973,7 @@ var FeatureProbeStatusSchema = object({
|
|
|
59950
59973
|
/** Camera/firmware model string. `null` when the firmware doesn't expose it. */
|
|
59951
59974
|
model: string().nullable(),
|
|
59952
59975
|
/** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
|
|
59953
|
-
channelCount: number().nullable(),
|
|
59976
|
+
channelCount: number().nullable().meta(readingMeta("count")),
|
|
59954
59977
|
/**
|
|
59955
59978
|
* Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
|
|
59956
59979
|
* completes — drivers' `getAccessoryChildren()` should treat zero as
|
|
@@ -60075,7 +60098,7 @@ var gasCapability = {
|
|
|
60075
60098
|
};
|
|
60076
60099
|
var HeatingValveStatusSchema = object({
|
|
60077
60100
|
/** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
|
|
60078
|
-
position: number().min(0).max(100).nullable(),
|
|
60101
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60079
60102
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
60080
60103
|
lastFetchedAt: number()
|
|
60081
60104
|
});
|
|
@@ -60118,9 +60141,9 @@ var HumidifierStatusSchema = object({
|
|
|
60118
60141
|
/** Whether the humidifier is currently on. */
|
|
60119
60142
|
on: boolean(),
|
|
60120
60143
|
/** Current measured relative humidity (0..100). Null when not reported. */
|
|
60121
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
60144
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
60122
60145
|
/** Target relative humidity (0..100). Null when no setpoint surface. */
|
|
60123
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
60146
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
60124
60147
|
/** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
|
|
60125
60148
|
* device has no mode surface. */
|
|
60126
60149
|
mode: string().nullable(),
|
|
@@ -60130,9 +60153,9 @@ var HumidifierStatusSchema = object({
|
|
|
60130
60153
|
* `idle` / `off`). Null when the device doesn't report it. */
|
|
60131
60154
|
action: string().nullable(),
|
|
60132
60155
|
/** HA `min_humidity` attribute. Null → UI uses 0. */
|
|
60133
|
-
minHumidity: number().nullable(),
|
|
60156
|
+
minHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
60134
60157
|
/** HA `max_humidity` attribute. Null → UI uses 100. */
|
|
60135
|
-
maxHumidity: number().nullable(),
|
|
60158
|
+
maxHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
60136
60159
|
/** Ms epoch when the slice was last updated. */
|
|
60137
60160
|
lastChangedAt: number()
|
|
60138
60161
|
});
|
|
@@ -60202,22 +60225,13 @@ var humidifierCapability = {
|
|
|
60202
60225
|
* `sourceInfo`.
|
|
60203
60226
|
*/
|
|
60204
60227
|
var HumiditySensorStatusSchema = object({
|
|
60205
|
-
/** Current relative humidity, 0..100. */
|
|
60206
|
-
percent: number().min(0).max(100).meta({
|
|
60207
|
-
|
|
60228
|
+
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
60229
|
+
percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
60230
|
+
precision: 0,
|
|
60208
60231
|
label: "Humidity"
|
|
60209
|
-
}),
|
|
60232
|
+
})),
|
|
60210
60233
|
/** Ms epoch when the slice was last updated. */
|
|
60211
|
-
lastFetchedAt: number()
|
|
60212
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
60213
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
60214
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
60215
|
-
unit: string().optional(),
|
|
60216
|
-
/** Suggested decimal places for numeric display.
|
|
60217
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
60218
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
60219
|
-
* auto-formatting when absent. */
|
|
60220
|
-
precision: number().int().min(0).max(10).optional()
|
|
60234
|
+
lastFetchedAt: number()
|
|
60221
60235
|
});
|
|
60222
60236
|
var humiditySensorCapability = {
|
|
60223
60237
|
name: "humidity-sensor",
|
|
@@ -60248,12 +60262,7 @@ var humiditySensorCapability = {
|
|
|
60248
60262
|
} } },
|
|
60249
60263
|
/** Clock fields: written, but excluded from the compare that decides
|
|
60250
60264
|
* whether persisting is worth a SQLite commit. */
|
|
60251
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
60252
|
-
customize: {
|
|
60253
|
-
version: 1,
|
|
60254
|
-
advanced: "derive",
|
|
60255
|
-
targets: []
|
|
60256
|
-
}
|
|
60265
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
60257
60266
|
};
|
|
60258
60267
|
/**
|
|
60259
60268
|
* Image display cap. Models a single still image exposed by an integration —
|
|
@@ -60274,7 +60283,7 @@ var ImageStatusSchema = object({
|
|
|
60274
60283
|
* entity exposes no `entity_picture` (yet). */
|
|
60275
60284
|
url: string().nullable(),
|
|
60276
60285
|
/** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
|
|
60277
|
-
lastUpdated: number().nullable()
|
|
60286
|
+
lastUpdated: number().nullable().meta(readingMeta("none"))
|
|
60278
60287
|
});
|
|
60279
60288
|
var imageCapability = {
|
|
60280
60289
|
name: "image",
|
|
@@ -60362,19 +60371,19 @@ var NormalizedRangeSchema = object({
|
|
|
60362
60371
|
*/
|
|
60363
60372
|
var ImageSettingsStatusSchema = object({
|
|
60364
60373
|
/** Normalized 0–100. */
|
|
60365
|
-
brightness: number().optional(),
|
|
60374
|
+
brightness: number().optional().meta(readingMeta("none")),
|
|
60366
60375
|
/** Normalized 0–100. */
|
|
60367
|
-
contrast: number().optional(),
|
|
60376
|
+
contrast: number().optional().meta(readingMeta("none")),
|
|
60368
60377
|
/** Normalized 0–100. */
|
|
60369
|
-
saturation: number().optional(),
|
|
60378
|
+
saturation: number().optional().meta(readingMeta("none")),
|
|
60370
60379
|
/** Normalized 0–100. */
|
|
60371
|
-
sharpness: number().optional(),
|
|
60380
|
+
sharpness: number().optional().meta(readingMeta("none")),
|
|
60372
60381
|
mirror: boolean().optional(),
|
|
60373
60382
|
flip: boolean().optional(),
|
|
60374
60383
|
rotate: ImageRotateSchema.optional(),
|
|
60375
60384
|
whiteBalance: WhiteBalanceModeSchema.optional(),
|
|
60376
60385
|
/** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
|
|
60377
|
-
warmth: number().optional(),
|
|
60386
|
+
warmth: number().optional().meta(readingMeta("none")),
|
|
60378
60387
|
exposureMode: ExposureModeSchema.optional(),
|
|
60379
60388
|
backlightMode: BacklightModeSchema.optional(),
|
|
60380
60389
|
lastFetchedAt: number()
|
|
@@ -60653,10 +60662,10 @@ var IntercomAbilitySchema = object({
|
|
|
60653
60662
|
/** Codecs accepted by firmware, in preference order. Driver-authored strings. */
|
|
60654
60663
|
codecs: array(string()),
|
|
60655
60664
|
/** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
|
|
60656
|
-
sampleRate: number(),
|
|
60665
|
+
sampleRate: number().meta(readingMeta("none")),
|
|
60657
60666
|
duplex: _enum(["half", "full"]),
|
|
60658
60667
|
/** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
|
|
60659
|
-
maxBacklogMs: number()
|
|
60668
|
+
maxBacklogMs: number().meta(readingMeta("duration-ms"))
|
|
60660
60669
|
});
|
|
60661
60670
|
var IntercomStatusSchema = object({
|
|
60662
60671
|
/** True while a talk session is open. */
|
|
@@ -60883,13 +60892,13 @@ var LawnMowerControlStatusSchema = object({
|
|
|
60883
60892
|
activity: LawnMowerActivitySchema,
|
|
60884
60893
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
60885
60894
|
* reading. */
|
|
60886
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
60895
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60887
60896
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
60888
60897
|
* task is active / progress is unavailable. */
|
|
60889
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
60898
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60890
60899
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
60891
60900
|
* or null when unknown. */
|
|
60892
|
-
currentCode: number().nullable(),
|
|
60901
|
+
currentCode: number().nullable().meta(readingMeta("none")),
|
|
60893
60902
|
/** Human label for {@link currentCode}, or null when undecodable. */
|
|
60894
60903
|
currentCodeLabel: string().nullable(),
|
|
60895
60904
|
/** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
|
|
@@ -61422,7 +61431,7 @@ var MediaPlayerStatusSchema = object({
|
|
|
61422
61431
|
state: MediaPlayerStateSchema,
|
|
61423
61432
|
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
61424
61433
|
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
61425
|
-
volumeLevel: number().min(0).max(100).nullable(),
|
|
61434
|
+
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61426
61435
|
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
61427
61436
|
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
61428
61437
|
isMuted: boolean().nullable(),
|
|
@@ -61435,10 +61444,10 @@ var MediaPlayerStatusSchema = object({
|
|
|
61435
61444
|
currentMedia: MediaInfoSchema.nullable(),
|
|
61436
61445
|
/** Current playback position in ms. Null when not seekable or
|
|
61437
61446
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
61438
|
-
positionMs: number().int().nonnegative().nullable(),
|
|
61447
|
+
positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61439
61448
|
/** Total duration of the current media in ms. Null when unknown
|
|
61440
61449
|
* (live stream) or nothing is playing. */
|
|
61441
|
-
durationMs: number().int().nonnegative().nullable(),
|
|
61450
|
+
durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61442
61451
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
61443
61452
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
61444
61453
|
shuffle: boolean().nullable(),
|
|
@@ -61747,7 +61756,7 @@ var motionCustomize = {
|
|
|
61747
61756
|
eventName: "onMotionChanged"
|
|
61748
61757
|
},
|
|
61749
61758
|
deviceTypes: [DeviceType.Camera],
|
|
61750
|
-
apply: "
|
|
61759
|
+
apply: "add",
|
|
61751
61760
|
applyAuthority: {
|
|
61752
61761
|
kind: "camera-sensors",
|
|
61753
61762
|
timeoutField: "motionCooldownMs"
|
|
@@ -61819,7 +61828,7 @@ var MotionStatusSchema = object({
|
|
|
61819
61828
|
* it, every provider seeded it at boot, and every session left it
|
|
61820
61829
|
* `detected: true` after its detach.
|
|
61821
61830
|
*/
|
|
61822
|
-
autoClearAfterMs: number().nullable()
|
|
61831
|
+
autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
|
|
61823
61832
|
});
|
|
61824
61833
|
/**
|
|
61825
61834
|
* Payload of `motion.onMotionChanged` event + the corresponding bus
|
|
@@ -62001,7 +62010,7 @@ var MotionZoneRegionSchema = object({
|
|
|
62001
62010
|
* the grid region(s). */
|
|
62002
62011
|
var MotionZoneStatusSchema = object({
|
|
62003
62012
|
enabled: boolean(),
|
|
62004
|
-
sensitivity: number(),
|
|
62013
|
+
sensitivity: number().meta(readingMeta("none")),
|
|
62005
62014
|
/** Grid region(s). Today exactly one `grid` shape. */
|
|
62006
62015
|
regions: array(MotionZoneRegionSchema),
|
|
62007
62016
|
lastFetchedAt: number()
|
|
@@ -62371,7 +62380,7 @@ var NavigationStatusSchema = object({
|
|
|
62371
62380
|
following: boolean(),
|
|
62372
62381
|
flash: boolean(),
|
|
62373
62382
|
lightMode: NavigationLightModeSchema,
|
|
62374
|
-
lightLevel: number().min(40).max(100),
|
|
62383
|
+
lightLevel: number().min(40).max(100).meta(readingMeta("none")),
|
|
62375
62384
|
/** Ms epoch when the slice was last updated. */
|
|
62376
62385
|
lastChangedAt: number()
|
|
62377
62386
|
});
|
|
@@ -62505,13 +62514,13 @@ var NetworkLinkStatusSchema = object({
|
|
|
62505
62514
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
62506
62515
|
* SKIP a null rather than coerce it.
|
|
62507
62516
|
*/
|
|
62508
|
-
signalPercent: number().min(0).max(100).nullable(),
|
|
62517
|
+
signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62509
62518
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
62510
|
-
rssiDbm: number().optional(),
|
|
62519
|
+
rssiDbm: number().optional().meta(readingMeta("signal-level")),
|
|
62511
62520
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
62512
62521
|
ssid: string().optional(),
|
|
62513
62522
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
62514
|
-
lastUpdated: number()
|
|
62523
|
+
lastUpdated: number().meta(readingMeta("none"))
|
|
62515
62524
|
});
|
|
62516
62525
|
var networkLinkCapability = {
|
|
62517
62526
|
name: "network-link",
|
|
@@ -62896,7 +62905,7 @@ var NotifierStatusSchema = object({
|
|
|
62896
62905
|
lastError: string().nullable(),
|
|
62897
62906
|
/** Number of deliveries currently buffered server-side (rate-limited
|
|
62898
62907
|
* notifiers may queue). 0 when the provider sends synchronously. */
|
|
62899
|
-
queueDepth: number().int().nonnegative(),
|
|
62908
|
+
queueDepth: number().int().nonnegative().meta(readingMeta("count")),
|
|
62900
62909
|
/** Per-feature capability matrix — authoritative source for the
|
|
62901
62910
|
* compose-form field gating. */
|
|
62902
62911
|
supports: NotifierSupportsSchema
|
|
@@ -62988,11 +62997,9 @@ onSent: { data: object({
|
|
|
62988
62997
|
* cap fits (HA `sensor` whose `device_class` we don't have a typed cap
|
|
62989
62998
|
* for: water flow, distance, weight, frequency, signal strength, …).
|
|
62990
62999
|
*
|
|
62991
|
-
* The
|
|
62992
|
-
*
|
|
62993
|
-
*
|
|
62994
|
-
* The UI reads them directly from the slice — they are the single source
|
|
62995
|
-
* of truth for generic numeric rendering and do NOT live on `sourceInfo`.
|
|
63000
|
+
* The quantity is not on this slice. It is chosen when the device is
|
|
63001
|
+
* adopted and stored once on `SourceInfo.quantity` (D944). `value` is in
|
|
63002
|
+
* that quantity's base unit. `null` is a refused sample (D393).
|
|
62996
63003
|
*
|
|
62997
63004
|
* Prefer the typed alternatives (`temperature-sensor`, `humidity-sensor`,
|
|
62998
63005
|
* `ambient-light-sensor`, `pressure-sensor`, `power-meter`,
|
|
@@ -63001,15 +63008,8 @@ onSent: { data: object({
|
|
|
63001
63008
|
* numeric values land here.
|
|
63002
63009
|
*/
|
|
63003
63010
|
var NumericSensorStatusSchema = object({
|
|
63004
|
-
|
|
63005
|
-
|
|
63006
|
-
* Populated live from the upstream source on each state push.
|
|
63007
|
-
* Absent for unitless sensors. */
|
|
63008
|
-
unit: string().optional(),
|
|
63009
|
-
/** Suggested decimal places for numeric display.
|
|
63010
|
-
* Populated live from the upstream source when provided.
|
|
63011
|
-
* Falls back to auto-formatting when absent. */
|
|
63012
|
-
precision: number().int().min(0).max(10).optional(),
|
|
63011
|
+
/** Reading in the device quantity's base unit. `null` when refused (D393). */
|
|
63012
|
+
value: number().nullable().meta(readingMeta("device")),
|
|
63013
63013
|
/** Ms epoch when the slice was last updated. */
|
|
63014
63014
|
lastFetchedAt: number()
|
|
63015
63015
|
});
|
|
@@ -63033,11 +63033,11 @@ var numericSensorCapability = {
|
|
|
63033
63033
|
*/
|
|
63034
63034
|
durability: "restored",
|
|
63035
63035
|
/**
|
|
63036
|
-
* History (D850): recorded series. The unit is
|
|
63036
|
+
* History (D850): recorded series. The unit is the device quantity's base,
|
|
63037
|
+
* resolved when the series is read — the cap has no single unit.
|
|
63037
63038
|
*/
|
|
63038
63039
|
history: { fields: { value: {
|
|
63039
63040
|
kind: "gauge",
|
|
63040
|
-
unitFrom: "unit",
|
|
63041
63041
|
minIntervalMs: 3e4
|
|
63042
63042
|
} } },
|
|
63043
63043
|
/** Clock fields: written, but excluded from the compare that decides
|
|
@@ -63329,23 +63329,26 @@ var PetFeederStatusSchema = object({
|
|
|
63329
63329
|
/** Food currently in the bowl (grams). Null when the device has not
|
|
63330
63330
|
* reported a reading yet. On dual-hopper models this is the combined
|
|
63331
63331
|
* bowl reading; per-hopper levels live in `food1`/`food2`. */
|
|
63332
|
-
foodLevel: number().nullable(),
|
|
63332
|
+
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
63333
63333
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
63334
63334
|
* single-hopper models. */
|
|
63335
|
-
food1: number().nullable(),
|
|
63335
|
+
food1: number().nullable().meta(readingMeta("none")),
|
|
63336
63336
|
/** Hopper-2 food level (grams) on dual-hopper feeders; null on
|
|
63337
63337
|
* single-hopper models. */
|
|
63338
|
-
food2: number().nullable(),
|
|
63338
|
+
food2: number().nullable().meta(readingMeta("none")),
|
|
63339
63339
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
63340
63340
|
* (`device_class: problem`, on = low). True when the bowl is empty /
|
|
63341
63341
|
* below the feeder's low threshold. */
|
|
63342
63342
|
lowFood: boolean(),
|
|
63343
63343
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
63344
63344
|
* device has no battery reading. */
|
|
63345
|
-
batteryPower: number().min(0).max(100).nullable(),
|
|
63346
|
-
/**
|
|
63347
|
-
*
|
|
63348
|
-
|
|
63345
|
+
batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63346
|
+
/**
|
|
63347
|
+
* Desiccant life remaining, stored in seconds (duration base, D944).
|
|
63348
|
+
* Providers convert from the days the feeder reports. Null when the
|
|
63349
|
+
* model has no desiccant sensor.
|
|
63350
|
+
*/
|
|
63351
|
+
desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
|
|
63349
63352
|
/** True while a feed is in progress. */
|
|
63350
63353
|
feeding: boolean(),
|
|
63351
63354
|
/** Decoded connectivity / power status (HA petkit device-status enum).
|
|
@@ -63356,7 +63359,7 @@ var PetFeederStatusSchema = object({
|
|
|
63356
63359
|
* with `errorCode` for consumers that want the raw integer. */
|
|
63357
63360
|
error: string().nullable(),
|
|
63358
63361
|
/** Raw device error code (0 / null = no error). */
|
|
63359
|
-
errorCode: number().nullable(),
|
|
63362
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
63360
63363
|
/** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
|
|
63361
63364
|
isDualHopper: boolean(),
|
|
63362
63365
|
/** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
|
|
@@ -63366,7 +63369,7 @@ var PetFeederStatusSchema = object({
|
|
|
63366
63369
|
/** Play a chime when dispensing. */
|
|
63367
63370
|
feedSound: boolean(),
|
|
63368
63371
|
/** Speaker / prompt volume level (device-scaled integer). */
|
|
63369
|
-
volume: number(),
|
|
63372
|
+
volume: number().meta(readingMeta("none")),
|
|
63370
63373
|
/** Ms epoch when the slice was last refreshed from the cloud. */
|
|
63371
63374
|
lastFetchedAt: number()
|
|
63372
63375
|
});
|
|
@@ -63485,7 +63488,7 @@ var petFeederCapability = {
|
|
|
63485
63488
|
},
|
|
63486
63489
|
desiccantLeftDays: {
|
|
63487
63490
|
kind: "gauge",
|
|
63488
|
-
unit: "
|
|
63491
|
+
unit: "s"
|
|
63489
63492
|
}
|
|
63490
63493
|
} }
|
|
63491
63494
|
};
|
|
@@ -63886,36 +63889,19 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
63886
63889
|
*/
|
|
63887
63890
|
var PowerMeterStatusSchema = object({
|
|
63888
63891
|
/** Instantaneous power draw in watts. */
|
|
63889
|
-
watts: number().optional().meta({
|
|
63890
|
-
unit: "W",
|
|
63891
|
-
label: "Power"
|
|
63892
|
-
}),
|
|
63892
|
+
watts: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
63893
63893
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
63894
|
-
kwhTotal: number().optional().meta({
|
|
63895
|
-
unit: "kWh",
|
|
63896
|
-
label: "Energy"
|
|
63897
|
-
}),
|
|
63894
|
+
kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
63898
63895
|
/** Voltage in volts. */
|
|
63899
|
-
volts: number().optional(),
|
|
63896
|
+
volts: number().optional().meta(readingMeta("voltage")),
|
|
63900
63897
|
/** Current in amperes. */
|
|
63901
|
-
amps: number().optional(),
|
|
63902
|
-
/** Apparent power in volt-amperes
|
|
63903
|
-
va: number().optional(),
|
|
63898
|
+
amps: number().optional().meta(readingMeta("current")),
|
|
63899
|
+
/** Apparent power in volt-amperes. */
|
|
63900
|
+
va: number().optional().meta(readingMeta("apparent-power")),
|
|
63904
63901
|
/** Power factor (0..1) if reported. */
|
|
63905
|
-
powerFactor: number().min(0).max(1).optional(),
|
|
63902
|
+
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
63906
63903
|
/** Ms epoch when the slice was last updated. */
|
|
63907
|
-
lastFetchedAt: number()
|
|
63908
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
63909
|
-
* `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
|
|
63910
|
-
* Each upstream `sensor.*` entity surfaces ONE device_class, so one
|
|
63911
|
-
* unit per slice is unambiguous. The UI prefers this over the role's
|
|
63912
|
-
* canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
|
|
63913
|
-
unit: string().optional(),
|
|
63914
|
-
/** Suggested decimal places for numeric display.
|
|
63915
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
63916
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
63917
|
-
* auto-formatting when absent. */
|
|
63918
|
-
precision: number().int().min(0).max(10).optional()
|
|
63904
|
+
lastFetchedAt: number()
|
|
63919
63905
|
});
|
|
63920
63906
|
var powerMeterCapability = {
|
|
63921
63907
|
name: "power-meter",
|
|
@@ -63979,12 +63965,7 @@ var powerMeterCapability = {
|
|
|
63979
63965
|
} },
|
|
63980
63966
|
/** Clock fields: written, but excluded from the compare that decides
|
|
63981
63967
|
* whether persisting is worth a SQLite commit. */
|
|
63982
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
63983
|
-
customize: {
|
|
63984
|
-
version: 1,
|
|
63985
|
-
advanced: "derive",
|
|
63986
|
-
targets: []
|
|
63987
|
-
}
|
|
63968
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
63988
63969
|
};
|
|
63989
63970
|
/**
|
|
63990
63971
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
@@ -64004,11 +63985,11 @@ var powerMeterCapability = {
|
|
|
64004
63985
|
*/
|
|
64005
63986
|
var GpsLocationSchema = object({
|
|
64006
63987
|
/** Latitude in decimal degrees, -90..90. */
|
|
64007
|
-
latitude: number().min(-90).max(90),
|
|
63988
|
+
latitude: number().min(-90).max(90).meta(readingMeta("angle")),
|
|
64008
63989
|
/** Longitude in decimal degrees, -180..180. */
|
|
64009
|
-
longitude: number().min(-180).max(180),
|
|
63990
|
+
longitude: number().min(-180).max(180).meta(readingMeta("angle")),
|
|
64010
63991
|
/** Reported accuracy in meters (lower = better). */
|
|
64011
|
-
accuracyMeters: number().nonnegative()
|
|
63992
|
+
accuracyMeters: number().nonnegative().meta(readingMeta("length"))
|
|
64012
63993
|
});
|
|
64013
63994
|
var PresenceStatusSchema = object({
|
|
64014
63995
|
/** `home` / `not_home` / any user-defined zone name. */
|
|
@@ -64020,7 +64001,7 @@ var PresenceStatusSchema = object({
|
|
|
64020
64001
|
gps: GpsLocationSchema.nullable(),
|
|
64021
64002
|
/** Optional battery level of the tracking device (0..100). Null
|
|
64022
64003
|
* when not reported. */
|
|
64023
|
-
batteryPercent: number().min(0).max(100).nullable(),
|
|
64004
|
+
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
64024
64005
|
/** Ms epoch when the slice was last updated. */
|
|
64025
64006
|
lastChangedAt: number()
|
|
64026
64007
|
});
|
|
@@ -64070,19 +64051,10 @@ var presenceCapability = {
|
|
|
64070
64051
|
* `sourceInfo`.
|
|
64071
64052
|
*/
|
|
64072
64053
|
var PressureSensorStatusSchema = object({
|
|
64073
|
-
/** Current pressure in hPa. */
|
|
64074
|
-
hpa: number(),
|
|
64054
|
+
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
64055
|
+
hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
64075
64056
|
/** Ms epoch when the slice was last updated. */
|
|
64076
|
-
lastFetchedAt: number()
|
|
64077
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
64078
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
64079
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
64080
|
-
unit: string().optional(),
|
|
64081
|
-
/** Suggested decimal places for numeric display.
|
|
64082
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
64083
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
64084
|
-
* auto-formatting when absent. */
|
|
64085
|
-
precision: number().int().min(0).max(10).optional()
|
|
64057
|
+
lastFetchedAt: number()
|
|
64086
64058
|
});
|
|
64087
64059
|
var pressureSensorCapability = {
|
|
64088
64060
|
name: "pressure-sensor",
|
|
@@ -64496,8 +64468,8 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
|
|
|
64496
64468
|
}), _void(), { kind: "mutation" });
|
|
64497
64469
|
var PtzAutotrackSettingsSchema = object({
|
|
64498
64470
|
targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
|
|
64499
|
-
stopDelaySeconds: number().int().min(0).max(300),
|
|
64500
|
-
disappearDelaySeconds: number().int().min(0).max(300)
|
|
64471
|
+
stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
|
|
64472
|
+
disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
|
|
64501
64473
|
});
|
|
64502
64474
|
/**
|
|
64503
64475
|
* One option the camera advertises as a valid `targetType`. Drivers
|
|
@@ -64651,7 +64623,7 @@ var RunningStateStatusSchema = object({
|
|
|
64651
64623
|
/** null = unknown — never coerced to false (D393). */
|
|
64652
64624
|
running: boolean().nullable(),
|
|
64653
64625
|
/** Epoch ms the current boolean began; null while `running` is null. */
|
|
64654
|
-
since: number().nullable(),
|
|
64626
|
+
since: number().nullable().meta(readingMeta("none")),
|
|
64655
64627
|
source: RunningStateSourceSchema.meta({ label: "Source" }),
|
|
64656
64628
|
phase: string().nullable(),
|
|
64657
64629
|
/** null until a reading has been published (D393). */
|
|
@@ -64666,7 +64638,6 @@ var RUNNING_DEVICE_TYPES = Object.values(DeviceType);
|
|
|
64666
64638
|
/** Generic Customize target for the running boolean (D936). */
|
|
64667
64639
|
var runningCustomize = {
|
|
64668
64640
|
version: 1,
|
|
64669
|
-
advanced: "derive",
|
|
64670
64641
|
targets: [{
|
|
64671
64642
|
id: "running",
|
|
64672
64643
|
cardinality: "scalar",
|
|
@@ -64686,7 +64657,7 @@ var runningCustomize = {
|
|
|
64686
64657
|
DeviceType.Thermostat,
|
|
64687
64658
|
DeviceType.PetFeeder
|
|
64688
64659
|
],
|
|
64689
|
-
apply: "add
|
|
64660
|
+
apply: "add",
|
|
64690
64661
|
path: "running",
|
|
64691
64662
|
sources: [
|
|
64692
64663
|
{
|
|
@@ -64702,6 +64673,7 @@ var runningCustomize = {
|
|
|
64702
64673
|
requiredUnit: "W",
|
|
64703
64674
|
transforms: [{
|
|
64704
64675
|
kind: "threshold",
|
|
64676
|
+
comparisons: ["gte", "lte"],
|
|
64705
64677
|
comparison: "gte",
|
|
64706
64678
|
defaultThreshold: 5,
|
|
64707
64679
|
defaultStartDwellMs: RUNNING_POWER_START_DELAY_MS,
|
|
@@ -65949,9 +65921,9 @@ var RecordTriggerSchema = _enum([
|
|
|
65949
65921
|
*/
|
|
65950
65922
|
var RecordWindowSchema = object({
|
|
65951
65923
|
trigger: RecordTriggerSchema,
|
|
65952
|
-
day: number().int().min(0).max(6),
|
|
65953
|
-
startMinute: number().int().min(0).max(1439),
|
|
65954
|
-
endMinute: number().int().min(1).max(1440)
|
|
65924
|
+
day: number().int().min(0).max(6).meta(readingMeta("none")),
|
|
65925
|
+
startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
|
|
65926
|
+
endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
|
|
65955
65927
|
});
|
|
65956
65928
|
/** Status of one physical volume, as the camera itself describes it. */
|
|
65957
65929
|
var OnboardStorageVolumeSchema = object({
|
|
@@ -66027,10 +65999,10 @@ var RecordingOnboardStatusSchema = object({
|
|
|
66027
65999
|
/** Master "record to the card at all" switch. */
|
|
66028
66000
|
enabled: boolean().nullable(),
|
|
66029
66001
|
overwriteWhenFull: boolean().nullable(),
|
|
66030
|
-
preRecordSec: number().nullable(),
|
|
66031
|
-
postRecordSec: number().nullable(),
|
|
66002
|
+
preRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
66003
|
+
postRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
66032
66004
|
/** Length of one recorded file, in minutes. */
|
|
66033
|
-
segmentMinutes: number().nullable(),
|
|
66005
|
+
segmentMinutes: number().nullable().meta(readingMeta("none")),
|
|
66034
66006
|
/** The primary track's weekly windows, flattened. */
|
|
66035
66007
|
windows: array(RecordWindowSchema),
|
|
66036
66008
|
/**
|
|
@@ -66046,7 +66018,7 @@ var RecordingOnboardStatusSchema = object({
|
|
|
66046
66018
|
* provider reporting a non-zero count also reports the schedule as not
|
|
66047
66019
|
* writable.
|
|
66048
66020
|
*/
|
|
66049
|
-
unreadableWindows: number(),
|
|
66021
|
+
unreadableWindows: number().meta(readingMeta("count")),
|
|
66050
66022
|
/**
|
|
66051
66023
|
* The camera is scheduled to record and has NO usable storage.
|
|
66052
66024
|
*
|
|
@@ -66891,7 +66863,7 @@ var CamStreamDescriptorSchema = object({
|
|
|
66891
66863
|
url: string().optional(),
|
|
66892
66864
|
codec: string().optional(),
|
|
66893
66865
|
resolution: CamStreamResolutionSchema.optional(),
|
|
66894
|
-
fps: number().positive().optional(),
|
|
66866
|
+
fps: number().positive().optional().meta(readingMeta("rate")),
|
|
66895
66867
|
label: string().optional(),
|
|
66896
66868
|
/** Device-level features (e.g. `battery-operated`) — drives broker policy. */
|
|
66897
66869
|
deviceFeatures: array(string()).optional(),
|
|
@@ -66978,18 +66950,18 @@ var StreamProfileSchema = _enum([
|
|
|
66978
66950
|
]);
|
|
66979
66951
|
/** The current encoding config of one stream profile. */
|
|
66980
66952
|
var StreamProfileConfigSchema = object({
|
|
66981
|
-
width: number(),
|
|
66982
|
-
height: number(),
|
|
66953
|
+
width: number().meta(readingMeta("none")),
|
|
66954
|
+
height: number().meta(readingMeta("none")),
|
|
66983
66955
|
codec: _enum(["h264", "h265"]),
|
|
66984
|
-
framerate: number(),
|
|
66985
|
-
bitrate: number(),
|
|
66956
|
+
framerate: number().meta(readingMeta("rate")),
|
|
66957
|
+
bitrate: number().meta(readingMeta("bitrate")),
|
|
66986
66958
|
bitrateMode: _enum(["vbr", "cbr"]).optional(),
|
|
66987
66959
|
encoderProfile: _enum([
|
|
66988
66960
|
"high",
|
|
66989
66961
|
"main",
|
|
66990
66962
|
"baseline"
|
|
66991
66963
|
]).optional(),
|
|
66992
|
-
gop: number().optional(),
|
|
66964
|
+
gop: number().optional().meta(readingMeta("none")),
|
|
66993
66965
|
/**
|
|
66994
66966
|
* Whether THIS profile currently carries an audio track. READ-ONLY here.
|
|
66995
66967
|
*
|
|
@@ -67210,12 +67182,7 @@ var switchCapability = {
|
|
|
67210
67182
|
label: "On",
|
|
67211
67183
|
kind: "boolean"
|
|
67212
67184
|
}
|
|
67213
|
-
}] }
|
|
67214
|
-
customize: {
|
|
67215
|
-
version: 1,
|
|
67216
|
-
advanced: "derive",
|
|
67217
|
-
targets: []
|
|
67218
|
-
}
|
|
67185
|
+
}] }
|
|
67219
67186
|
};
|
|
67220
67187
|
/** C0 controls, DEL and the C1 range: none of them belongs in a name. */
|
|
67221
67188
|
function hasControlCharacter(value) {
|
|
@@ -67798,35 +67765,21 @@ var tamperCapability = {
|
|
|
67798
67765
|
* entries with `device_class: temperature` and any future native
|
|
67799
67766
|
* thermometer.
|
|
67800
67767
|
*
|
|
67801
|
-
*
|
|
67802
|
-
*
|
|
67803
|
-
* (climate-control's `currentTemp`, energy analytics) can compose
|
|
67804
|
-
* without per-source unit fixups. The canonical display unit (`°C`) is
|
|
67805
|
-
* a descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
67806
|
-
* `sourceInfo`.
|
|
67768
|
+
* The number is Celsius (D944). Providers convert on ingest. A failed
|
|
67769
|
+
* conversion is `null`, never a guess. Display unit is a read-time preference.
|
|
67807
67770
|
*
|
|
67808
67771
|
* Status `lastFetchedAt` lets staleness-aware consumers detect a
|
|
67809
67772
|
* frozen feed (provider hung) distinct from a "temperature hasn't
|
|
67810
67773
|
* changed" steady state.
|
|
67811
67774
|
*/
|
|
67812
67775
|
var TemperatureSensorStatusSchema = object({
|
|
67813
|
-
/** Current temperature in Celsius. */
|
|
67814
|
-
celsius: number().meta({
|
|
67815
|
-
|
|
67776
|
+
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
67777
|
+
celsius: number().nullable().meta(readingMeta("temperature", {
|
|
67778
|
+
precision: 1,
|
|
67816
67779
|
label: "Temperature"
|
|
67817
|
-
}),
|
|
67780
|
+
})),
|
|
67818
67781
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
67819
|
-
lastFetchedAt: number()
|
|
67820
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
67821
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
67822
|
-
* role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
|
|
67823
|
-
* Absent → the UI falls back to the role's canonical unit. */
|
|
67824
|
-
unit: string().optional(),
|
|
67825
|
-
/** Suggested decimal places for numeric display.
|
|
67826
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
67827
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
67828
|
-
* auto-formatting when absent. */
|
|
67829
|
-
precision: number().int().min(0).max(10).optional()
|
|
67782
|
+
lastFetchedAt: number()
|
|
67830
67783
|
});
|
|
67831
67784
|
var temperatureSensorCapability = {
|
|
67832
67785
|
name: "temperature-sensor",
|
|
@@ -67857,12 +67810,7 @@ var temperatureSensorCapability = {
|
|
|
67857
67810
|
} } },
|
|
67858
67811
|
/** Clock fields: written, but excluded from the compare that decides
|
|
67859
67812
|
* whether persisting is worth a SQLite commit. */
|
|
67860
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
67861
|
-
customize: {
|
|
67862
|
-
version: 1,
|
|
67863
|
-
advanced: "derive",
|
|
67864
|
-
targets: []
|
|
67865
|
-
}
|
|
67813
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
67866
67814
|
};
|
|
67867
67815
|
/**
|
|
67868
67816
|
* toast — system-scoped singleton capability that streams toast
|
|
@@ -68145,16 +68093,16 @@ var VacuumControlStatusSchema = object({
|
|
|
68145
68093
|
]),
|
|
68146
68094
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
68147
68095
|
* reading. */
|
|
68148
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
68096
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68149
68097
|
/** Current fan-speed token (provider-verbatim). Null when unknown or
|
|
68150
68098
|
* the vacuum has no speed control. */
|
|
68151
68099
|
fanSpeed: string().nullable(),
|
|
68152
68100
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
68153
68101
|
availableFanSpeeds: array(string()),
|
|
68154
68102
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
68155
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
68103
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68156
68104
|
/** Current error code (0 / null = no error). */
|
|
68157
|
-
errorCode: number().nullable(),
|
|
68105
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
68158
68106
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
68159
68107
|
errorLabel: string().nullable(),
|
|
68160
68108
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -68232,7 +68180,7 @@ var ValveStatusSchema = object({
|
|
|
68232
68180
|
]),
|
|
68233
68181
|
/** 0 = fully closed, 100 = fully open. Null when the device has no
|
|
68234
68182
|
* intermediate position surface. */
|
|
68235
|
-
position: number().min(0).max(100).nullable(),
|
|
68183
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68236
68184
|
/** Ms epoch when the slice was last updated. */
|
|
68237
68185
|
lastChangedAt: number()
|
|
68238
68186
|
});
|
|
@@ -68340,9 +68288,9 @@ var vibrationCapability = {
|
|
|
68340
68288
|
*/
|
|
68341
68289
|
var WaterHeaterStatusSchema = object({
|
|
68342
68290
|
/** Current measured temperature. Null when not reported. */
|
|
68343
|
-
currentTemp: number().nullable(),
|
|
68291
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68344
68292
|
/** Target temperature setpoint. Null when no setpoint surface. */
|
|
68345
|
-
targetTemp: number().nullable(),
|
|
68293
|
+
targetTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68346
68294
|
/** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
|
|
68347
68295
|
* `heat_pump` / `high_demand` / `performance` / `off`). Null when the
|
|
68348
68296
|
* device reports an unknown state. */
|
|
@@ -68353,9 +68301,9 @@ var WaterHeaterStatusSchema = object({
|
|
|
68353
68301
|
* has no away surface. */
|
|
68354
68302
|
away: boolean().nullable(),
|
|
68355
68303
|
/** HA `min_temp` attribute. Null when not reported. */
|
|
68356
|
-
minTemp: number().nullable(),
|
|
68304
|
+
minTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68357
68305
|
/** HA `max_temp` attribute. Null when not reported. */
|
|
68358
|
-
maxTemp: number().nullable(),
|
|
68306
|
+
maxTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68359
68307
|
/** Ms epoch when the slice was last updated. */
|
|
68360
68308
|
lastChangedAt: number()
|
|
68361
68309
|
});
|
|
@@ -68440,25 +68388,18 @@ var waterHeaterCapability = {
|
|
|
68440
68388
|
* Forecast deferred — current conditions only for v1.
|
|
68441
68389
|
*/
|
|
68442
68390
|
var WeatherStatusSchema = object({
|
|
68443
|
-
/** Verbatim
|
|
68444
|
-
* when no condition has been reported yet. */
|
|
68391
|
+
/** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
|
|
68445
68392
|
condition: string().nullable(),
|
|
68446
|
-
/** Current temperature in
|
|
68447
|
-
temperature: number().nullable(),
|
|
68448
|
-
/** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
|
|
68449
|
-
temperatureUnit: string().nullable(),
|
|
68393
|
+
/** Current temperature in Celsius. Null when not provided or refused. */
|
|
68394
|
+
temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
|
|
68450
68395
|
/** Relative humidity (0..100). Null when not provided. */
|
|
68451
|
-
humidity: number().min(0).max(100).nullable(),
|
|
68452
|
-
/** Barometric pressure in
|
|
68453
|
-
pressure: number().nullable(),
|
|
68454
|
-
/**
|
|
68455
|
-
|
|
68456
|
-
/** Wind speed in the reported unit. Null when not provided. */
|
|
68457
|
-
windSpeed: number().nullable(),
|
|
68458
|
-
/** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
|
|
68459
|
-
windSpeedUnit: string().nullable(),
|
|
68396
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
68397
|
+
/** Barometric pressure in hPa. Null when not provided or refused. */
|
|
68398
|
+
pressure: number().nullable().meta(readingMeta("pressure")),
|
|
68399
|
+
/** Wind speed in metres per second. Null when not provided or refused. */
|
|
68400
|
+
windSpeed: number().nullable().meta(readingMeta("speed")),
|
|
68460
68401
|
/** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
|
|
68461
|
-
windBearing: number().nullable(),
|
|
68402
|
+
windBearing: number().nullable().meta(readingMeta("angle")),
|
|
68462
68403
|
/** Ms epoch when the slice was last updated. */
|
|
68463
68404
|
lastFetchedAt: number()
|
|
68464
68405
|
});
|
|
@@ -68486,12 +68427,12 @@ var weatherCapability = {
|
|
|
68486
68427
|
*/
|
|
68487
68428
|
durability: "session",
|
|
68488
68429
|
/**
|
|
68489
|
-
* History (D850): recorded series.
|
|
68430
|
+
* History (D850): recorded series. Numbers are the quantity base (D944).
|
|
68490
68431
|
*/
|
|
68491
68432
|
history: { fields: {
|
|
68492
68433
|
temperature: {
|
|
68493
68434
|
kind: "gauge",
|
|
68494
|
-
|
|
68435
|
+
unit: "°C",
|
|
68495
68436
|
deadband: .2
|
|
68496
68437
|
},
|
|
68497
68438
|
humidity: {
|
|
@@ -68501,12 +68442,12 @@ var weatherCapability = {
|
|
|
68501
68442
|
},
|
|
68502
68443
|
pressure: {
|
|
68503
68444
|
kind: "gauge",
|
|
68504
|
-
|
|
68445
|
+
unit: "hPa",
|
|
68505
68446
|
deadband: .5
|
|
68506
68447
|
},
|
|
68507
68448
|
windSpeed: {
|
|
68508
68449
|
kind: "gauge",
|
|
68509
|
-
|
|
68450
|
+
unit: "m/s",
|
|
68510
68451
|
deadband: .5
|
|
68511
68452
|
}
|
|
68512
68453
|
} }
|
|
@@ -69407,6 +69348,389 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
69407
69348
|
kind: "mutation",
|
|
69408
69349
|
auth: "admin"
|
|
69409
69350
|
});
|
|
69351
|
+
/**
|
|
69352
|
+
* Frozen unit → spec table. `•` in the doc marks each dimension's canonical
|
|
69353
|
+
* unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
|
|
69354
|
+
* `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
|
|
69355
|
+
* decimals). K derives from `°C = K − 273.15`.
|
|
69356
|
+
*/
|
|
69357
|
+
var UNIT_TABLE = {
|
|
69358
|
+
"°C": {
|
|
69359
|
+
dimension: "temperature",
|
|
69360
|
+
factor: 1,
|
|
69361
|
+
offset: 0
|
|
69362
|
+
},
|
|
69363
|
+
"°F": {
|
|
69364
|
+
dimension: "temperature",
|
|
69365
|
+
factor: 5 / 9,
|
|
69366
|
+
offset: -160 / 9
|
|
69367
|
+
},
|
|
69368
|
+
K: {
|
|
69369
|
+
dimension: "temperature",
|
|
69370
|
+
factor: 1,
|
|
69371
|
+
offset: -273.15
|
|
69372
|
+
},
|
|
69373
|
+
hPa: {
|
|
69374
|
+
dimension: "pressure",
|
|
69375
|
+
factor: 1,
|
|
69376
|
+
offset: 0
|
|
69377
|
+
},
|
|
69378
|
+
kPa: {
|
|
69379
|
+
dimension: "pressure",
|
|
69380
|
+
factor: 10,
|
|
69381
|
+
offset: 0
|
|
69382
|
+
},
|
|
69383
|
+
Pa: {
|
|
69384
|
+
dimension: "pressure",
|
|
69385
|
+
factor: .01,
|
|
69386
|
+
offset: 0
|
|
69387
|
+
},
|
|
69388
|
+
mbar: {
|
|
69389
|
+
dimension: "pressure",
|
|
69390
|
+
factor: 1,
|
|
69391
|
+
offset: 0
|
|
69392
|
+
},
|
|
69393
|
+
bar: {
|
|
69394
|
+
dimension: "pressure",
|
|
69395
|
+
factor: 1e3,
|
|
69396
|
+
offset: 0
|
|
69397
|
+
},
|
|
69398
|
+
inHg: {
|
|
69399
|
+
dimension: "pressure",
|
|
69400
|
+
factor: 33.8639,
|
|
69401
|
+
offset: 0
|
|
69402
|
+
},
|
|
69403
|
+
mmHg: {
|
|
69404
|
+
dimension: "pressure",
|
|
69405
|
+
factor: 1.33322,
|
|
69406
|
+
offset: 0
|
|
69407
|
+
},
|
|
69408
|
+
psi: {
|
|
69409
|
+
dimension: "pressure",
|
|
69410
|
+
factor: 68.9476,
|
|
69411
|
+
offset: 0
|
|
69412
|
+
},
|
|
69413
|
+
"m/s": {
|
|
69414
|
+
dimension: "speed",
|
|
69415
|
+
factor: 1,
|
|
69416
|
+
offset: 0
|
|
69417
|
+
},
|
|
69418
|
+
"km/h": {
|
|
69419
|
+
dimension: "speed",
|
|
69420
|
+
factor: 1 / 3.6,
|
|
69421
|
+
offset: 0
|
|
69422
|
+
},
|
|
69423
|
+
mph: {
|
|
69424
|
+
dimension: "speed",
|
|
69425
|
+
factor: .44704,
|
|
69426
|
+
offset: 0
|
|
69427
|
+
},
|
|
69428
|
+
kn: {
|
|
69429
|
+
dimension: "speed",
|
|
69430
|
+
factor: .514444,
|
|
69431
|
+
offset: 0
|
|
69432
|
+
},
|
|
69433
|
+
"mm/h": {
|
|
69434
|
+
dimension: "precipitation-rate",
|
|
69435
|
+
factor: 1,
|
|
69436
|
+
offset: 0
|
|
69437
|
+
},
|
|
69438
|
+
"in/h": {
|
|
69439
|
+
dimension: "precipitation-rate",
|
|
69440
|
+
factor: 25.4,
|
|
69441
|
+
offset: 0
|
|
69442
|
+
},
|
|
69443
|
+
m: {
|
|
69444
|
+
dimension: "length",
|
|
69445
|
+
factor: 1,
|
|
69446
|
+
offset: 0
|
|
69447
|
+
},
|
|
69448
|
+
mm: {
|
|
69449
|
+
dimension: "length",
|
|
69450
|
+
factor: .001,
|
|
69451
|
+
offset: 0
|
|
69452
|
+
},
|
|
69453
|
+
cm: {
|
|
69454
|
+
dimension: "length",
|
|
69455
|
+
factor: .01,
|
|
69456
|
+
offset: 0
|
|
69457
|
+
},
|
|
69458
|
+
km: {
|
|
69459
|
+
dimension: "length",
|
|
69460
|
+
factor: 1e3,
|
|
69461
|
+
offset: 0
|
|
69462
|
+
},
|
|
69463
|
+
in: {
|
|
69464
|
+
dimension: "length",
|
|
69465
|
+
factor: .0254,
|
|
69466
|
+
offset: 0
|
|
69467
|
+
},
|
|
69468
|
+
ft: {
|
|
69469
|
+
dimension: "length",
|
|
69470
|
+
factor: .3048,
|
|
69471
|
+
offset: 0
|
|
69472
|
+
},
|
|
69473
|
+
mi: {
|
|
69474
|
+
dimension: "length",
|
|
69475
|
+
factor: 1609.344,
|
|
69476
|
+
offset: 0
|
|
69477
|
+
},
|
|
69478
|
+
"m²": {
|
|
69479
|
+
dimension: "area",
|
|
69480
|
+
factor: 1,
|
|
69481
|
+
offset: 0
|
|
69482
|
+
},
|
|
69483
|
+
lx: {
|
|
69484
|
+
dimension: "illuminance",
|
|
69485
|
+
factor: 1,
|
|
69486
|
+
offset: 0
|
|
69487
|
+
},
|
|
69488
|
+
"W/m²": {
|
|
69489
|
+
dimension: "irradiance",
|
|
69490
|
+
factor: 1,
|
|
69491
|
+
offset: 0
|
|
69492
|
+
},
|
|
69493
|
+
W: {
|
|
69494
|
+
dimension: "power",
|
|
69495
|
+
factor: 1,
|
|
69496
|
+
offset: 0
|
|
69497
|
+
},
|
|
69498
|
+
kW: {
|
|
69499
|
+
dimension: "power",
|
|
69500
|
+
factor: 1e3,
|
|
69501
|
+
offset: 0
|
|
69502
|
+
},
|
|
69503
|
+
VA: {
|
|
69504
|
+
dimension: "apparent-power",
|
|
69505
|
+
factor: 1,
|
|
69506
|
+
offset: 0
|
|
69507
|
+
},
|
|
69508
|
+
Wh: {
|
|
69509
|
+
dimension: "energy",
|
|
69510
|
+
factor: 1,
|
|
69511
|
+
offset: 0
|
|
69512
|
+
},
|
|
69513
|
+
kWh: {
|
|
69514
|
+
dimension: "energy",
|
|
69515
|
+
factor: 1e3,
|
|
69516
|
+
offset: 0
|
|
69517
|
+
},
|
|
69518
|
+
MWh: {
|
|
69519
|
+
dimension: "energy",
|
|
69520
|
+
factor: 1e6,
|
|
69521
|
+
offset: 0
|
|
69522
|
+
},
|
|
69523
|
+
V: {
|
|
69524
|
+
dimension: "voltage",
|
|
69525
|
+
factor: 1,
|
|
69526
|
+
offset: 0
|
|
69527
|
+
},
|
|
69528
|
+
mV: {
|
|
69529
|
+
dimension: "voltage",
|
|
69530
|
+
factor: .001,
|
|
69531
|
+
offset: 0
|
|
69532
|
+
},
|
|
69533
|
+
A: {
|
|
69534
|
+
dimension: "current",
|
|
69535
|
+
factor: 1,
|
|
69536
|
+
offset: 0
|
|
69537
|
+
},
|
|
69538
|
+
mA: {
|
|
69539
|
+
dimension: "current",
|
|
69540
|
+
factor: .001,
|
|
69541
|
+
offset: 0
|
|
69542
|
+
},
|
|
69543
|
+
ppm: {
|
|
69544
|
+
dimension: "concentration-volume",
|
|
69545
|
+
factor: 1,
|
|
69546
|
+
offset: 0
|
|
69547
|
+
},
|
|
69548
|
+
ppb: {
|
|
69549
|
+
dimension: "concentration-volume",
|
|
69550
|
+
factor: .001,
|
|
69551
|
+
offset: 0
|
|
69552
|
+
},
|
|
69553
|
+
"µg/m³": {
|
|
69554
|
+
dimension: "concentration-mass",
|
|
69555
|
+
factor: 1,
|
|
69556
|
+
offset: 0
|
|
69557
|
+
},
|
|
69558
|
+
"mg/m³": {
|
|
69559
|
+
dimension: "concentration-mass",
|
|
69560
|
+
factor: 1e3,
|
|
69561
|
+
offset: 0
|
|
69562
|
+
},
|
|
69563
|
+
kg: {
|
|
69564
|
+
dimension: "mass",
|
|
69565
|
+
factor: 1,
|
|
69566
|
+
offset: 0
|
|
69567
|
+
},
|
|
69568
|
+
g: {
|
|
69569
|
+
dimension: "mass",
|
|
69570
|
+
factor: .001,
|
|
69571
|
+
offset: 0
|
|
69572
|
+
},
|
|
69573
|
+
lb: {
|
|
69574
|
+
dimension: "mass",
|
|
69575
|
+
factor: .453592,
|
|
69576
|
+
offset: 0
|
|
69577
|
+
},
|
|
69578
|
+
oz: {
|
|
69579
|
+
dimension: "mass",
|
|
69580
|
+
factor: .0283495,
|
|
69581
|
+
offset: 0
|
|
69582
|
+
},
|
|
69583
|
+
"%": {
|
|
69584
|
+
dimension: "percentage",
|
|
69585
|
+
factor: 1,
|
|
69586
|
+
offset: 0
|
|
69587
|
+
},
|
|
69588
|
+
L: {
|
|
69589
|
+
dimension: "volume",
|
|
69590
|
+
factor: 1,
|
|
69591
|
+
offset: 0
|
|
69592
|
+
},
|
|
69593
|
+
mL: {
|
|
69594
|
+
dimension: "volume",
|
|
69595
|
+
factor: .001,
|
|
69596
|
+
offset: 0
|
|
69597
|
+
},
|
|
69598
|
+
gal: {
|
|
69599
|
+
dimension: "volume",
|
|
69600
|
+
factor: 3.785411784,
|
|
69601
|
+
offset: 0
|
|
69602
|
+
},
|
|
69603
|
+
s: {
|
|
69604
|
+
dimension: "duration",
|
|
69605
|
+
factor: 1,
|
|
69606
|
+
offset: 0
|
|
69607
|
+
},
|
|
69608
|
+
ms: {
|
|
69609
|
+
dimension: "duration",
|
|
69610
|
+
factor: .001,
|
|
69611
|
+
offset: 0
|
|
69612
|
+
},
|
|
69613
|
+
min: {
|
|
69614
|
+
dimension: "duration",
|
|
69615
|
+
factor: 60,
|
|
69616
|
+
offset: 0
|
|
69617
|
+
},
|
|
69618
|
+
h: {
|
|
69619
|
+
dimension: "duration",
|
|
69620
|
+
factor: 3600,
|
|
69621
|
+
offset: 0
|
|
69622
|
+
},
|
|
69623
|
+
d: {
|
|
69624
|
+
dimension: "duration",
|
|
69625
|
+
factor: 86400,
|
|
69626
|
+
offset: 0
|
|
69627
|
+
},
|
|
69628
|
+
B: {
|
|
69629
|
+
dimension: "information",
|
|
69630
|
+
factor: 1,
|
|
69631
|
+
offset: 0
|
|
69632
|
+
},
|
|
69633
|
+
KiB: {
|
|
69634
|
+
dimension: "information",
|
|
69635
|
+
factor: 1024,
|
|
69636
|
+
offset: 0
|
|
69637
|
+
},
|
|
69638
|
+
MiB: {
|
|
69639
|
+
dimension: "information",
|
|
69640
|
+
factor: 1024 ** 2,
|
|
69641
|
+
offset: 0
|
|
69642
|
+
},
|
|
69643
|
+
GiB: {
|
|
69644
|
+
dimension: "information",
|
|
69645
|
+
factor: 1024 ** 3,
|
|
69646
|
+
offset: 0
|
|
69647
|
+
},
|
|
69648
|
+
TiB: {
|
|
69649
|
+
dimension: "information",
|
|
69650
|
+
factor: 1024 ** 4,
|
|
69651
|
+
offset: 0
|
|
69652
|
+
},
|
|
69653
|
+
kB: {
|
|
69654
|
+
dimension: "information",
|
|
69655
|
+
factor: 1e3,
|
|
69656
|
+
offset: 0
|
|
69657
|
+
},
|
|
69658
|
+
MB: {
|
|
69659
|
+
dimension: "information",
|
|
69660
|
+
factor: 1e6,
|
|
69661
|
+
offset: 0
|
|
69662
|
+
},
|
|
69663
|
+
GB: {
|
|
69664
|
+
dimension: "information",
|
|
69665
|
+
factor: 1e9,
|
|
69666
|
+
offset: 0
|
|
69667
|
+
},
|
|
69668
|
+
"°": {
|
|
69669
|
+
dimension: "angle",
|
|
69670
|
+
factor: 1,
|
|
69671
|
+
offset: 0
|
|
69672
|
+
},
|
|
69673
|
+
dBFS: {
|
|
69674
|
+
dimension: "sound-level",
|
|
69675
|
+
factor: 1,
|
|
69676
|
+
offset: 0
|
|
69677
|
+
},
|
|
69678
|
+
dBm: {
|
|
69679
|
+
dimension: "signal-level",
|
|
69680
|
+
factor: 1,
|
|
69681
|
+
offset: 0
|
|
69682
|
+
},
|
|
69683
|
+
mired: {
|
|
69684
|
+
dimension: "colour-temperature",
|
|
69685
|
+
factor: 1,
|
|
69686
|
+
offset: 0
|
|
69687
|
+
},
|
|
69688
|
+
kbps: {
|
|
69689
|
+
dimension: "bitrate",
|
|
69690
|
+
factor: 1,
|
|
69691
|
+
offset: 0
|
|
69692
|
+
},
|
|
69693
|
+
Mbps: {
|
|
69694
|
+
dimension: "bitrate",
|
|
69695
|
+
factor: 1e3,
|
|
69696
|
+
offset: 0
|
|
69697
|
+
},
|
|
69698
|
+
fps: {
|
|
69699
|
+
dimension: "rate",
|
|
69700
|
+
factor: 1,
|
|
69701
|
+
offset: 0
|
|
69702
|
+
}
|
|
69703
|
+
};
|
|
69704
|
+
/** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
|
|
69705
|
+
function specFor(unit) {
|
|
69706
|
+
return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
|
|
69707
|
+
}
|
|
69708
|
+
/** The dimension a unit belongs to, or `undefined` for an unknown unit. */
|
|
69709
|
+
function unitDimension(unit) {
|
|
69710
|
+
return specFor(unit)?.dimension;
|
|
69711
|
+
}
|
|
69712
|
+
/** Value in `spec`'s unit → its dimension's canonical unit. */
|
|
69713
|
+
function toCanonical(value, spec) {
|
|
69714
|
+
return value * spec.factor + spec.offset;
|
|
69715
|
+
}
|
|
69716
|
+
/** Canonical-unit value → the unit described by `spec`. */
|
|
69717
|
+
function fromCanonical(canonical, spec) {
|
|
69718
|
+
return (canonical - spec.offset) / spec.factor;
|
|
69719
|
+
}
|
|
69720
|
+
/**
|
|
69721
|
+
* Graceful variant for the render path: returns the converted value, or
|
|
69722
|
+
* `undefined` when the conversion is refused (unknown unit / cross-dimension).
|
|
69723
|
+
* Never throws — a render caller falls back to the raw value + source unit.
|
|
69724
|
+
*/
|
|
69725
|
+
function tryConvertUnit(value, from, to) {
|
|
69726
|
+
if (from === to) return value;
|
|
69727
|
+
const fromSpec = specFor(from);
|
|
69728
|
+
if (!fromSpec) return void 0;
|
|
69729
|
+
const toSpec = specFor(to);
|
|
69730
|
+
if (!toSpec) return void 0;
|
|
69731
|
+
if (fromSpec.dimension !== toSpec.dimension) return void 0;
|
|
69732
|
+
return fromCanonical(toCanonical(value, fromSpec), toSpec);
|
|
69733
|
+
}
|
|
69410
69734
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
69411
69735
|
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Camera, DeviceType.Camera, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
69412
69736
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
@@ -84076,7 +84400,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84076
84400
|
field: "value",
|
|
84077
84401
|
quantity: t("the reading", "il valore"),
|
|
84078
84402
|
label: t("Reading", "Valore"),
|
|
84079
|
-
unitField: "unit",
|
|
84080
84403
|
icon: "gauge"
|
|
84081
84404
|
}]
|
|
84082
84405
|
},
|
|
@@ -84212,7 +84535,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84212
84535
|
field: "temperature",
|
|
84213
84536
|
quantity: t("the outdoor temperature", "la temperatura esterna"),
|
|
84214
84537
|
label: t("Temperature", "Temperatura"),
|
|
84215
|
-
|
|
84538
|
+
unit: "°C",
|
|
84216
84539
|
icon: "thermometer"
|
|
84217
84540
|
},
|
|
84218
84541
|
{
|
|
@@ -84228,7 +84551,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84228
84551
|
field: "windSpeed",
|
|
84229
84552
|
quantity: t("the wind speed", "la velocità del vento"),
|
|
84230
84553
|
label: t("Wind", "Vento"),
|
|
84231
|
-
|
|
84554
|
+
unit: "m/s",
|
|
84232
84555
|
icon: "wind"
|
|
84233
84556
|
},
|
|
84234
84557
|
{
|
|
@@ -84236,7 +84559,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84236
84559
|
field: "pressure",
|
|
84237
84560
|
quantity: t("the air pressure", "la pressione atmosferica"),
|
|
84238
84561
|
label: t("Pressure", "Pressione"),
|
|
84239
|
-
|
|
84562
|
+
unit: "hPa",
|
|
84240
84563
|
icon: "gauge"
|
|
84241
84564
|
}
|
|
84242
84565
|
]
|
|
@@ -87373,6 +87696,11 @@ var PulseOverrideDraftSchema = object({
|
|
|
87373
87696
|
targetId: string().min(1),
|
|
87374
87697
|
enabled: boolean(),
|
|
87375
87698
|
debounceMs: number().nonnegative(),
|
|
87699
|
+
edges: array(_enum([
|
|
87700
|
+
"rising",
|
|
87701
|
+
"button",
|
|
87702
|
+
"native-event"
|
|
87703
|
+
])).min(1).optional(),
|
|
87376
87704
|
sources: array(OverrideSourceSchema).min(1)
|
|
87377
87705
|
});
|
|
87378
87706
|
var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([
|
|
@@ -88597,6 +88925,96 @@ DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
|
88597
88925
|
DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
|
|
88598
88926
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
88599
88927
|
/**
|
|
88928
|
+
* Convert an upstream number into the stored base of its quantity (D944 §4).
|
|
88929
|
+
* An unknown unit or a unit from another dimension is a refusal: the caller
|
|
88930
|
+
* logs it and publishes null (D393). This function never guesses.
|
|
88931
|
+
*/
|
|
88932
|
+
var DIMENSION_QUANTITY = {
|
|
88933
|
+
temperature: "temperature",
|
|
88934
|
+
pressure: "pressure",
|
|
88935
|
+
speed: "speed",
|
|
88936
|
+
"precipitation-rate": "precipitation-rate",
|
|
88937
|
+
length: "length",
|
|
88938
|
+
area: "area",
|
|
88939
|
+
illuminance: "illuminance",
|
|
88940
|
+
irradiance: "irradiance",
|
|
88941
|
+
power: "power",
|
|
88942
|
+
"apparent-power": "apparent-power",
|
|
88943
|
+
energy: "energy",
|
|
88944
|
+
voltage: "voltage",
|
|
88945
|
+
current: "current",
|
|
88946
|
+
"concentration-volume": "concentration-volume",
|
|
88947
|
+
"concentration-mass": "concentration-mass",
|
|
88948
|
+
mass: "mass",
|
|
88949
|
+
percentage: "percentage",
|
|
88950
|
+
volume: "volume",
|
|
88951
|
+
duration: "duration",
|
|
88952
|
+
information: "information",
|
|
88953
|
+
angle: "angle",
|
|
88954
|
+
"sound-level": "sound-level",
|
|
88955
|
+
"signal-level": "signal-level",
|
|
88956
|
+
"colour-temperature": "colour-temperature",
|
|
88957
|
+
bitrate: "bitrate",
|
|
88958
|
+
rate: "rate"
|
|
88959
|
+
};
|
|
88960
|
+
/**
|
|
88961
|
+
* Quantity implied by a unit symbol. `ppb` and `ms` are the two symbols
|
|
88962
|
+
* whose stored base is not the dimension's canonical unit. `mm` is length;
|
|
88963
|
+
* precipitation is chosen by the caller (a device class), not by the symbol.
|
|
88964
|
+
*/
|
|
88965
|
+
function quantityFromUnit(rawUnit) {
|
|
88966
|
+
const unit = normalizeUnit(rawUnit);
|
|
88967
|
+
if (unit === void 0) return void 0;
|
|
88968
|
+
if (unit === "ppb") return "concentration-ppb";
|
|
88969
|
+
if (unit === "ms") return "duration-ms";
|
|
88970
|
+
const dimension = unitDimension(unit);
|
|
88971
|
+
if (dimension === void 0) return void 0;
|
|
88972
|
+
return DIMENSION_QUANTITY[dimension];
|
|
88973
|
+
}
|
|
88974
|
+
function ingestReading(input) {
|
|
88975
|
+
if (!Number.isFinite(input.value)) return {
|
|
88976
|
+
ok: false,
|
|
88977
|
+
reason: "non-finite"
|
|
88978
|
+
};
|
|
88979
|
+
if (input.quantity === "device") return {
|
|
88980
|
+
ok: false,
|
|
88981
|
+
reason: "unknown-unit"
|
|
88982
|
+
};
|
|
88983
|
+
const raw = input.rawUnit?.trim();
|
|
88984
|
+
if (raw === void 0 || raw.length === 0) return {
|
|
88985
|
+
ok: true,
|
|
88986
|
+
value: input.value
|
|
88987
|
+
};
|
|
88988
|
+
const unit = normalizeUnit(raw);
|
|
88989
|
+
if (unit === void 0) return {
|
|
88990
|
+
ok: false,
|
|
88991
|
+
reason: "unknown-unit"
|
|
88992
|
+
};
|
|
88993
|
+
const base = baseUnitOf(input.quantity);
|
|
88994
|
+
if (base === void 0) {
|
|
88995
|
+
if (unitDimension(unit) !== void 0) return {
|
|
88996
|
+
ok: false,
|
|
88997
|
+
reason: "cross-dimension"
|
|
88998
|
+
};
|
|
88999
|
+
return {
|
|
89000
|
+
ok: false,
|
|
89001
|
+
reason: "unknown-unit"
|
|
89002
|
+
};
|
|
89003
|
+
}
|
|
89004
|
+
const converted = tryConvertUnit(input.value, unit, base);
|
|
89005
|
+
if (converted === void 0) return unitDimension(unit) === void 0 ? {
|
|
89006
|
+
ok: false,
|
|
89007
|
+
reason: "unknown-unit"
|
|
89008
|
+
} : {
|
|
89009
|
+
ok: false,
|
|
89010
|
+
reason: "cross-dimension"
|
|
89011
|
+
};
|
|
89012
|
+
return {
|
|
89013
|
+
ok: true,
|
|
89014
|
+
value: converted
|
|
89015
|
+
};
|
|
89016
|
+
}
|
|
89017
|
+
/**
|
|
88600
89018
|
* Pure fuzzy matcher for adoption location import. Normalized
|
|
88601
89019
|
* case-insensitive Levenshtein similarity, used to reuse an existing
|
|
88602
89020
|
* CamStack location when an imported source location (e.g. an HA area) is
|
|
@@ -114231,11 +114649,7 @@ var hmDiagnosticSchema = hmChildSchema.extend({
|
|
|
114231
114649
|
sourceParams: array(string()).min(1)
|
|
114232
114650
|
});
|
|
114233
114651
|
/** Cold-start slices keyed by cap. */
|
|
114234
|
-
var COLD_NUMERIC =
|
|
114235
|
-
value: 0,
|
|
114236
|
-
unit,
|
|
114237
|
-
lastFetchedAt: 0
|
|
114238
|
-
} : {
|
|
114652
|
+
var COLD_NUMERIC = {
|
|
114239
114653
|
value: 0,
|
|
114240
114654
|
lastFetchedAt: 0
|
|
114241
114655
|
};
|
|
@@ -114279,7 +114693,7 @@ var HmDiagnosticDevice = class extends HmChildDevice {
|
|
|
114279
114693
|
this.runtimeState.setCapState("temperature-sensor", COLD_TEMP);
|
|
114280
114694
|
return;
|
|
114281
114695
|
default: {
|
|
114282
|
-
const cold = COLD_NUMERIC
|
|
114696
|
+
const cold = COLD_NUMERIC;
|
|
114283
114697
|
this.ctx.registerNativeCap(numericSensorCapability, { getStatus: async () => this.runtimeState.getCapState("numeric-sensor") ?? cold });
|
|
114284
114698
|
this.runtimeState.setCapState("numeric-sensor", cold);
|
|
114285
114699
|
return;
|
|
@@ -114304,22 +114718,55 @@ var HmDiagnosticDevice = class extends HmChildDevice {
|
|
|
114304
114718
|
return;
|
|
114305
114719
|
}
|
|
114306
114720
|
if (typeof value !== "number") return;
|
|
114721
|
+
const stored = this.toBase(value);
|
|
114722
|
+
if (stored === null) return;
|
|
114307
114723
|
if (this.capName === "temperature-sensor") {
|
|
114308
114724
|
this.runtimeState.setCapState("temperature-sensor", {
|
|
114309
|
-
celsius:
|
|
114725
|
+
celsius: stored,
|
|
114310
114726
|
lastFetchedAt: now
|
|
114311
114727
|
});
|
|
114312
114728
|
return;
|
|
114313
114729
|
}
|
|
114314
|
-
|
|
114315
|
-
value,
|
|
114316
|
-
unit: this.unit,
|
|
114730
|
+
this.runtimeState.setCapState("numeric-sensor", {
|
|
114731
|
+
value: stored,
|
|
114317
114732
|
lastFetchedAt: now
|
|
114318
|
-
}
|
|
114733
|
+
});
|
|
114734
|
+
}
|
|
114735
|
+
/** Upstream diagnostic number → the quantity's base. Unknown units publish nothing. */
|
|
114736
|
+
toBase(value) {
|
|
114737
|
+
const quantity = this.readingQuantity();
|
|
114738
|
+
if (quantity === null) {
|
|
114739
|
+
this.warnRefusal("unknown-unit");
|
|
114740
|
+
return null;
|
|
114741
|
+
}
|
|
114742
|
+
const result = ingestReading({
|
|
114319
114743
|
value,
|
|
114320
|
-
|
|
114321
|
-
|
|
114322
|
-
|
|
114744
|
+
rawUnit: this.unit,
|
|
114745
|
+
quantity
|
|
114746
|
+
});
|
|
114747
|
+
if (!result.ok) {
|
|
114748
|
+
this.warnRefusal(result.reason);
|
|
114749
|
+
return null;
|
|
114750
|
+
}
|
|
114751
|
+
if (this.capName === "numeric-sensor" && this.sourceInfo.quantity !== quantity) this.updateSourceInfo({ quantity });
|
|
114752
|
+
return result.value;
|
|
114753
|
+
}
|
|
114754
|
+
readingQuantity() {
|
|
114755
|
+
if (this.capName === "temperature-sensor") return "temperature";
|
|
114756
|
+
if (this.unit === void 0) return "none";
|
|
114757
|
+
const learned = quantityFromUnit(this.unit);
|
|
114758
|
+
if (learned === void 0 || learned === "device") return null;
|
|
114759
|
+
return learned;
|
|
114760
|
+
}
|
|
114761
|
+
warnRefusal(reason) {
|
|
114762
|
+
this.ctx.logger.warn("numeric reading refused", {
|
|
114763
|
+
tags: { deviceId: this.id },
|
|
114764
|
+
meta: {
|
|
114765
|
+
quantity: this.readingQuantity() ?? "device",
|
|
114766
|
+
unit: this.unit ?? "",
|
|
114767
|
+
reason
|
|
114768
|
+
}
|
|
114769
|
+
});
|
|
114323
114770
|
}
|
|
114324
114771
|
};
|
|
114325
114772
|
//#endregion
|