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