@camstack/addon-provider-ecowitt 0.2.146 → 0.2.147
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 +994 -543
- package/dist/addon.mjs +994 -543
- package/package.json +1 -1
package/dist/addon.mjs
CHANGED
|
@@ -5436,7 +5436,7 @@ var ZodIssueCode$1 = {
|
|
|
5436
5436
|
var ZodFirstPartyTypeKind$1;
|
|
5437
5437
|
ZodFirstPartyTypeKind$1 || (ZodFirstPartyTypeKind$1 = {});
|
|
5438
5438
|
//#endregion
|
|
5439
|
-
//#region ../types/dist/sleep-
|
|
5439
|
+
//#region ../types/dist/sleep-CARd6wZF.mjs
|
|
5440
5440
|
/**
|
|
5441
5441
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5442
5442
|
* window to float samples (D455).
|
|
@@ -7990,6 +7990,99 @@ var RecordKindDeclarationSchema = object({
|
|
|
7990
7990
|
}).strict();
|
|
7991
7991
|
/** The kind names: lower-case, dash separated, short enough for an index name. */
|
|
7992
7992
|
var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
|
|
7993
|
+
var READING_QUANTITIES = [
|
|
7994
|
+
"temperature",
|
|
7995
|
+
"pressure",
|
|
7996
|
+
"speed",
|
|
7997
|
+
"precipitation",
|
|
7998
|
+
"precipitation-rate",
|
|
7999
|
+
"length",
|
|
8000
|
+
"area",
|
|
8001
|
+
"illuminance",
|
|
8002
|
+
"irradiance",
|
|
8003
|
+
"power",
|
|
8004
|
+
"energy",
|
|
8005
|
+
"voltage",
|
|
8006
|
+
"current",
|
|
8007
|
+
"apparent-power",
|
|
8008
|
+
"humidity",
|
|
8009
|
+
"percentage",
|
|
8010
|
+
"concentration-volume",
|
|
8011
|
+
"concentration-mass",
|
|
8012
|
+
"concentration-ppb",
|
|
8013
|
+
"volume",
|
|
8014
|
+
"duration",
|
|
8015
|
+
"duration-ms",
|
|
8016
|
+
"information",
|
|
8017
|
+
"sound-level",
|
|
8018
|
+
"signal-level",
|
|
8019
|
+
"colour-temperature",
|
|
8020
|
+
"angle",
|
|
8021
|
+
"bitrate",
|
|
8022
|
+
"rate",
|
|
8023
|
+
"mass",
|
|
8024
|
+
"index",
|
|
8025
|
+
"ratio",
|
|
8026
|
+
"count",
|
|
8027
|
+
"none",
|
|
8028
|
+
"device"
|
|
8029
|
+
];
|
|
8030
|
+
var ReadingQuantitySchema = _enum(READING_QUANTITIES);
|
|
8031
|
+
new Set(READING_QUANTITIES);
|
|
8032
|
+
/**
|
|
8033
|
+
* Stored unit symbol for a quantity. `none` means the number is dimensionless
|
|
8034
|
+
* (or an index / ratio / count). `device` means the cap has no single unit.
|
|
8035
|
+
*/
|
|
8036
|
+
var QUANTITY_BASE = {
|
|
8037
|
+
temperature: "°C",
|
|
8038
|
+
pressure: "hPa",
|
|
8039
|
+
speed: "m/s",
|
|
8040
|
+
precipitation: "mm",
|
|
8041
|
+
"precipitation-rate": "mm/h",
|
|
8042
|
+
length: "m",
|
|
8043
|
+
area: "m²",
|
|
8044
|
+
illuminance: "lx",
|
|
8045
|
+
irradiance: "W/m²",
|
|
8046
|
+
power: "W",
|
|
8047
|
+
energy: "kWh",
|
|
8048
|
+
voltage: "V",
|
|
8049
|
+
current: "A",
|
|
8050
|
+
"apparent-power": "VA",
|
|
8051
|
+
humidity: "%",
|
|
8052
|
+
percentage: "%",
|
|
8053
|
+
"concentration-volume": "ppm",
|
|
8054
|
+
"concentration-mass": "µg/m³",
|
|
8055
|
+
"concentration-ppb": "ppb",
|
|
8056
|
+
volume: "L",
|
|
8057
|
+
duration: "s",
|
|
8058
|
+
"duration-ms": "ms",
|
|
8059
|
+
information: "B",
|
|
8060
|
+
"sound-level": "dBFS",
|
|
8061
|
+
"signal-level": "dBm",
|
|
8062
|
+
"colour-temperature": "mired",
|
|
8063
|
+
angle: "°",
|
|
8064
|
+
bitrate: "kbps",
|
|
8065
|
+
rate: "fps",
|
|
8066
|
+
mass: "kg",
|
|
8067
|
+
index: "none",
|
|
8068
|
+
ratio: "none",
|
|
8069
|
+
count: "none",
|
|
8070
|
+
none: "none",
|
|
8071
|
+
device: "device"
|
|
8072
|
+
};
|
|
8073
|
+
/** The stored unit, or `undefined` when the quantity has no unit symbol. */
|
|
8074
|
+
function baseUnitOf(quantity) {
|
|
8075
|
+
const symbol = QUANTITY_BASE[quantity];
|
|
8076
|
+
if (symbol === "none" || symbol === "device") return void 0;
|
|
8077
|
+
return symbol;
|
|
8078
|
+
}
|
|
8079
|
+
function readingMeta(quantity, extras) {
|
|
8080
|
+
return {
|
|
8081
|
+
quantity,
|
|
8082
|
+
...extras?.precision !== void 0 ? { precision: extras.precision } : {},
|
|
8083
|
+
...extras?.label !== void 0 ? { label: extras.label } : {}
|
|
8084
|
+
};
|
|
8085
|
+
}
|
|
7993
8086
|
/** Shared Zod schemas used across streaming capabilities. */
|
|
7994
8087
|
var CamProfileSchema = _enum([
|
|
7995
8088
|
"high",
|
|
@@ -8017,8 +8110,8 @@ var CamStreamKindSchema = _enum([
|
|
|
8017
8110
|
"derived"
|
|
8018
8111
|
]);
|
|
8019
8112
|
var CamStreamResolutionSchema = object({
|
|
8020
|
-
width: number().int().positive(),
|
|
8021
|
-
height: number().int().positive()
|
|
8113
|
+
width: number().int().positive().meta(readingMeta("none")),
|
|
8114
|
+
height: number().int().positive().meta(readingMeta("none"))
|
|
8022
8115
|
});
|
|
8023
8116
|
var CameraStreamSchema = object({
|
|
8024
8117
|
/** Stable, provider-assigned id unique within the (deviceId) scope. */
|
|
@@ -8665,14 +8758,6 @@ Object.freeze({
|
|
|
8665
8758
|
{
|
|
8666
8759
|
"path": "aqi",
|
|
8667
8760
|
"valueType": "number"
|
|
8668
|
-
},
|
|
8669
|
-
{
|
|
8670
|
-
"path": "unit",
|
|
8671
|
-
"valueType": "string"
|
|
8672
|
-
},
|
|
8673
|
-
{
|
|
8674
|
-
"path": "precision",
|
|
8675
|
-
"valueType": "number"
|
|
8676
8761
|
}
|
|
8677
8762
|
],
|
|
8678
8763
|
"alarm-panel": [
|
|
@@ -8708,20 +8793,10 @@ Object.freeze({
|
|
|
8708
8793
|
"valueType": "boolean"
|
|
8709
8794
|
}
|
|
8710
8795
|
],
|
|
8711
|
-
"ambient-light-sensor": [
|
|
8712
|
-
|
|
8713
|
-
|
|
8714
|
-
|
|
8715
|
-
},
|
|
8716
|
-
{
|
|
8717
|
-
"path": "unit",
|
|
8718
|
-
"valueType": "string"
|
|
8719
|
-
},
|
|
8720
|
-
{
|
|
8721
|
-
"path": "precision",
|
|
8722
|
-
"valueType": "number"
|
|
8723
|
-
}
|
|
8724
|
-
],
|
|
8796
|
+
"ambient-light-sensor": [{
|
|
8797
|
+
"path": "lux",
|
|
8798
|
+
"valueType": "number"
|
|
8799
|
+
}],
|
|
8725
8800
|
"audio-metrics": [
|
|
8726
8801
|
{
|
|
8727
8802
|
"path": "ts",
|
|
@@ -8801,6 +8876,10 @@ Object.freeze({
|
|
|
8801
8876
|
"path": "lastUpdated",
|
|
8802
8877
|
"valueType": "number"
|
|
8803
8878
|
},
|
|
8879
|
+
{
|
|
8880
|
+
"path": "volts",
|
|
8881
|
+
"valueType": "number"
|
|
8882
|
+
},
|
|
8804
8883
|
{
|
|
8805
8884
|
"path": "lastContactAt",
|
|
8806
8885
|
"valueType": "number"
|
|
@@ -9043,10 +9122,6 @@ Object.freeze({
|
|
|
9043
9122
|
"datetime"
|
|
9044
9123
|
]
|
|
9045
9124
|
},
|
|
9046
|
-
{
|
|
9047
|
-
"path": "unit",
|
|
9048
|
-
"valueType": "string"
|
|
9049
|
-
},
|
|
9050
9125
|
{
|
|
9051
9126
|
"path": "min",
|
|
9052
9127
|
"valueType": "number"
|
|
@@ -9058,19 +9133,6 @@ Object.freeze({
|
|
|
9058
9133
|
{
|
|
9059
9134
|
"path": "step",
|
|
9060
9135
|
"valueType": "number"
|
|
9061
|
-
},
|
|
9062
|
-
{
|
|
9063
|
-
"path": "precision",
|
|
9064
|
-
"valueType": "number"
|
|
9065
|
-
},
|
|
9066
|
-
{
|
|
9067
|
-
"path": "format",
|
|
9068
|
-
"valueType": "enum",
|
|
9069
|
-
"enumValues": [
|
|
9070
|
-
"date",
|
|
9071
|
-
"time",
|
|
9072
|
-
"datetime"
|
|
9073
|
-
]
|
|
9074
9136
|
}
|
|
9075
9137
|
],
|
|
9076
9138
|
"cover": [
|
|
@@ -9311,13 +9373,8 @@ Object.freeze({
|
|
|
9311
9373
|
"path": "value",
|
|
9312
9374
|
"valueType": "string"
|
|
9313
9375
|
}, {
|
|
9314
|
-
"path": "
|
|
9315
|
-
"valueType": "
|
|
9316
|
-
"enumValues": [
|
|
9317
|
-
"date",
|
|
9318
|
-
"time",
|
|
9319
|
-
"datetime"
|
|
9320
|
-
]
|
|
9376
|
+
"path": "timestamp",
|
|
9377
|
+
"valueType": "boolean"
|
|
9321
9378
|
}],
|
|
9322
9379
|
"event-emitter": [
|
|
9323
9380
|
{
|
|
@@ -9424,20 +9481,10 @@ Object.freeze({
|
|
|
9424
9481
|
"valueType": "number"
|
|
9425
9482
|
}
|
|
9426
9483
|
],
|
|
9427
|
-
"humidity-sensor": [
|
|
9428
|
-
|
|
9429
|
-
|
|
9430
|
-
|
|
9431
|
-
},
|
|
9432
|
-
{
|
|
9433
|
-
"path": "unit",
|
|
9434
|
-
"valueType": "string"
|
|
9435
|
-
},
|
|
9436
|
-
{
|
|
9437
|
-
"path": "precision",
|
|
9438
|
-
"valueType": "number"
|
|
9439
|
-
}
|
|
9440
|
-
],
|
|
9484
|
+
"humidity-sensor": [{
|
|
9485
|
+
"path": "percent",
|
|
9486
|
+
"valueType": "number"
|
|
9487
|
+
}],
|
|
9441
9488
|
"image": [{
|
|
9442
9489
|
"path": "url",
|
|
9443
9490
|
"valueType": "string"
|
|
@@ -9776,20 +9823,10 @@ Object.freeze({
|
|
|
9776
9823
|
"valueType": "boolean"
|
|
9777
9824
|
}
|
|
9778
9825
|
],
|
|
9779
|
-
"numeric-sensor": [
|
|
9780
|
-
|
|
9781
|
-
|
|
9782
|
-
|
|
9783
|
-
},
|
|
9784
|
-
{
|
|
9785
|
-
"path": "unit",
|
|
9786
|
-
"valueType": "string"
|
|
9787
|
-
},
|
|
9788
|
-
{
|
|
9789
|
-
"path": "precision",
|
|
9790
|
-
"valueType": "number"
|
|
9791
|
-
}
|
|
9792
|
-
],
|
|
9826
|
+
"numeric-sensor": [{
|
|
9827
|
+
"path": "value",
|
|
9828
|
+
"valueType": "number"
|
|
9829
|
+
}],
|
|
9793
9830
|
"pet-feeder": [
|
|
9794
9831
|
{
|
|
9795
9832
|
"path": "foodLevel",
|
|
@@ -9881,14 +9918,6 @@ Object.freeze({
|
|
|
9881
9918
|
{
|
|
9882
9919
|
"path": "powerFactor",
|
|
9883
9920
|
"valueType": "number"
|
|
9884
|
-
},
|
|
9885
|
-
{
|
|
9886
|
-
"path": "unit",
|
|
9887
|
-
"valueType": "string"
|
|
9888
|
-
},
|
|
9889
|
-
{
|
|
9890
|
-
"path": "precision",
|
|
9891
|
-
"valueType": "number"
|
|
9892
9921
|
}
|
|
9893
9922
|
],
|
|
9894
9923
|
"presence": [
|
|
@@ -9917,20 +9946,10 @@ Object.freeze({
|
|
|
9917
9946
|
"valueType": "number"
|
|
9918
9947
|
}
|
|
9919
9948
|
],
|
|
9920
|
-
"pressure-sensor": [
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
},
|
|
9925
|
-
{
|
|
9926
|
-
"path": "unit",
|
|
9927
|
-
"valueType": "string"
|
|
9928
|
-
},
|
|
9929
|
-
{
|
|
9930
|
-
"path": "precision",
|
|
9931
|
-
"valueType": "number"
|
|
9932
|
-
}
|
|
9933
|
-
],
|
|
9949
|
+
"pressure-sensor": [{
|
|
9950
|
+
"path": "hpa",
|
|
9951
|
+
"valueType": "number"
|
|
9952
|
+
}],
|
|
9934
9953
|
"privacy-mask": [{
|
|
9935
9954
|
"path": "enabled",
|
|
9936
9955
|
"valueType": "boolean"
|
|
@@ -10241,20 +10260,10 @@ Object.freeze({
|
|
|
10241
10260
|
"path": "tampered",
|
|
10242
10261
|
"valueType": "boolean"
|
|
10243
10262
|
}],
|
|
10244
|
-
"temperature-sensor": [
|
|
10245
|
-
|
|
10246
|
-
|
|
10247
|
-
|
|
10248
|
-
},
|
|
10249
|
-
{
|
|
10250
|
-
"path": "unit",
|
|
10251
|
-
"valueType": "string"
|
|
10252
|
-
},
|
|
10253
|
-
{
|
|
10254
|
-
"path": "precision",
|
|
10255
|
-
"valueType": "number"
|
|
10256
|
-
}
|
|
10257
|
-
],
|
|
10263
|
+
"temperature-sensor": [{
|
|
10264
|
+
"path": "celsius",
|
|
10265
|
+
"valueType": "number"
|
|
10266
|
+
}],
|
|
10258
10267
|
"update": [
|
|
10259
10268
|
{
|
|
10260
10269
|
"path": "currentVersion",
|
|
@@ -10365,10 +10374,6 @@ Object.freeze({
|
|
|
10365
10374
|
"path": "temperature",
|
|
10366
10375
|
"valueType": "number"
|
|
10367
10376
|
},
|
|
10368
|
-
{
|
|
10369
|
-
"path": "temperatureUnit",
|
|
10370
|
-
"valueType": "string"
|
|
10371
|
-
},
|
|
10372
10377
|
{
|
|
10373
10378
|
"path": "humidity",
|
|
10374
10379
|
"valueType": "number"
|
|
@@ -10377,18 +10382,10 @@ Object.freeze({
|
|
|
10377
10382
|
"path": "pressure",
|
|
10378
10383
|
"valueType": "number"
|
|
10379
10384
|
},
|
|
10380
|
-
{
|
|
10381
|
-
"path": "pressureUnit",
|
|
10382
|
-
"valueType": "string"
|
|
10383
|
-
},
|
|
10384
10385
|
{
|
|
10385
10386
|
"path": "windSpeed",
|
|
10386
10387
|
"valueType": "number"
|
|
10387
10388
|
},
|
|
10388
|
-
{
|
|
10389
|
-
"path": "windSpeedUnit",
|
|
10390
|
-
"valueType": "string"
|
|
10391
|
-
},
|
|
10392
10389
|
{
|
|
10393
10390
|
"path": "windBearing",
|
|
10394
10391
|
"valueType": "number"
|
|
@@ -17866,7 +17863,8 @@ var CompositionProblemSchema = object({
|
|
|
17866
17863
|
"item-field-reserved",
|
|
17867
17864
|
"target-missing",
|
|
17868
17865
|
"target-unreadable",
|
|
17869
|
-
"not-replaceable"
|
|
17866
|
+
"not-replaceable",
|
|
17867
|
+
"native-capability-replace"
|
|
17870
17868
|
]),
|
|
17871
17869
|
path: string(),
|
|
17872
17870
|
message: string()
|
|
@@ -17924,9 +17922,11 @@ var CompositionFieldPlanSchema = object({
|
|
|
17924
17922
|
reads: array(CompositionFieldReadSchema)
|
|
17925
17923
|
});
|
|
17926
17924
|
/**
|
|
17927
|
-
* What a feature does to its target (D663): `new` builds a capability on a
|
|
17928
|
-
* device; on an existing device it `add`s a capability the device lacks
|
|
17929
|
-
*
|
|
17925
|
+
* What a feature does to its target (D663, D944): `new` builds a capability on a
|
|
17926
|
+
* new device; on an existing device it `add`s a capability the device lacks.
|
|
17927
|
+
* Replacing a capability the device already serves is refused
|
|
17928
|
+
* (`native-capability-replace`), not planned. `replace` remains on the claim
|
|
17929
|
+
* wire for a claim the hub already holds.
|
|
17930
17930
|
*/
|
|
17931
17931
|
var CompositionFeatureModeSchema = _enum([
|
|
17932
17932
|
"new",
|
|
@@ -19925,12 +19925,12 @@ function buildDeviceAdminUrl(parts) {
|
|
|
19925
19925
|
* providers detect renames by matching `uniqueId` and patch `id`
|
|
19926
19926
|
* in-place so dispatch keeps working.
|
|
19927
19927
|
*
|
|
19928
|
-
* **
|
|
19929
|
-
*
|
|
19930
|
-
*
|
|
19931
|
-
*
|
|
19932
|
-
* (`ha-domain-mapping`)
|
|
19933
|
-
* stored after that point.
|
|
19928
|
+
* **Display unit and precision are NOT stored here.** They are a read-time
|
|
19929
|
+
* preference (D944). A polymorphic reading stores its `quantity` here once,
|
|
19930
|
+
* at adopt, and a date/time value stores `dateTimeShape` the same way —
|
|
19931
|
+
* neither is repeated on each sample. The `device_class` tag is an
|
|
19932
|
+
* HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
|
|
19933
|
+
* to derive the CamStack `DeviceRole`; it is never stored after that point.
|
|
19934
19934
|
*
|
|
19935
19935
|
* The `raw` escape hatch carries provider-specific attributes that don't
|
|
19936
19936
|
* fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
|
|
@@ -19944,6 +19944,14 @@ var SourceInfoSchema = object({
|
|
|
19944
19944
|
/** Immutable upstream identifier when available (HA `unique_id`, … ).
|
|
19945
19945
|
* Used to detect rename when `id` changes. */
|
|
19946
19946
|
uniqueId: string().optional(),
|
|
19947
|
+
/** Quantity of a polymorphic reading, written once at adopt (D944). */
|
|
19948
|
+
quantity: ReadingQuantitySchema.optional(),
|
|
19949
|
+
/** Date / time / datetime shape of a string reading, written once at adopt. */
|
|
19950
|
+
dateTimeShape: _enum([
|
|
19951
|
+
"date",
|
|
19952
|
+
"time",
|
|
19953
|
+
"datetime"
|
|
19954
|
+
]).optional(),
|
|
19947
19955
|
/** Provider-specific extras that don't fit the structured slots.
|
|
19948
19956
|
* Whitelist what goes here per provider — do NOT dump entire HA
|
|
19949
19957
|
* attribute blobs (would explode the meta JSON column). */
|
|
@@ -19989,6 +19997,80 @@ function mergeSourceInfo(prev, patch) {
|
|
|
19989
19997
|
return next;
|
|
19990
19998
|
}
|
|
19991
19999
|
/**
|
|
20000
|
+
* Coerce common unit-of-measurement string variants to a canonical form
|
|
20001
|
+
* so the UI's display templates can rely on stable strings. Pure
|
|
20002
|
+
* passthrough for unknown units — kept conservative on purpose, the goal
|
|
20003
|
+
* is to normalise the long tail of HA `unit_of_measurement` values that
|
|
20004
|
+
* have multiple historical spellings (`'°C'` vs `'C'` vs `'celsius'`).
|
|
20005
|
+
*
|
|
20006
|
+
* Extend the map as we encounter real-world data. Returning the raw
|
|
20007
|
+
* input for unknown values is intentional: providers should never
|
|
20008
|
+
* lose information silently here.
|
|
20009
|
+
*/
|
|
20010
|
+
function normalizeUnit(raw) {
|
|
20011
|
+
if (raw == null) return void 0;
|
|
20012
|
+
const trimmed = raw.trim();
|
|
20013
|
+
if (trimmed === "") return void 0;
|
|
20014
|
+
const lower = trimmed.toLowerCase();
|
|
20015
|
+
if (lower === "°c" || lower === "c" || lower === "celsius") return "°C";
|
|
20016
|
+
if (lower === "°f" || lower === "f" || lower === "fahrenheit") return "°F";
|
|
20017
|
+
if (lower === "k" || lower === "kelvin") return "K";
|
|
20018
|
+
if (lower === "%" || lower === "percent" || lower === "percentage") return "%";
|
|
20019
|
+
if (lower === "lx" || lower === "lux") return "lx";
|
|
20020
|
+
if (lower === "hpa" || lower === "hectopascal") return "hPa";
|
|
20021
|
+
if (lower === "kpa") return "kPa";
|
|
20022
|
+
if (lower === "pa") return "Pa";
|
|
20023
|
+
if (lower === "mbar" || lower === "millibar") return "mbar";
|
|
20024
|
+
if (lower === "w" || lower === "watt") return "W";
|
|
20025
|
+
if (lower === "kw" || lower === "kilowatt") return "kW";
|
|
20026
|
+
if (lower === "wh" || lower === "watt-hour") return "Wh";
|
|
20027
|
+
if (lower === "kwh" || lower === "kilowatt-hour") return "kWh";
|
|
20028
|
+
if (lower === "v" || lower === "volt") return "V";
|
|
20029
|
+
if (lower === "a" || lower === "amp" || lower === "ampere") return "A";
|
|
20030
|
+
if (lower === "ma" || lower === "milliampere") return "mA";
|
|
20031
|
+
if (lower === "va") return "VA";
|
|
20032
|
+
if (lower === "ppm") return "ppm";
|
|
20033
|
+
if (lower === "ppb") return "ppb";
|
|
20034
|
+
if (lower === "µg/m³" || lower === "ug/m3" || lower === "µg/m3") return "µg/m³";
|
|
20035
|
+
if (lower === "km/h" || lower === "kph") return "km/h";
|
|
20036
|
+
if (lower === "mph") return "mph";
|
|
20037
|
+
if (lower === "m/s") return "m/s";
|
|
20038
|
+
if (lower === "m") return "m";
|
|
20039
|
+
if (lower === "m²" || lower === "m2") return "m²";
|
|
20040
|
+
if (lower === "mm") return "mm";
|
|
20041
|
+
if (lower === "cm") return "cm";
|
|
20042
|
+
if (lower === "km") return "km";
|
|
20043
|
+
if (lower === "in" || lower === "inch" || lower === "inches") return "in";
|
|
20044
|
+
if (lower === "ft" || lower === "foot" || lower === "feet") return "ft";
|
|
20045
|
+
if (lower === "kg") return "kg";
|
|
20046
|
+
if (lower === "g") return "g";
|
|
20047
|
+
if (lower === "l" || lower === "liter" || lower === "litre" || lower === "liters" || lower === "litres") return "L";
|
|
20048
|
+
if (lower === "ml") return "mL";
|
|
20049
|
+
if (lower === "gal" || lower === "gallon" || lower === "gallons") return "gal";
|
|
20050
|
+
if (lower === "s" || lower === "sec" || lower === "second" || lower === "seconds") return "s";
|
|
20051
|
+
if (lower === "ms") return "ms";
|
|
20052
|
+
if (lower === "min" || lower === "minute" || lower === "minutes") return "min";
|
|
20053
|
+
if (lower === "h" || lower === "hr" || lower === "hour" || lower === "hours") return "h";
|
|
20054
|
+
if (lower === "d" || lower === "day" || lower === "days") return "d";
|
|
20055
|
+
if (lower === "b" || lower === "byte" || lower === "bytes") return "B";
|
|
20056
|
+
if (lower === "kib") return "KiB";
|
|
20057
|
+
if (lower === "mib") return "MiB";
|
|
20058
|
+
if (lower === "gib") return "GiB";
|
|
20059
|
+
if (lower === "tib") return "TiB";
|
|
20060
|
+
if (lower === "kb") return "kB";
|
|
20061
|
+
if (lower === "mb") return "MB";
|
|
20062
|
+
if (lower === "gb") return "GB";
|
|
20063
|
+
if (lower === "dbm") return "dBm";
|
|
20064
|
+
if (lower === "dbfs") return "dBFS";
|
|
20065
|
+
if (lower === "inhg") return "inHg";
|
|
20066
|
+
if (lower === "mm/h") return "mm/h";
|
|
20067
|
+
if (lower === "w/m²" || lower === "w/m2") return "W/m²";
|
|
20068
|
+
if (lower === "°" || lower === "deg" || lower === "degree" || lower === "degrees") return "°";
|
|
20069
|
+
if (lower === "kbps") return "kbps";
|
|
20070
|
+
if (lower === "fps") return "fps";
|
|
20071
|
+
return trimmed;
|
|
20072
|
+
}
|
|
20073
|
+
/**
|
|
19992
20074
|
* Key under which `SourceInfo` lives inside `DeviceMeta.metadata`. The
|
|
19993
20075
|
* blob is a free-form `Record<string, unknown>` shared with hardware-
|
|
19994
20076
|
* identity fields (`manufacturer`, `model`, `firmware`, …); SourceInfo
|
|
@@ -41048,7 +41130,8 @@ method(object({
|
|
|
41048
41130
|
"enum"
|
|
41049
41131
|
]),
|
|
41050
41132
|
enumValues: array(string()).optional(),
|
|
41051
|
-
item: boolean().optional()
|
|
41133
|
+
item: boolean().optional(),
|
|
41134
|
+
quantity: ReadingQuantitySchema.optional()
|
|
41052
41135
|
})).readonly(),
|
|
41053
41136
|
itemArray: object({
|
|
41054
41137
|
path: string(),
|
|
@@ -44892,8 +44975,8 @@ var MaskPolygonShapeSchema = object({
|
|
|
44892
44975
|
/** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
|
|
44893
44976
|
var MaskGridShapeSchema = object({
|
|
44894
44977
|
kind: literal("grid"),
|
|
44895
|
-
gridWidth: number(),
|
|
44896
|
-
gridHeight: number(),
|
|
44978
|
+
gridWidth: number().meta(readingMeta("none")),
|
|
44979
|
+
gridHeight: number().meta(readingMeta("none")),
|
|
44897
44980
|
cells: array(boolean())
|
|
44898
44981
|
});
|
|
44899
44982
|
discriminatedUnion("kind", [
|
|
@@ -56373,27 +56456,17 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
56373
56456
|
*/
|
|
56374
56457
|
var AirQualitySensorStatusSchema = object({
|
|
56375
56458
|
/** Carbon dioxide concentration in ppm. */
|
|
56376
|
-
co2Ppm: number().min(0).optional(),
|
|
56459
|
+
co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
56377
56460
|
/** Total volatile organic compounds in ppb. */
|
|
56378
|
-
vocPpb: number().min(0).optional(),
|
|
56461
|
+
vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
56379
56462
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
56380
|
-
pm25: number().min(0).optional(),
|
|
56463
|
+
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56381
56464
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
56382
|
-
pm10: number().min(0).optional(),
|
|
56465
|
+
pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56383
56466
|
/** Composite AQI value (typically 0..500). */
|
|
56384
|
-
aqi: number().optional(),
|
|
56467
|
+
aqi: number().optional().meta(readingMeta("index")),
|
|
56385
56468
|
/** Ms epoch when the slice was last updated. */
|
|
56386
|
-
lastFetchedAt: number()
|
|
56387
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
56388
|
-
* `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
|
|
56389
|
-
* upstream `sensor.*` entity surfaces ONE device_class, so one unit
|
|
56390
|
-
* per slice is unambiguous. */
|
|
56391
|
-
unit: string().optional(),
|
|
56392
|
-
/** Suggested decimal places for numeric display.
|
|
56393
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
56394
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
56395
|
-
* auto-formatting when absent. */
|
|
56396
|
-
precision: number().int().min(0).max(10).optional()
|
|
56469
|
+
lastFetchedAt: number()
|
|
56397
56470
|
});
|
|
56398
56471
|
var airQualitySensorCapability = {
|
|
56399
56472
|
name: "air-quality-sensor",
|
|
@@ -56430,12 +56503,12 @@ var airQualitySensorCapability = {
|
|
|
56430
56503
|
},
|
|
56431
56504
|
pm25: {
|
|
56432
56505
|
kind: "gauge",
|
|
56433
|
-
unit: "
|
|
56506
|
+
unit: "µg/m³",
|
|
56434
56507
|
deadband: 1
|
|
56435
56508
|
},
|
|
56436
56509
|
pm10: {
|
|
56437
56510
|
kind: "gauge",
|
|
56438
|
-
unit: "
|
|
56511
|
+
unit: "µg/m³",
|
|
56439
56512
|
deadband: 1
|
|
56440
56513
|
},
|
|
56441
56514
|
aqi: {
|
|
@@ -56453,22 +56526,13 @@ var airQualitySensorCapability = {
|
|
|
56453
56526
|
* entries with `device_class: illuminance`.
|
|
56454
56527
|
*/
|
|
56455
56528
|
var AmbientLightSensorStatusSchema = object({
|
|
56456
|
-
/** Current illuminance in lux (lx). */
|
|
56457
|
-
lux: number().min(0).meta({
|
|
56458
|
-
|
|
56529
|
+
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
56530
|
+
lux: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
56531
|
+
precision: 0,
|
|
56459
56532
|
label: "Illuminance"
|
|
56460
|
-
}),
|
|
56533
|
+
})),
|
|
56461
56534
|
/** Ms epoch when the slice was last updated. */
|
|
56462
|
-
lastFetchedAt: number()
|
|
56463
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
56464
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
56465
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
56466
|
-
unit: string().optional(),
|
|
56467
|
-
/** Suggested decimal places for numeric display.
|
|
56468
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
56469
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
56470
|
-
* auto-formatting when absent. */
|
|
56471
|
-
precision: number().int().min(0).max(10).optional()
|
|
56535
|
+
lastFetchedAt: number()
|
|
56472
56536
|
});
|
|
56473
56537
|
var ambientLightSensorCapability = {
|
|
56474
56538
|
name: "ambient-light-sensor",
|
|
@@ -56500,12 +56564,7 @@ var ambientLightSensorCapability = {
|
|
|
56500
56564
|
} } },
|
|
56501
56565
|
/** Clock fields: written, but excluded from the compare that decides
|
|
56502
56566
|
* whether persisting is worth a SQLite commit. */
|
|
56503
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
56504
|
-
customize: {
|
|
56505
|
-
version: 1,
|
|
56506
|
-
advanced: "derive",
|
|
56507
|
-
targets: []
|
|
56508
|
-
}
|
|
56567
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
56509
56568
|
};
|
|
56510
56569
|
/**
|
|
56511
56570
|
* Per-class audio metrics aggregated over a sliding window.
|
|
@@ -56732,10 +56791,7 @@ var BatteryStatusSchema = object({
|
|
|
56732
56791
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
56733
56792
|
* declines to band a non-finite reading, which is the correct shape.
|
|
56734
56793
|
*/
|
|
56735
|
-
percentage: number().min(0).max(100).nullable().meta({
|
|
56736
|
-
unit: "%",
|
|
56737
|
-
label: "Charge"
|
|
56738
|
-
}),
|
|
56794
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
56739
56795
|
/**
|
|
56740
56796
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
56741
56797
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -56762,7 +56818,14 @@ var BatteryStatusSchema = object({
|
|
|
56762
56818
|
*/
|
|
56763
56819
|
sleeping: boolean().meta({ label: "Sleeping" }),
|
|
56764
56820
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
56765
|
-
lastUpdated: number(),
|
|
56821
|
+
lastUpdated: number().meta(readingMeta("none")),
|
|
56822
|
+
/**
|
|
56823
|
+
* Battery voltage in volts, for a cell the firmware reports as voltage
|
|
56824
|
+
* rather than a percentage (Ecowitt capacitor packs). Absent on cameras
|
|
56825
|
+
* that only report charge. A second reading on this cap, not a second
|
|
56826
|
+
* `numeric-sensor` on the same device.
|
|
56827
|
+
*/
|
|
56828
|
+
volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
56766
56829
|
/**
|
|
56767
56830
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
56768
56831
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -56814,7 +56877,6 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
56814
56877
|
/** Generic Customize target for the percentage reading (D936). */
|
|
56815
56878
|
var batteryCustomize = {
|
|
56816
56879
|
version: 1,
|
|
56817
|
-
advanced: "derive",
|
|
56818
56880
|
targets: [{
|
|
56819
56881
|
id: "percentage",
|
|
56820
56882
|
cardinality: "scalar",
|
|
@@ -56823,7 +56885,7 @@ var batteryCustomize = {
|
|
|
56823
56885
|
stateField: "percentage"
|
|
56824
56886
|
},
|
|
56825
56887
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
56826
|
-
apply: "add
|
|
56888
|
+
apply: "add",
|
|
56827
56889
|
path: "percentage",
|
|
56828
56890
|
sources: [{
|
|
56829
56891
|
kind: "field",
|
|
@@ -56944,10 +57006,16 @@ onStatusChanged: { data: object({
|
|
|
56944
57006
|
/**
|
|
56945
57007
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
56946
57008
|
*/
|
|
56947
|
-
history: { fields: {
|
|
56948
|
-
|
|
56949
|
-
|
|
56950
|
-
|
|
57009
|
+
history: { fields: {
|
|
57010
|
+
percentage: {
|
|
57011
|
+
kind: "gauge",
|
|
57012
|
+
unit: "%"
|
|
57013
|
+
},
|
|
57014
|
+
volts: {
|
|
57015
|
+
kind: "gauge",
|
|
57016
|
+
unit: "V"
|
|
57017
|
+
}
|
|
57018
|
+
} },
|
|
56951
57019
|
customize: batteryCustomize,
|
|
56952
57020
|
/** Clock fields: written, but excluded from the compare that decides
|
|
56953
57021
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -56994,12 +57062,7 @@ var binaryCapability = {
|
|
|
56994
57062
|
history: { fields: { on: { kind: "boolean" } } },
|
|
56995
57063
|
/** Clock fields: written, but excluded from the compare that decides
|
|
56996
57064
|
* whether persisting is worth a SQLite commit. */
|
|
56997
|
-
volatileStateFields: ["lastChangedAt"]
|
|
56998
|
-
customize: {
|
|
56999
|
-
version: 1,
|
|
57000
|
-
advanced: "derive",
|
|
57001
|
-
targets: []
|
|
57002
|
-
}
|
|
57065
|
+
volatileStateFields: ["lastChangedAt"]
|
|
57003
57066
|
};
|
|
57004
57067
|
/**
|
|
57005
57068
|
* Dimmable-light brightness control. Co-exists with `switch` on the
|
|
@@ -57015,7 +57078,7 @@ var binaryCapability = {
|
|
|
57015
57078
|
*/
|
|
57016
57079
|
var BrightnessStatusSchema = object({
|
|
57017
57080
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
57018
|
-
percentage: number().min(0).max(100),
|
|
57081
|
+
percentage: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
57019
57082
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
57020
57083
|
lastChangedAt: number()
|
|
57021
57084
|
});
|
|
@@ -57469,21 +57532,21 @@ var ClimateControlStatusSchema = object({
|
|
|
57469
57532
|
/** Single setpoint in Celsius. Used by single-target modes
|
|
57470
57533
|
* (heat / cool / dry). Null when the device is in a range mode
|
|
57471
57534
|
* (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
|
|
57472
|
-
target: number().nullable(),
|
|
57535
|
+
target: number().nullable().meta(readingMeta("temperature")),
|
|
57473
57536
|
/** Upper bound of the dual setpoint in Celsius. Populated only
|
|
57474
57537
|
* in `heat_cool` mode. */
|
|
57475
|
-
targetHigh: number().nullable(),
|
|
57538
|
+
targetHigh: number().nullable().meta(readingMeta("temperature")),
|
|
57476
57539
|
/** Lower bound of the dual setpoint in Celsius. Populated only
|
|
57477
57540
|
* in `heat_cool` mode. */
|
|
57478
|
-
targetLow: number().nullable(),
|
|
57541
|
+
targetLow: number().nullable().meta(readingMeta("temperature")),
|
|
57479
57542
|
/** Target relative humidity (0..100). Null when the device has no
|
|
57480
57543
|
* humidity control. */
|
|
57481
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
57544
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
57482
57545
|
/** Current measured temperature in Celsius. Read-only — pushed by
|
|
57483
57546
|
* the device's internal sensor. */
|
|
57484
|
-
currentTemp: number().nullable(),
|
|
57547
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
57485
57548
|
/** Current measured relative humidity (0..100). Read-only. */
|
|
57486
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
57549
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
57487
57550
|
/** Vertical louver swing on/off. Null when the device has no vertical
|
|
57488
57551
|
* swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
|
|
57489
57552
|
swingVertical: boolean().nullable(),
|
|
@@ -57491,11 +57554,11 @@ var ClimateControlStatusSchema = object({
|
|
|
57491
57554
|
* horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
|
|
57492
57555
|
swingHorizontal: boolean().nullable(),
|
|
57493
57556
|
/** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
57494
|
-
minTemp: number().nullable().optional(),
|
|
57557
|
+
minTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
57495
57558
|
/** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
57496
|
-
maxTemp: number().nullable().optional(),
|
|
57559
|
+
maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
57497
57560
|
/** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
|
|
57498
|
-
tempStep: number().positive().nullable().optional(),
|
|
57561
|
+
tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
|
|
57499
57562
|
/** Ms epoch when the slice was last updated (push or command). */
|
|
57500
57563
|
lastFetchedAt: number()
|
|
57501
57564
|
});
|
|
@@ -57637,17 +57700,17 @@ var climateControlCapability = {
|
|
|
57637
57700
|
* modes to expose in the picker without a slice fetch.
|
|
57638
57701
|
*/
|
|
57639
57702
|
var RgbTripletSchema = object({
|
|
57640
|
-
r: number().int().min(0).max(255),
|
|
57641
|
-
g: number().int().min(0).max(255),
|
|
57642
|
-
b: number().int().min(0).max(255)
|
|
57703
|
+
r: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
57704
|
+
g: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
57705
|
+
b: number().int().min(0).max(255).meta(readingMeta("none"))
|
|
57643
57706
|
});
|
|
57644
57707
|
var HsvTripletSchema = object({
|
|
57645
57708
|
/** Hue in degrees, 0..360. */
|
|
57646
|
-
h: number().min(0).max(360),
|
|
57709
|
+
h: number().min(0).max(360).meta(readingMeta("none")),
|
|
57647
57710
|
/** Saturation as 0..100 inclusive. */
|
|
57648
|
-
s: number().min(0).max(100),
|
|
57711
|
+
s: number().min(0).max(100).meta(readingMeta("none")),
|
|
57649
57712
|
/** Value/brightness as 0..100 inclusive. */
|
|
57650
|
-
v: number().min(0).max(100)
|
|
57713
|
+
v: number().min(0).max(100).meta(readingMeta("none"))
|
|
57651
57714
|
});
|
|
57652
57715
|
var ColorInputSchema = discriminatedUnion("mode", [
|
|
57653
57716
|
object({
|
|
@@ -57676,7 +57739,7 @@ var ColorStatusSchema = object({
|
|
|
57676
57739
|
/** Populated when `mode === 'hsv'`. */
|
|
57677
57740
|
hsv: HsvTripletSchema.optional(),
|
|
57678
57741
|
/** Populated when `mode === 'mired'`. */
|
|
57679
|
-
mireds: number().int().optional(),
|
|
57742
|
+
mireds: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
57680
57743
|
/** Ms epoch of the last operator-driven change. */
|
|
57681
57744
|
lastChangedAt: number()
|
|
57682
57745
|
});
|
|
@@ -57871,18 +57934,12 @@ _enum([
|
|
|
57871
57934
|
"empty",
|
|
57872
57935
|
"full"
|
|
57873
57936
|
]);
|
|
57874
|
-
/**
|
|
57875
|
-
|
|
57876
|
-
|
|
57877
|
-
"days",
|
|
57878
|
-
"hours",
|
|
57879
|
-
"cycles"
|
|
57880
|
-
]);
|
|
57881
|
-
/** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
|
|
57882
|
-
* days). `value` null = unknown (D393). */
|
|
57937
|
+
/** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
|
|
57938
|
+
var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
|
|
57939
|
+
/** Remaining life. `value` null = unknown (D393). Duration is seconds. */
|
|
57883
57940
|
var ConsumableRemainingSchema = object({
|
|
57884
|
-
value: number().min(0).nullable(),
|
|
57885
|
-
|
|
57941
|
+
value: number().min(0).nullable().meta(readingMeta("device")),
|
|
57942
|
+
kind: ConsumableRemainingKindSchema
|
|
57886
57943
|
});
|
|
57887
57944
|
/** A single consumable item. Either a continuous `level` (remaining
|
|
57888
57945
|
* life %, or fill % for a `supply` / `waste` item) or a discrete `status`
|
|
@@ -57895,7 +57952,7 @@ var ConsumableItemSchema = object({
|
|
|
57895
57952
|
/** Display name. */
|
|
57896
57953
|
label: string().min(1),
|
|
57897
57954
|
/** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
|
|
57898
|
-
level: number().min(0).max(100).nullable(),
|
|
57955
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
57899
57956
|
/** The provider's discrete verdict when known (binary mode). */
|
|
57900
57957
|
status: ConsumableStatusSchema.nullable(),
|
|
57901
57958
|
/** What the item is — decides whether low or full is bad. Absent = `wear`. */
|
|
@@ -57977,8 +58034,8 @@ var consumablesCustomize = {
|
|
|
57977
58034
|
transforms: [{ kind: "identity" }]
|
|
57978
58035
|
}],
|
|
57979
58036
|
literals: [{
|
|
57980
|
-
path: "remaining.
|
|
57981
|
-
values: [...
|
|
58037
|
+
path: "remaining.kind",
|
|
58038
|
+
values: [...ConsumableRemainingKindSchema.options]
|
|
57982
58039
|
}]
|
|
57983
58040
|
},
|
|
57984
58041
|
{
|
|
@@ -58107,7 +58164,6 @@ reset: method(object({
|
|
|
58107
58164
|
},
|
|
58108
58165
|
"remaining.value": {
|
|
58109
58166
|
kind: "gauge",
|
|
58110
|
-
unitFrom: "remaining.unit",
|
|
58111
58167
|
deadband: 1,
|
|
58112
58168
|
displayName: "Remaining"
|
|
58113
58169
|
}
|
|
@@ -58177,10 +58233,9 @@ var contactCapability = {
|
|
|
58177
58233
|
* (empty array for the other kinds). It lives in the slice because
|
|
58178
58234
|
* HA `select.*` entities may update their options at runtime.
|
|
58179
58235
|
*
|
|
58180
|
-
*
|
|
58181
|
-
*
|
|
58182
|
-
*
|
|
58183
|
-
* push — consistent with `options` and the sensor-unit approach.
|
|
58236
|
+
* Numeric `min` / `max` / `step` live in the slice and share the device
|
|
58237
|
+
* quantity (`SourceInfo.quantity`, D944). Display precision is a read-time
|
|
58238
|
+
* preference. A datetime control's shape is `SourceInfo.dateTimeShape`.
|
|
58184
58239
|
*/
|
|
58185
58240
|
var ControlStatusSchema = object({
|
|
58186
58241
|
kind: _enum([
|
|
@@ -58192,39 +58247,17 @@ var ControlStatusSchema = object({
|
|
|
58192
58247
|
/** Current value. Numeric kind → number; select/text/datetime → string.
|
|
58193
58248
|
* Datetime values are ISO 8601 strings (full timestamp, date-only,
|
|
58194
58249
|
* or time-only — HA accepts all three forms). */
|
|
58195
|
-
value: union([number(), string()]),
|
|
58250
|
+
value: union([number().meta(readingMeta("device")), string()]),
|
|
58196
58251
|
/** Acceptable values when `kind === 'select'`. Empty for other kinds. */
|
|
58197
58252
|
options: array(string()),
|
|
58198
58253
|
/** Ms epoch when the slice was last updated. */
|
|
58199
58254
|
lastChangedAt: number(),
|
|
58200
|
-
/**
|
|
58201
|
-
|
|
58202
|
-
|
|
58203
|
-
|
|
58204
|
-
/**
|
|
58205
|
-
|
|
58206
|
-
min: number().optional(),
|
|
58207
|
-
/** Maximum allowed value. Populated live from HA `attributes.max`.
|
|
58208
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58209
|
-
max: number().optional(),
|
|
58210
|
-
/** Step increment. Populated live from HA `attributes.step`.
|
|
58211
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58212
|
-
step: number().optional(),
|
|
58213
|
-
/** Suggested decimal places for numeric display. Populated live from HA
|
|
58214
|
-
* `attributes.suggested_display_precision`, or derived from `step` when the
|
|
58215
|
-
* HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
|
|
58216
|
-
* other kinds. Falls back to auto-formatting when absent. */
|
|
58217
|
-
precision: number().int().min(0).max(10).optional(),
|
|
58218
|
-
/** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
|
|
58219
|
-
* 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
|
|
58220
|
-
* (UI defaults to 'datetime'). Derived live from the device's HA domain
|
|
58221
|
-
* (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
|
|
58222
|
-
* `has_date`/`has_time` attributes on each push. */
|
|
58223
|
-
format: _enum([
|
|
58224
|
-
"date",
|
|
58225
|
-
"time",
|
|
58226
|
-
"datetime"
|
|
58227
|
-
]).optional()
|
|
58255
|
+
/** Minimum allowed value, in the device quantity's base unit. */
|
|
58256
|
+
min: number().optional().meta(readingMeta("device")),
|
|
58257
|
+
/** Maximum allowed value, in the device quantity's base unit. */
|
|
58258
|
+
max: number().optional().meta(readingMeta("device")),
|
|
58259
|
+
/** Step increment, in the device quantity's base unit. */
|
|
58260
|
+
step: number().optional().meta(readingMeta("device"))
|
|
58228
58261
|
});
|
|
58229
58262
|
var ControlSetValueInputSchema = discriminatedUnion("kind", [
|
|
58230
58263
|
object({
|
|
@@ -58290,9 +58323,9 @@ var CoverStatusSchema = object({
|
|
|
58290
58323
|
]),
|
|
58291
58324
|
/** 0 = fully closed, 100 = fully open. Null when the device has
|
|
58292
58325
|
* no intermediate position surface. */
|
|
58293
|
-
position: number().min(0).max(100).nullable(),
|
|
58326
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58294
58327
|
/** 0 = closed slats, 100 = open slats. Null when no tilt support. */
|
|
58295
|
-
tiltPosition: number().min(0).max(100).nullable(),
|
|
58328
|
+
tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58296
58329
|
/** Ms epoch when the slice was last updated. */
|
|
58297
58330
|
lastChangedAt: number()
|
|
58298
58331
|
});
|
|
@@ -58395,9 +58428,9 @@ var NormalizedRangeSchema$1 = object({
|
|
|
58395
58428
|
var DayNightStatusSchema = object({
|
|
58396
58429
|
mode: DayNightModeSchema,
|
|
58397
58430
|
/** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
|
|
58398
|
-
sensitivity: number().optional(),
|
|
58431
|
+
sensitivity: number().optional().meta(readingMeta("none")),
|
|
58399
58432
|
/** Delay before the IR-cut filter flips, in seconds. */
|
|
58400
|
-
switchDelaySec: number().optional(),
|
|
58433
|
+
switchDelaySec: number().optional().meta(readingMeta("duration")),
|
|
58401
58434
|
lastFetchedAt: number()
|
|
58402
58435
|
});
|
|
58403
58436
|
/**
|
|
@@ -58615,11 +58648,11 @@ var DishwasherStatusSchema = object({
|
|
|
58615
58648
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
58616
58649
|
availablePrograms: array(string()),
|
|
58617
58650
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
58618
|
-
remainingSeconds: number().int().nonnegative().nullable(),
|
|
58651
|
+
remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
58619
58652
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
58620
|
-
delayedStartSeconds: number().int().nonnegative().nullable(),
|
|
58653
|
+
delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
58621
58654
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
58622
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
58655
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58623
58656
|
/** True when the door is reported open; null when the door state is not known. */
|
|
58624
58657
|
doorOpen: boolean().nullable(),
|
|
58625
58658
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -58643,7 +58676,7 @@ var DishwasherStatusSchema = object({
|
|
|
58643
58676
|
/** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
|
|
58644
58677
|
machineCareDue: boolean().nullable(),
|
|
58645
58678
|
/** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
|
|
58646
|
-
machineCareRemainingRuns: number().int().nonnegative().nullable(),
|
|
58679
|
+
machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
|
|
58647
58680
|
/** Option states: null when the appliance does not expose the option or has not said. */
|
|
58648
58681
|
halfLoad: boolean().nullable(),
|
|
58649
58682
|
hygienePlus: boolean().nullable(),
|
|
@@ -58658,7 +58691,7 @@ var DishwasherStatusSchema = object({
|
|
|
58658
58691
|
/** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
|
|
58659
58692
|
activeEvents: array(string()),
|
|
58660
58693
|
/** Numeric error code when the appliance reports one; null otherwise. */
|
|
58661
|
-
errorCode: number().nullable(),
|
|
58694
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
58662
58695
|
/** Name of the raised alert-level event while the appliance is in error; null otherwise. */
|
|
58663
58696
|
errorLabel: string().nullable(),
|
|
58664
58697
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
@@ -58889,7 +58922,7 @@ var DoorbellStatusSchema = object({
|
|
|
58889
58922
|
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
58890
58923
|
lastPressedAt: number().nullable(),
|
|
58891
58924
|
/** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
|
|
58892
|
-
pressCountSinceStart: number()
|
|
58925
|
+
pressCountSinceStart: number().meta(readingMeta("count"))
|
|
58893
58926
|
});
|
|
58894
58927
|
var DoorbellPressEventSchema = object({
|
|
58895
58928
|
deviceId: number(),
|
|
@@ -58954,17 +58987,7 @@ onPressed: {
|
|
|
58954
58987
|
history: "none",
|
|
58955
58988
|
customize: doorbellCustomize
|
|
58956
58989
|
};
|
|
58957
|
-
|
|
58958
|
-
* Enum-state sensor — a string value picked from a finite option set.
|
|
58959
|
-
* Drives HA `sensor` entries with `state_class: enum` (HVAC action
|
|
58960
|
-
* states, weather conditions, contact-source channels, mode strings).
|
|
58961
|
-
* The option set is stable across observations; it is persisted in the
|
|
58962
|
-
* device config blob (via the control slice at adoption) and is NOT
|
|
58963
|
-
* stored in `sourceInfo`.
|
|
58964
|
-
*/
|
|
58965
|
-
/** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
|
|
58966
|
-
* enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
|
|
58967
|
-
var EnumSensorDateTimeFormatSchema = _enum([
|
|
58990
|
+
_enum([
|
|
58968
58991
|
"date",
|
|
58969
58992
|
"time",
|
|
58970
58993
|
"datetime"
|
|
@@ -58972,12 +58995,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
|
|
|
58972
58995
|
var EnumSensorStatusSchema = object({
|
|
58973
58996
|
value: string(),
|
|
58974
58997
|
/**
|
|
58975
|
-
* Set
|
|
58976
|
-
* as
|
|
58977
|
-
*
|
|
58978
|
-
* a time-only sensor → `'time'`.
|
|
58998
|
+
* Set when `value` is a timestamp rather than an enum state, so history
|
|
58999
|
+
* does not learn ISO strings as states. The display shape (date / time /
|
|
59000
|
+
* datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
|
|
58979
59001
|
*/
|
|
58980
|
-
|
|
59002
|
+
timestamp: boolean().optional(),
|
|
58981
59003
|
/** Ms epoch when the slice was last updated. */
|
|
58982
59004
|
lastFetchedAt: number()
|
|
58983
59005
|
});
|
|
@@ -59004,11 +59026,11 @@ var enumSensorCapability = {
|
|
|
59004
59026
|
* History (D850): the value as an `enum` series (an appliance's program or
|
|
59005
59027
|
* operation state, an HVAC action). The option set is per device, so the
|
|
59006
59028
|
* value list is LEARNED as values are first seen (bounded). A sensor with a
|
|
59007
|
-
* `
|
|
59029
|
+
* `timestamp` carries a clock, not a state, and is not recorded.
|
|
59008
59030
|
*/
|
|
59009
59031
|
history: { fields: { value: {
|
|
59010
59032
|
kind: "enum",
|
|
59011
|
-
skipWhenSet: "
|
|
59033
|
+
skipWhenSet: "timestamp"
|
|
59012
59034
|
} } },
|
|
59013
59035
|
/** Clock fields: written, but excluded from the compare that decides
|
|
59014
59036
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -59028,13 +59050,13 @@ var EventFireSchema = object({
|
|
|
59028
59050
|
deviceId: number(),
|
|
59029
59051
|
eventType: string(),
|
|
59030
59052
|
data: record(string(), unknown()).nullable(),
|
|
59031
|
-
timestamp: number(),
|
|
59032
|
-
seq: number()
|
|
59053
|
+
timestamp: number().meta(readingMeta("none")),
|
|
59054
|
+
seq: number().meta(readingMeta("count"))
|
|
59033
59055
|
});
|
|
59034
59056
|
var EventEmitterStatusSchema = object({
|
|
59035
59057
|
eventTypes: array(string()),
|
|
59036
59058
|
lastEvent: EventFireSchema.nullable(),
|
|
59037
|
-
eventCountSinceStart: number()
|
|
59059
|
+
eventCountSinceStart: number().meta(readingMeta("count"))
|
|
59038
59060
|
});
|
|
59039
59061
|
var eventEmitterCapability = {
|
|
59040
59062
|
name: "event-emitter",
|
|
@@ -59447,10 +59469,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
59447
59469
|
var FanControlStatusSchema = object({
|
|
59448
59470
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
59449
59471
|
* speed surface (single-speed fan). */
|
|
59450
|
-
percentage: number().min(0).max(100).nullable(),
|
|
59472
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59451
59473
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
59452
59474
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
59453
|
-
percentageStep: number().positive().optional(),
|
|
59475
|
+
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
59454
59476
|
/** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
|
|
59455
59477
|
* — empty string when no preset is active or supported. */
|
|
59456
59478
|
preset: string(),
|
|
@@ -59571,7 +59593,7 @@ var FeatureProbeStatusSchema = object({
|
|
|
59571
59593
|
/** Camera/firmware model string. `null` when the firmware doesn't expose it. */
|
|
59572
59594
|
model: string().nullable(),
|
|
59573
59595
|
/** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
|
|
59574
|
-
channelCount: number().nullable(),
|
|
59596
|
+
channelCount: number().nullable().meta(readingMeta("count")),
|
|
59575
59597
|
/**
|
|
59576
59598
|
* Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
|
|
59577
59599
|
* completes — drivers' `getAccessoryChildren()` should treat zero as
|
|
@@ -59696,7 +59718,7 @@ var gasCapability = {
|
|
|
59696
59718
|
};
|
|
59697
59719
|
var HeatingValveStatusSchema = object({
|
|
59698
59720
|
/** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
|
|
59699
|
-
position: number().min(0).max(100).nullable(),
|
|
59721
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59700
59722
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
59701
59723
|
lastFetchedAt: number()
|
|
59702
59724
|
});
|
|
@@ -59739,9 +59761,9 @@ var HumidifierStatusSchema = object({
|
|
|
59739
59761
|
/** Whether the humidifier is currently on. */
|
|
59740
59762
|
on: boolean(),
|
|
59741
59763
|
/** Current measured relative humidity (0..100). Null when not reported. */
|
|
59742
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
59764
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
59743
59765
|
/** Target relative humidity (0..100). Null when no setpoint surface. */
|
|
59744
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
59766
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
59745
59767
|
/** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
|
|
59746
59768
|
* device has no mode surface. */
|
|
59747
59769
|
mode: string().nullable(),
|
|
@@ -59751,9 +59773,9 @@ var HumidifierStatusSchema = object({
|
|
|
59751
59773
|
* `idle` / `off`). Null when the device doesn't report it. */
|
|
59752
59774
|
action: string().nullable(),
|
|
59753
59775
|
/** HA `min_humidity` attribute. Null → UI uses 0. */
|
|
59754
|
-
minHumidity: number().nullable(),
|
|
59776
|
+
minHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
59755
59777
|
/** HA `max_humidity` attribute. Null → UI uses 100. */
|
|
59756
|
-
maxHumidity: number().nullable(),
|
|
59778
|
+
maxHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
59757
59779
|
/** Ms epoch when the slice was last updated. */
|
|
59758
59780
|
lastChangedAt: number()
|
|
59759
59781
|
});
|
|
@@ -59823,22 +59845,13 @@ var humidifierCapability = {
|
|
|
59823
59845
|
* `sourceInfo`.
|
|
59824
59846
|
*/
|
|
59825
59847
|
var HumiditySensorStatusSchema = object({
|
|
59826
|
-
/** Current relative humidity, 0..100. */
|
|
59827
|
-
percent: number().min(0).max(100).meta({
|
|
59828
|
-
|
|
59848
|
+
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
59849
|
+
percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
59850
|
+
precision: 0,
|
|
59829
59851
|
label: "Humidity"
|
|
59830
|
-
}),
|
|
59852
|
+
})),
|
|
59831
59853
|
/** Ms epoch when the slice was last updated. */
|
|
59832
|
-
lastFetchedAt: number()
|
|
59833
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
59834
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
59835
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
59836
|
-
unit: string().optional(),
|
|
59837
|
-
/** Suggested decimal places for numeric display.
|
|
59838
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
59839
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
59840
|
-
* auto-formatting when absent. */
|
|
59841
|
-
precision: number().int().min(0).max(10).optional()
|
|
59854
|
+
lastFetchedAt: number()
|
|
59842
59855
|
});
|
|
59843
59856
|
var humiditySensorCapability = {
|
|
59844
59857
|
name: "humidity-sensor",
|
|
@@ -59869,12 +59882,7 @@ var humiditySensorCapability = {
|
|
|
59869
59882
|
} } },
|
|
59870
59883
|
/** Clock fields: written, but excluded from the compare that decides
|
|
59871
59884
|
* whether persisting is worth a SQLite commit. */
|
|
59872
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
59873
|
-
customize: {
|
|
59874
|
-
version: 1,
|
|
59875
|
-
advanced: "derive",
|
|
59876
|
-
targets: []
|
|
59877
|
-
}
|
|
59885
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
59878
59886
|
};
|
|
59879
59887
|
/**
|
|
59880
59888
|
* Image display cap. Models a single still image exposed by an integration —
|
|
@@ -59895,7 +59903,7 @@ var ImageStatusSchema = object({
|
|
|
59895
59903
|
* entity exposes no `entity_picture` (yet). */
|
|
59896
59904
|
url: string().nullable(),
|
|
59897
59905
|
/** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
|
|
59898
|
-
lastUpdated: number().nullable()
|
|
59906
|
+
lastUpdated: number().nullable().meta(readingMeta("none"))
|
|
59899
59907
|
});
|
|
59900
59908
|
var imageCapability = {
|
|
59901
59909
|
name: "image",
|
|
@@ -59983,19 +59991,19 @@ var NormalizedRangeSchema = object({
|
|
|
59983
59991
|
*/
|
|
59984
59992
|
var ImageSettingsStatusSchema = object({
|
|
59985
59993
|
/** Normalized 0–100. */
|
|
59986
|
-
brightness: number().optional(),
|
|
59994
|
+
brightness: number().optional().meta(readingMeta("none")),
|
|
59987
59995
|
/** Normalized 0–100. */
|
|
59988
|
-
contrast: number().optional(),
|
|
59996
|
+
contrast: number().optional().meta(readingMeta("none")),
|
|
59989
59997
|
/** Normalized 0–100. */
|
|
59990
|
-
saturation: number().optional(),
|
|
59998
|
+
saturation: number().optional().meta(readingMeta("none")),
|
|
59991
59999
|
/** Normalized 0–100. */
|
|
59992
|
-
sharpness: number().optional(),
|
|
60000
|
+
sharpness: number().optional().meta(readingMeta("none")),
|
|
59993
60001
|
mirror: boolean().optional(),
|
|
59994
60002
|
flip: boolean().optional(),
|
|
59995
60003
|
rotate: ImageRotateSchema.optional(),
|
|
59996
60004
|
whiteBalance: WhiteBalanceModeSchema.optional(),
|
|
59997
60005
|
/** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
|
|
59998
|
-
warmth: number().optional(),
|
|
60006
|
+
warmth: number().optional().meta(readingMeta("none")),
|
|
59999
60007
|
exposureMode: ExposureModeSchema.optional(),
|
|
60000
60008
|
backlightMode: BacklightModeSchema.optional(),
|
|
60001
60009
|
lastFetchedAt: number()
|
|
@@ -60274,10 +60282,10 @@ var IntercomAbilitySchema = object({
|
|
|
60274
60282
|
/** Codecs accepted by firmware, in preference order. Driver-authored strings. */
|
|
60275
60283
|
codecs: array(string()),
|
|
60276
60284
|
/** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
|
|
60277
|
-
sampleRate: number(),
|
|
60285
|
+
sampleRate: number().meta(readingMeta("none")),
|
|
60278
60286
|
duplex: _enum(["half", "full"]),
|
|
60279
60287
|
/** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
|
|
60280
|
-
maxBacklogMs: number()
|
|
60288
|
+
maxBacklogMs: number().meta(readingMeta("duration-ms"))
|
|
60281
60289
|
});
|
|
60282
60290
|
var IntercomStatusSchema = object({
|
|
60283
60291
|
/** True while a talk session is open. */
|
|
@@ -60504,13 +60512,13 @@ var LawnMowerControlStatusSchema = object({
|
|
|
60504
60512
|
activity: LawnMowerActivitySchema,
|
|
60505
60513
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
60506
60514
|
* reading. */
|
|
60507
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
60515
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60508
60516
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
60509
60517
|
* task is active / progress is unavailable. */
|
|
60510
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
60518
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60511
60519
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
60512
60520
|
* or null when unknown. */
|
|
60513
|
-
currentCode: number().nullable(),
|
|
60521
|
+
currentCode: number().nullable().meta(readingMeta("none")),
|
|
60514
60522
|
/** Human label for {@link currentCode}, or null when undecodable. */
|
|
60515
60523
|
currentCodeLabel: string().nullable(),
|
|
60516
60524
|
/** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
|
|
@@ -61043,7 +61051,7 @@ var MediaPlayerStatusSchema = object({
|
|
|
61043
61051
|
state: MediaPlayerStateSchema,
|
|
61044
61052
|
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
61045
61053
|
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
61046
|
-
volumeLevel: number().min(0).max(100).nullable(),
|
|
61054
|
+
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61047
61055
|
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
61048
61056
|
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
61049
61057
|
isMuted: boolean().nullable(),
|
|
@@ -61056,10 +61064,10 @@ var MediaPlayerStatusSchema = object({
|
|
|
61056
61064
|
currentMedia: MediaInfoSchema.nullable(),
|
|
61057
61065
|
/** Current playback position in ms. Null when not seekable or
|
|
61058
61066
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
61059
|
-
positionMs: number().int().nonnegative().nullable(),
|
|
61067
|
+
positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61060
61068
|
/** Total duration of the current media in ms. Null when unknown
|
|
61061
61069
|
* (live stream) or nothing is playing. */
|
|
61062
|
-
durationMs: number().int().nonnegative().nullable(),
|
|
61070
|
+
durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61063
61071
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
61064
61072
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
61065
61073
|
shuffle: boolean().nullable(),
|
|
@@ -61368,7 +61376,7 @@ var motionCustomize = {
|
|
|
61368
61376
|
eventName: "onMotionChanged"
|
|
61369
61377
|
},
|
|
61370
61378
|
deviceTypes: [DeviceType.Camera],
|
|
61371
|
-
apply: "
|
|
61379
|
+
apply: "add",
|
|
61372
61380
|
applyAuthority: {
|
|
61373
61381
|
kind: "camera-sensors",
|
|
61374
61382
|
timeoutField: "motionCooldownMs"
|
|
@@ -61440,7 +61448,7 @@ var MotionStatusSchema = object({
|
|
|
61440
61448
|
* it, every provider seeded it at boot, and every session left it
|
|
61441
61449
|
* `detected: true` after its detach.
|
|
61442
61450
|
*/
|
|
61443
|
-
autoClearAfterMs: number().nullable()
|
|
61451
|
+
autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
|
|
61444
61452
|
});
|
|
61445
61453
|
/**
|
|
61446
61454
|
* Payload of `motion.onMotionChanged` event + the corresponding bus
|
|
@@ -61622,7 +61630,7 @@ var MotionZoneRegionSchema = object({
|
|
|
61622
61630
|
* the grid region(s). */
|
|
61623
61631
|
var MotionZoneStatusSchema = object({
|
|
61624
61632
|
enabled: boolean(),
|
|
61625
|
-
sensitivity: number(),
|
|
61633
|
+
sensitivity: number().meta(readingMeta("none")),
|
|
61626
61634
|
/** Grid region(s). Today exactly one `grid` shape. */
|
|
61627
61635
|
regions: array(MotionZoneRegionSchema),
|
|
61628
61636
|
lastFetchedAt: number()
|
|
@@ -61992,7 +62000,7 @@ var NavigationStatusSchema = object({
|
|
|
61992
62000
|
following: boolean(),
|
|
61993
62001
|
flash: boolean(),
|
|
61994
62002
|
lightMode: NavigationLightModeSchema,
|
|
61995
|
-
lightLevel: number().min(40).max(100),
|
|
62003
|
+
lightLevel: number().min(40).max(100).meta(readingMeta("none")),
|
|
61996
62004
|
/** Ms epoch when the slice was last updated. */
|
|
61997
62005
|
lastChangedAt: number()
|
|
61998
62006
|
});
|
|
@@ -62126,13 +62134,13 @@ var NetworkLinkStatusSchema = object({
|
|
|
62126
62134
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
62127
62135
|
* SKIP a null rather than coerce it.
|
|
62128
62136
|
*/
|
|
62129
|
-
signalPercent: number().min(0).max(100).nullable(),
|
|
62137
|
+
signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62130
62138
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
62131
|
-
rssiDbm: number().optional(),
|
|
62139
|
+
rssiDbm: number().optional().meta(readingMeta("signal-level")),
|
|
62132
62140
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
62133
62141
|
ssid: string().optional(),
|
|
62134
62142
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
62135
|
-
lastUpdated: number()
|
|
62143
|
+
lastUpdated: number().meta(readingMeta("none"))
|
|
62136
62144
|
});
|
|
62137
62145
|
var networkLinkCapability = {
|
|
62138
62146
|
name: "network-link",
|
|
@@ -62517,7 +62525,7 @@ var NotifierStatusSchema = object({
|
|
|
62517
62525
|
lastError: string().nullable(),
|
|
62518
62526
|
/** Number of deliveries currently buffered server-side (rate-limited
|
|
62519
62527
|
* notifiers may queue). 0 when the provider sends synchronously. */
|
|
62520
|
-
queueDepth: number().int().nonnegative(),
|
|
62528
|
+
queueDepth: number().int().nonnegative().meta(readingMeta("count")),
|
|
62521
62529
|
/** Per-feature capability matrix — authoritative source for the
|
|
62522
62530
|
* compose-form field gating. */
|
|
62523
62531
|
supports: NotifierSupportsSchema
|
|
@@ -62609,11 +62617,9 @@ onSent: { data: object({
|
|
|
62609
62617
|
* cap fits (HA `sensor` whose `device_class` we don't have a typed cap
|
|
62610
62618
|
* for: water flow, distance, weight, frequency, signal strength, …).
|
|
62611
62619
|
*
|
|
62612
|
-
* The
|
|
62613
|
-
*
|
|
62614
|
-
*
|
|
62615
|
-
* The UI reads them directly from the slice — they are the single source
|
|
62616
|
-
* of truth for generic numeric rendering and do NOT live on `sourceInfo`.
|
|
62620
|
+
* The quantity is not on this slice. It is chosen when the device is
|
|
62621
|
+
* adopted and stored once on `SourceInfo.quantity` (D944). `value` is in
|
|
62622
|
+
* that quantity's base unit. `null` is a refused sample (D393).
|
|
62617
62623
|
*
|
|
62618
62624
|
* Prefer the typed alternatives (`temperature-sensor`, `humidity-sensor`,
|
|
62619
62625
|
* `ambient-light-sensor`, `pressure-sensor`, `power-meter`,
|
|
@@ -62622,15 +62628,8 @@ onSent: { data: object({
|
|
|
62622
62628
|
* numeric values land here.
|
|
62623
62629
|
*/
|
|
62624
62630
|
var NumericSensorStatusSchema = object({
|
|
62625
|
-
|
|
62626
|
-
|
|
62627
|
-
* Populated live from the upstream source on each state push.
|
|
62628
|
-
* Absent for unitless sensors. */
|
|
62629
|
-
unit: string().optional(),
|
|
62630
|
-
/** Suggested decimal places for numeric display.
|
|
62631
|
-
* Populated live from the upstream source when provided.
|
|
62632
|
-
* Falls back to auto-formatting when absent. */
|
|
62633
|
-
precision: number().int().min(0).max(10).optional(),
|
|
62631
|
+
/** Reading in the device quantity's base unit. `null` when refused (D393). */
|
|
62632
|
+
value: number().nullable().meta(readingMeta("device")),
|
|
62634
62633
|
/** Ms epoch when the slice was last updated. */
|
|
62635
62634
|
lastFetchedAt: number()
|
|
62636
62635
|
});
|
|
@@ -62654,11 +62653,11 @@ var numericSensorCapability = {
|
|
|
62654
62653
|
*/
|
|
62655
62654
|
durability: "restored",
|
|
62656
62655
|
/**
|
|
62657
|
-
* History (D850): recorded series. The unit is
|
|
62656
|
+
* History (D850): recorded series. The unit is the device quantity's base,
|
|
62657
|
+
* resolved when the series is read — the cap has no single unit.
|
|
62658
62658
|
*/
|
|
62659
62659
|
history: { fields: { value: {
|
|
62660
62660
|
kind: "gauge",
|
|
62661
|
-
unitFrom: "unit",
|
|
62662
62661
|
minIntervalMs: 3e4
|
|
62663
62662
|
} } },
|
|
62664
62663
|
/** Clock fields: written, but excluded from the compare that decides
|
|
@@ -62950,23 +62949,26 @@ var PetFeederStatusSchema = object({
|
|
|
62950
62949
|
/** Food currently in the bowl (grams). Null when the device has not
|
|
62951
62950
|
* reported a reading yet. On dual-hopper models this is the combined
|
|
62952
62951
|
* bowl reading; per-hopper levels live in `food1`/`food2`. */
|
|
62953
|
-
foodLevel: number().nullable(),
|
|
62952
|
+
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
62954
62953
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
62955
62954
|
* single-hopper models. */
|
|
62956
|
-
food1: number().nullable(),
|
|
62955
|
+
food1: number().nullable().meta(readingMeta("none")),
|
|
62957
62956
|
/** Hopper-2 food level (grams) on dual-hopper feeders; null on
|
|
62958
62957
|
* single-hopper models. */
|
|
62959
|
-
food2: number().nullable(),
|
|
62958
|
+
food2: number().nullable().meta(readingMeta("none")),
|
|
62960
62959
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
62961
62960
|
* (`device_class: problem`, on = low). True when the bowl is empty /
|
|
62962
62961
|
* below the feeder's low threshold. */
|
|
62963
62962
|
lowFood: boolean(),
|
|
62964
62963
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
62965
62964
|
* device has no battery reading. */
|
|
62966
|
-
batteryPower: number().min(0).max(100).nullable(),
|
|
62967
|
-
/**
|
|
62968
|
-
*
|
|
62969
|
-
|
|
62965
|
+
batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62966
|
+
/**
|
|
62967
|
+
* Desiccant life remaining, stored in seconds (duration base, D944).
|
|
62968
|
+
* Providers convert from the days the feeder reports. Null when the
|
|
62969
|
+
* model has no desiccant sensor.
|
|
62970
|
+
*/
|
|
62971
|
+
desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
|
|
62970
62972
|
/** True while a feed is in progress. */
|
|
62971
62973
|
feeding: boolean(),
|
|
62972
62974
|
/** Decoded connectivity / power status (HA petkit device-status enum).
|
|
@@ -62977,7 +62979,7 @@ var PetFeederStatusSchema = object({
|
|
|
62977
62979
|
* with `errorCode` for consumers that want the raw integer. */
|
|
62978
62980
|
error: string().nullable(),
|
|
62979
62981
|
/** Raw device error code (0 / null = no error). */
|
|
62980
|
-
errorCode: number().nullable(),
|
|
62982
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
62981
62983
|
/** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
|
|
62982
62984
|
isDualHopper: boolean(),
|
|
62983
62985
|
/** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
|
|
@@ -62987,7 +62989,7 @@ var PetFeederStatusSchema = object({
|
|
|
62987
62989
|
/** Play a chime when dispensing. */
|
|
62988
62990
|
feedSound: boolean(),
|
|
62989
62991
|
/** Speaker / prompt volume level (device-scaled integer). */
|
|
62990
|
-
volume: number(),
|
|
62992
|
+
volume: number().meta(readingMeta("none")),
|
|
62991
62993
|
/** Ms epoch when the slice was last refreshed from the cloud. */
|
|
62992
62994
|
lastFetchedAt: number()
|
|
62993
62995
|
});
|
|
@@ -63106,7 +63108,7 @@ var petFeederCapability = {
|
|
|
63106
63108
|
},
|
|
63107
63109
|
desiccantLeftDays: {
|
|
63108
63110
|
kind: "gauge",
|
|
63109
|
-
unit: "
|
|
63111
|
+
unit: "s"
|
|
63110
63112
|
}
|
|
63111
63113
|
} }
|
|
63112
63114
|
};
|
|
@@ -63507,36 +63509,19 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
63507
63509
|
*/
|
|
63508
63510
|
var PowerMeterStatusSchema = object({
|
|
63509
63511
|
/** Instantaneous power draw in watts. */
|
|
63510
|
-
watts: number().optional().meta({
|
|
63511
|
-
unit: "W",
|
|
63512
|
-
label: "Power"
|
|
63513
|
-
}),
|
|
63512
|
+
watts: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
63514
63513
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
63515
|
-
kwhTotal: number().optional().meta({
|
|
63516
|
-
unit: "kWh",
|
|
63517
|
-
label: "Energy"
|
|
63518
|
-
}),
|
|
63514
|
+
kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
63519
63515
|
/** Voltage in volts. */
|
|
63520
|
-
volts: number().optional(),
|
|
63516
|
+
volts: number().optional().meta(readingMeta("voltage")),
|
|
63521
63517
|
/** Current in amperes. */
|
|
63522
|
-
amps: number().optional(),
|
|
63523
|
-
/** Apparent power in volt-amperes
|
|
63524
|
-
va: number().optional(),
|
|
63518
|
+
amps: number().optional().meta(readingMeta("current")),
|
|
63519
|
+
/** Apparent power in volt-amperes. */
|
|
63520
|
+
va: number().optional().meta(readingMeta("apparent-power")),
|
|
63525
63521
|
/** Power factor (0..1) if reported. */
|
|
63526
|
-
powerFactor: number().min(0).max(1).optional(),
|
|
63522
|
+
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
63527
63523
|
/** Ms epoch when the slice was last updated. */
|
|
63528
|
-
lastFetchedAt: number()
|
|
63529
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
63530
|
-
* `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
|
|
63531
|
-
* Each upstream `sensor.*` entity surfaces ONE device_class, so one
|
|
63532
|
-
* unit per slice is unambiguous. The UI prefers this over the role's
|
|
63533
|
-
* canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
|
|
63534
|
-
unit: string().optional(),
|
|
63535
|
-
/** Suggested decimal places for numeric display.
|
|
63536
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
63537
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
63538
|
-
* auto-formatting when absent. */
|
|
63539
|
-
precision: number().int().min(0).max(10).optional()
|
|
63524
|
+
lastFetchedAt: number()
|
|
63540
63525
|
});
|
|
63541
63526
|
var powerMeterCapability = {
|
|
63542
63527
|
name: "power-meter",
|
|
@@ -63600,12 +63585,7 @@ var powerMeterCapability = {
|
|
|
63600
63585
|
} },
|
|
63601
63586
|
/** Clock fields: written, but excluded from the compare that decides
|
|
63602
63587
|
* whether persisting is worth a SQLite commit. */
|
|
63603
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
63604
|
-
customize: {
|
|
63605
|
-
version: 1,
|
|
63606
|
-
advanced: "derive",
|
|
63607
|
-
targets: []
|
|
63608
|
-
}
|
|
63588
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
63609
63589
|
};
|
|
63610
63590
|
/**
|
|
63611
63591
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
@@ -63625,11 +63605,11 @@ var powerMeterCapability = {
|
|
|
63625
63605
|
*/
|
|
63626
63606
|
var GpsLocationSchema = object({
|
|
63627
63607
|
/** Latitude in decimal degrees, -90..90. */
|
|
63628
|
-
latitude: number().min(-90).max(90),
|
|
63608
|
+
latitude: number().min(-90).max(90).meta(readingMeta("angle")),
|
|
63629
63609
|
/** Longitude in decimal degrees, -180..180. */
|
|
63630
|
-
longitude: number().min(-180).max(180),
|
|
63610
|
+
longitude: number().min(-180).max(180).meta(readingMeta("angle")),
|
|
63631
63611
|
/** Reported accuracy in meters (lower = better). */
|
|
63632
|
-
accuracyMeters: number().nonnegative()
|
|
63612
|
+
accuracyMeters: number().nonnegative().meta(readingMeta("length"))
|
|
63633
63613
|
});
|
|
63634
63614
|
var PresenceStatusSchema = object({
|
|
63635
63615
|
/** `home` / `not_home` / any user-defined zone name. */
|
|
@@ -63641,7 +63621,7 @@ var PresenceStatusSchema = object({
|
|
|
63641
63621
|
gps: GpsLocationSchema.nullable(),
|
|
63642
63622
|
/** Optional battery level of the tracking device (0..100). Null
|
|
63643
63623
|
* when not reported. */
|
|
63644
|
-
batteryPercent: number().min(0).max(100).nullable(),
|
|
63624
|
+
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63645
63625
|
/** Ms epoch when the slice was last updated. */
|
|
63646
63626
|
lastChangedAt: number()
|
|
63647
63627
|
});
|
|
@@ -63691,19 +63671,10 @@ var presenceCapability = {
|
|
|
63691
63671
|
* `sourceInfo`.
|
|
63692
63672
|
*/
|
|
63693
63673
|
var PressureSensorStatusSchema = object({
|
|
63694
|
-
/** Current pressure in hPa. */
|
|
63695
|
-
hpa: number(),
|
|
63674
|
+
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
63675
|
+
hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
63696
63676
|
/** Ms epoch when the slice was last updated. */
|
|
63697
|
-
lastFetchedAt: number()
|
|
63698
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
63699
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
63700
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
63701
|
-
unit: string().optional(),
|
|
63702
|
-
/** Suggested decimal places for numeric display.
|
|
63703
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
63704
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
63705
|
-
* auto-formatting when absent. */
|
|
63706
|
-
precision: number().int().min(0).max(10).optional()
|
|
63677
|
+
lastFetchedAt: number()
|
|
63707
63678
|
});
|
|
63708
63679
|
var pressureSensorCapability = {
|
|
63709
63680
|
name: "pressure-sensor",
|
|
@@ -64117,8 +64088,8 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
|
|
|
64117
64088
|
}), _void(), { kind: "mutation" });
|
|
64118
64089
|
var PtzAutotrackSettingsSchema = object({
|
|
64119
64090
|
targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
|
|
64120
|
-
stopDelaySeconds: number().int().min(0).max(300),
|
|
64121
|
-
disappearDelaySeconds: number().int().min(0).max(300)
|
|
64091
|
+
stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
|
|
64092
|
+
disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
|
|
64122
64093
|
});
|
|
64123
64094
|
/**
|
|
64124
64095
|
* One option the camera advertises as a valid `targetType`. Drivers
|
|
@@ -64272,7 +64243,7 @@ var RunningStateStatusSchema = object({
|
|
|
64272
64243
|
/** null = unknown — never coerced to false (D393). */
|
|
64273
64244
|
running: boolean().nullable(),
|
|
64274
64245
|
/** Epoch ms the current boolean began; null while `running` is null. */
|
|
64275
|
-
since: number().nullable(),
|
|
64246
|
+
since: number().nullable().meta(readingMeta("none")),
|
|
64276
64247
|
source: RunningStateSourceSchema.meta({ label: "Source" }),
|
|
64277
64248
|
phase: string().nullable(),
|
|
64278
64249
|
/** null until a reading has been published (D393). */
|
|
@@ -64287,7 +64258,6 @@ var RUNNING_DEVICE_TYPES = Object.values(DeviceType);
|
|
|
64287
64258
|
/** Generic Customize target for the running boolean (D936). */
|
|
64288
64259
|
var runningCustomize = {
|
|
64289
64260
|
version: 1,
|
|
64290
|
-
advanced: "derive",
|
|
64291
64261
|
targets: [{
|
|
64292
64262
|
id: "running",
|
|
64293
64263
|
cardinality: "scalar",
|
|
@@ -64307,7 +64277,7 @@ var runningCustomize = {
|
|
|
64307
64277
|
DeviceType.Thermostat,
|
|
64308
64278
|
DeviceType.PetFeeder
|
|
64309
64279
|
],
|
|
64310
|
-
apply: "add
|
|
64280
|
+
apply: "add",
|
|
64311
64281
|
path: "running",
|
|
64312
64282
|
sources: [
|
|
64313
64283
|
{
|
|
@@ -65571,9 +65541,9 @@ var RecordTriggerSchema = _enum([
|
|
|
65571
65541
|
*/
|
|
65572
65542
|
var RecordWindowSchema = object({
|
|
65573
65543
|
trigger: RecordTriggerSchema,
|
|
65574
|
-
day: number().int().min(0).max(6),
|
|
65575
|
-
startMinute: number().int().min(0).max(1439),
|
|
65576
|
-
endMinute: number().int().min(1).max(1440)
|
|
65544
|
+
day: number().int().min(0).max(6).meta(readingMeta("none")),
|
|
65545
|
+
startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
|
|
65546
|
+
endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
|
|
65577
65547
|
});
|
|
65578
65548
|
/** Status of one physical volume, as the camera itself describes it. */
|
|
65579
65549
|
var OnboardStorageVolumeSchema = object({
|
|
@@ -65649,10 +65619,10 @@ var RecordingOnboardStatusSchema = object({
|
|
|
65649
65619
|
/** Master "record to the card at all" switch. */
|
|
65650
65620
|
enabled: boolean().nullable(),
|
|
65651
65621
|
overwriteWhenFull: boolean().nullable(),
|
|
65652
|
-
preRecordSec: number().nullable(),
|
|
65653
|
-
postRecordSec: number().nullable(),
|
|
65622
|
+
preRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
65623
|
+
postRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
65654
65624
|
/** Length of one recorded file, in minutes. */
|
|
65655
|
-
segmentMinutes: number().nullable(),
|
|
65625
|
+
segmentMinutes: number().nullable().meta(readingMeta("none")),
|
|
65656
65626
|
/** The primary track's weekly windows, flattened. */
|
|
65657
65627
|
windows: array(RecordWindowSchema),
|
|
65658
65628
|
/**
|
|
@@ -65668,7 +65638,7 @@ var RecordingOnboardStatusSchema = object({
|
|
|
65668
65638
|
* provider reporting a non-zero count also reports the schedule as not
|
|
65669
65639
|
* writable.
|
|
65670
65640
|
*/
|
|
65671
|
-
unreadableWindows: number(),
|
|
65641
|
+
unreadableWindows: number().meta(readingMeta("count")),
|
|
65672
65642
|
/**
|
|
65673
65643
|
* The camera is scheduled to record and has NO usable storage.
|
|
65674
65644
|
*
|
|
@@ -66513,7 +66483,7 @@ var CamStreamDescriptorSchema = object({
|
|
|
66513
66483
|
url: string().optional(),
|
|
66514
66484
|
codec: string().optional(),
|
|
66515
66485
|
resolution: CamStreamResolutionSchema.optional(),
|
|
66516
|
-
fps: number().positive().optional(),
|
|
66486
|
+
fps: number().positive().optional().meta(readingMeta("rate")),
|
|
66517
66487
|
label: string().optional(),
|
|
66518
66488
|
/** Device-level features (e.g. `battery-operated`) — drives broker policy. */
|
|
66519
66489
|
deviceFeatures: array(string()).optional(),
|
|
@@ -66600,18 +66570,18 @@ var StreamProfileSchema = _enum([
|
|
|
66600
66570
|
]);
|
|
66601
66571
|
/** The current encoding config of one stream profile. */
|
|
66602
66572
|
var StreamProfileConfigSchema = object({
|
|
66603
|
-
width: number(),
|
|
66604
|
-
height: number(),
|
|
66573
|
+
width: number().meta(readingMeta("none")),
|
|
66574
|
+
height: number().meta(readingMeta("none")),
|
|
66605
66575
|
codec: _enum(["h264", "h265"]),
|
|
66606
|
-
framerate: number(),
|
|
66607
|
-
bitrate: number(),
|
|
66576
|
+
framerate: number().meta(readingMeta("rate")),
|
|
66577
|
+
bitrate: number().meta(readingMeta("bitrate")),
|
|
66608
66578
|
bitrateMode: _enum(["vbr", "cbr"]).optional(),
|
|
66609
66579
|
encoderProfile: _enum([
|
|
66610
66580
|
"high",
|
|
66611
66581
|
"main",
|
|
66612
66582
|
"baseline"
|
|
66613
66583
|
]).optional(),
|
|
66614
|
-
gop: number().optional(),
|
|
66584
|
+
gop: number().optional().meta(readingMeta("none")),
|
|
66615
66585
|
/**
|
|
66616
66586
|
* Whether THIS profile currently carries an audio track. READ-ONLY here.
|
|
66617
66587
|
*
|
|
@@ -66832,12 +66802,7 @@ var switchCapability = {
|
|
|
66832
66802
|
label: "On",
|
|
66833
66803
|
kind: "boolean"
|
|
66834
66804
|
}
|
|
66835
|
-
}] }
|
|
66836
|
-
customize: {
|
|
66837
|
-
version: 1,
|
|
66838
|
-
advanced: "derive",
|
|
66839
|
-
targets: []
|
|
66840
|
-
}
|
|
66805
|
+
}] }
|
|
66841
66806
|
};
|
|
66842
66807
|
/** C0 controls, DEL and the C1 range: none of them belongs in a name. */
|
|
66843
66808
|
function hasControlCharacter(value) {
|
|
@@ -67420,35 +67385,21 @@ var tamperCapability = {
|
|
|
67420
67385
|
* entries with `device_class: temperature` and any future native
|
|
67421
67386
|
* thermometer.
|
|
67422
67387
|
*
|
|
67423
|
-
*
|
|
67424
|
-
*
|
|
67425
|
-
* (climate-control's `currentTemp`, energy analytics) can compose
|
|
67426
|
-
* without per-source unit fixups. The canonical display unit (`°C`) is
|
|
67427
|
-
* a descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
67428
|
-
* `sourceInfo`.
|
|
67388
|
+
* The number is Celsius (D944). Providers convert on ingest. A failed
|
|
67389
|
+
* conversion is `null`, never a guess. Display unit is a read-time preference.
|
|
67429
67390
|
*
|
|
67430
67391
|
* Status `lastFetchedAt` lets staleness-aware consumers detect a
|
|
67431
67392
|
* frozen feed (provider hung) distinct from a "temperature hasn't
|
|
67432
67393
|
* changed" steady state.
|
|
67433
67394
|
*/
|
|
67434
67395
|
var TemperatureSensorStatusSchema = object({
|
|
67435
|
-
/** Current temperature in Celsius. */
|
|
67436
|
-
celsius: number().meta({
|
|
67437
|
-
|
|
67396
|
+
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
67397
|
+
celsius: number().nullable().meta(readingMeta("temperature", {
|
|
67398
|
+
precision: 1,
|
|
67438
67399
|
label: "Temperature"
|
|
67439
|
-
}),
|
|
67400
|
+
})),
|
|
67440
67401
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
67441
|
-
lastFetchedAt: number()
|
|
67442
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
67443
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
67444
|
-
* role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
|
|
67445
|
-
* Absent → the UI falls back to the role's canonical unit. */
|
|
67446
|
-
unit: string().optional(),
|
|
67447
|
-
/** Suggested decimal places for numeric display.
|
|
67448
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
67449
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
67450
|
-
* auto-formatting when absent. */
|
|
67451
|
-
precision: number().int().min(0).max(10).optional()
|
|
67402
|
+
lastFetchedAt: number()
|
|
67452
67403
|
});
|
|
67453
67404
|
var temperatureSensorCapability = {
|
|
67454
67405
|
name: "temperature-sensor",
|
|
@@ -67479,12 +67430,7 @@ var temperatureSensorCapability = {
|
|
|
67479
67430
|
} } },
|
|
67480
67431
|
/** Clock fields: written, but excluded from the compare that decides
|
|
67481
67432
|
* whether persisting is worth a SQLite commit. */
|
|
67482
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
67483
|
-
customize: {
|
|
67484
|
-
version: 1,
|
|
67485
|
-
advanced: "derive",
|
|
67486
|
-
targets: []
|
|
67487
|
-
}
|
|
67433
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
67488
67434
|
};
|
|
67489
67435
|
/**
|
|
67490
67436
|
* toast — system-scoped singleton capability that streams toast
|
|
@@ -67767,16 +67713,16 @@ var VacuumControlStatusSchema = object({
|
|
|
67767
67713
|
]),
|
|
67768
67714
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
67769
67715
|
* reading. */
|
|
67770
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
67716
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
67771
67717
|
/** Current fan-speed token (provider-verbatim). Null when unknown or
|
|
67772
67718
|
* the vacuum has no speed control. */
|
|
67773
67719
|
fanSpeed: string().nullable(),
|
|
67774
67720
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
67775
67721
|
availableFanSpeeds: array(string()),
|
|
67776
67722
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
67777
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
67723
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
67778
67724
|
/** Current error code (0 / null = no error). */
|
|
67779
|
-
errorCode: number().nullable(),
|
|
67725
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
67780
67726
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
67781
67727
|
errorLabel: string().nullable(),
|
|
67782
67728
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -67854,7 +67800,7 @@ var ValveStatusSchema = object({
|
|
|
67854
67800
|
]),
|
|
67855
67801
|
/** 0 = fully closed, 100 = fully open. Null when the device has no
|
|
67856
67802
|
* intermediate position surface. */
|
|
67857
|
-
position: number().min(0).max(100).nullable(),
|
|
67803
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
67858
67804
|
/** Ms epoch when the slice was last updated. */
|
|
67859
67805
|
lastChangedAt: number()
|
|
67860
67806
|
});
|
|
@@ -67962,9 +67908,9 @@ var vibrationCapability = {
|
|
|
67962
67908
|
*/
|
|
67963
67909
|
var WaterHeaterStatusSchema = object({
|
|
67964
67910
|
/** Current measured temperature. Null when not reported. */
|
|
67965
|
-
currentTemp: number().nullable(),
|
|
67911
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
67966
67912
|
/** Target temperature setpoint. Null when no setpoint surface. */
|
|
67967
|
-
targetTemp: number().nullable(),
|
|
67913
|
+
targetTemp: number().nullable().meta(readingMeta("temperature")),
|
|
67968
67914
|
/** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
|
|
67969
67915
|
* `heat_pump` / `high_demand` / `performance` / `off`). Null when the
|
|
67970
67916
|
* device reports an unknown state. */
|
|
@@ -67975,9 +67921,9 @@ var WaterHeaterStatusSchema = object({
|
|
|
67975
67921
|
* has no away surface. */
|
|
67976
67922
|
away: boolean().nullable(),
|
|
67977
67923
|
/** HA `min_temp` attribute. Null when not reported. */
|
|
67978
|
-
minTemp: number().nullable(),
|
|
67924
|
+
minTemp: number().nullable().meta(readingMeta("temperature")),
|
|
67979
67925
|
/** HA `max_temp` attribute. Null when not reported. */
|
|
67980
|
-
maxTemp: number().nullable(),
|
|
67926
|
+
maxTemp: number().nullable().meta(readingMeta("temperature")),
|
|
67981
67927
|
/** Ms epoch when the slice was last updated. */
|
|
67982
67928
|
lastChangedAt: number()
|
|
67983
67929
|
});
|
|
@@ -68062,25 +68008,18 @@ var waterHeaterCapability = {
|
|
|
68062
68008
|
* Forecast deferred — current conditions only for v1.
|
|
68063
68009
|
*/
|
|
68064
68010
|
var WeatherStatusSchema = object({
|
|
68065
|
-
/** Verbatim
|
|
68066
|
-
* when no condition has been reported yet. */
|
|
68011
|
+
/** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
|
|
68067
68012
|
condition: string().nullable(),
|
|
68068
|
-
/** Current temperature in
|
|
68069
|
-
temperature: number().nullable(),
|
|
68070
|
-
/** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
|
|
68071
|
-
temperatureUnit: string().nullable(),
|
|
68013
|
+
/** Current temperature in Celsius. Null when not provided or refused. */
|
|
68014
|
+
temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
|
|
68072
68015
|
/** Relative humidity (0..100). Null when not provided. */
|
|
68073
|
-
humidity: number().min(0).max(100).nullable(),
|
|
68074
|
-
/** Barometric pressure in
|
|
68075
|
-
pressure: number().nullable(),
|
|
68076
|
-
/**
|
|
68077
|
-
|
|
68078
|
-
/** Wind speed in the reported unit. Null when not provided. */
|
|
68079
|
-
windSpeed: number().nullable(),
|
|
68080
|
-
/** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
|
|
68081
|
-
windSpeedUnit: string().nullable(),
|
|
68016
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
68017
|
+
/** Barometric pressure in hPa. Null when not provided or refused. */
|
|
68018
|
+
pressure: number().nullable().meta(readingMeta("pressure")),
|
|
68019
|
+
/** Wind speed in metres per second. Null when not provided or refused. */
|
|
68020
|
+
windSpeed: number().nullable().meta(readingMeta("speed")),
|
|
68082
68021
|
/** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
|
|
68083
|
-
windBearing: number().nullable(),
|
|
68022
|
+
windBearing: number().nullable().meta(readingMeta("angle")),
|
|
68084
68023
|
/** Ms epoch when the slice was last updated. */
|
|
68085
68024
|
lastFetchedAt: number()
|
|
68086
68025
|
});
|
|
@@ -68108,12 +68047,12 @@ var weatherCapability = {
|
|
|
68108
68047
|
*/
|
|
68109
68048
|
durability: "session",
|
|
68110
68049
|
/**
|
|
68111
|
-
* History (D850): recorded series.
|
|
68050
|
+
* History (D850): recorded series. Numbers are the quantity base (D944).
|
|
68112
68051
|
*/
|
|
68113
68052
|
history: { fields: {
|
|
68114
68053
|
temperature: {
|
|
68115
68054
|
kind: "gauge",
|
|
68116
|
-
|
|
68055
|
+
unit: "°C",
|
|
68117
68056
|
deadband: .2
|
|
68118
68057
|
},
|
|
68119
68058
|
humidity: {
|
|
@@ -68123,12 +68062,12 @@ var weatherCapability = {
|
|
|
68123
68062
|
},
|
|
68124
68063
|
pressure: {
|
|
68125
68064
|
kind: "gauge",
|
|
68126
|
-
|
|
68065
|
+
unit: "hPa",
|
|
68127
68066
|
deadband: .5
|
|
68128
68067
|
},
|
|
68129
68068
|
windSpeed: {
|
|
68130
68069
|
kind: "gauge",
|
|
68131
|
-
|
|
68070
|
+
unit: "m/s",
|
|
68132
68071
|
deadband: .5
|
|
68133
68072
|
}
|
|
68134
68073
|
} }
|
|
@@ -69029,6 +68968,389 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
69029
68968
|
kind: "mutation",
|
|
69030
68969
|
auth: "admin"
|
|
69031
68970
|
});
|
|
68971
|
+
/**
|
|
68972
|
+
* Frozen unit → spec table. `•` in the doc marks each dimension's canonical
|
|
68973
|
+
* unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
|
|
68974
|
+
* `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
|
|
68975
|
+
* decimals). K derives from `°C = K − 273.15`.
|
|
68976
|
+
*/
|
|
68977
|
+
var UNIT_TABLE = {
|
|
68978
|
+
"°C": {
|
|
68979
|
+
dimension: "temperature",
|
|
68980
|
+
factor: 1,
|
|
68981
|
+
offset: 0
|
|
68982
|
+
},
|
|
68983
|
+
"°F": {
|
|
68984
|
+
dimension: "temperature",
|
|
68985
|
+
factor: 5 / 9,
|
|
68986
|
+
offset: -160 / 9
|
|
68987
|
+
},
|
|
68988
|
+
K: {
|
|
68989
|
+
dimension: "temperature",
|
|
68990
|
+
factor: 1,
|
|
68991
|
+
offset: -273.15
|
|
68992
|
+
},
|
|
68993
|
+
hPa: {
|
|
68994
|
+
dimension: "pressure",
|
|
68995
|
+
factor: 1,
|
|
68996
|
+
offset: 0
|
|
68997
|
+
},
|
|
68998
|
+
kPa: {
|
|
68999
|
+
dimension: "pressure",
|
|
69000
|
+
factor: 10,
|
|
69001
|
+
offset: 0
|
|
69002
|
+
},
|
|
69003
|
+
Pa: {
|
|
69004
|
+
dimension: "pressure",
|
|
69005
|
+
factor: .01,
|
|
69006
|
+
offset: 0
|
|
69007
|
+
},
|
|
69008
|
+
mbar: {
|
|
69009
|
+
dimension: "pressure",
|
|
69010
|
+
factor: 1,
|
|
69011
|
+
offset: 0
|
|
69012
|
+
},
|
|
69013
|
+
bar: {
|
|
69014
|
+
dimension: "pressure",
|
|
69015
|
+
factor: 1e3,
|
|
69016
|
+
offset: 0
|
|
69017
|
+
},
|
|
69018
|
+
inHg: {
|
|
69019
|
+
dimension: "pressure",
|
|
69020
|
+
factor: 33.8639,
|
|
69021
|
+
offset: 0
|
|
69022
|
+
},
|
|
69023
|
+
mmHg: {
|
|
69024
|
+
dimension: "pressure",
|
|
69025
|
+
factor: 1.33322,
|
|
69026
|
+
offset: 0
|
|
69027
|
+
},
|
|
69028
|
+
psi: {
|
|
69029
|
+
dimension: "pressure",
|
|
69030
|
+
factor: 68.9476,
|
|
69031
|
+
offset: 0
|
|
69032
|
+
},
|
|
69033
|
+
"m/s": {
|
|
69034
|
+
dimension: "speed",
|
|
69035
|
+
factor: 1,
|
|
69036
|
+
offset: 0
|
|
69037
|
+
},
|
|
69038
|
+
"km/h": {
|
|
69039
|
+
dimension: "speed",
|
|
69040
|
+
factor: 1 / 3.6,
|
|
69041
|
+
offset: 0
|
|
69042
|
+
},
|
|
69043
|
+
mph: {
|
|
69044
|
+
dimension: "speed",
|
|
69045
|
+
factor: .44704,
|
|
69046
|
+
offset: 0
|
|
69047
|
+
},
|
|
69048
|
+
kn: {
|
|
69049
|
+
dimension: "speed",
|
|
69050
|
+
factor: .514444,
|
|
69051
|
+
offset: 0
|
|
69052
|
+
},
|
|
69053
|
+
"mm/h": {
|
|
69054
|
+
dimension: "precipitation-rate",
|
|
69055
|
+
factor: 1,
|
|
69056
|
+
offset: 0
|
|
69057
|
+
},
|
|
69058
|
+
"in/h": {
|
|
69059
|
+
dimension: "precipitation-rate",
|
|
69060
|
+
factor: 25.4,
|
|
69061
|
+
offset: 0
|
|
69062
|
+
},
|
|
69063
|
+
m: {
|
|
69064
|
+
dimension: "length",
|
|
69065
|
+
factor: 1,
|
|
69066
|
+
offset: 0
|
|
69067
|
+
},
|
|
69068
|
+
mm: {
|
|
69069
|
+
dimension: "length",
|
|
69070
|
+
factor: .001,
|
|
69071
|
+
offset: 0
|
|
69072
|
+
},
|
|
69073
|
+
cm: {
|
|
69074
|
+
dimension: "length",
|
|
69075
|
+
factor: .01,
|
|
69076
|
+
offset: 0
|
|
69077
|
+
},
|
|
69078
|
+
km: {
|
|
69079
|
+
dimension: "length",
|
|
69080
|
+
factor: 1e3,
|
|
69081
|
+
offset: 0
|
|
69082
|
+
},
|
|
69083
|
+
in: {
|
|
69084
|
+
dimension: "length",
|
|
69085
|
+
factor: .0254,
|
|
69086
|
+
offset: 0
|
|
69087
|
+
},
|
|
69088
|
+
ft: {
|
|
69089
|
+
dimension: "length",
|
|
69090
|
+
factor: .3048,
|
|
69091
|
+
offset: 0
|
|
69092
|
+
},
|
|
69093
|
+
mi: {
|
|
69094
|
+
dimension: "length",
|
|
69095
|
+
factor: 1609.344,
|
|
69096
|
+
offset: 0
|
|
69097
|
+
},
|
|
69098
|
+
"m²": {
|
|
69099
|
+
dimension: "area",
|
|
69100
|
+
factor: 1,
|
|
69101
|
+
offset: 0
|
|
69102
|
+
},
|
|
69103
|
+
lx: {
|
|
69104
|
+
dimension: "illuminance",
|
|
69105
|
+
factor: 1,
|
|
69106
|
+
offset: 0
|
|
69107
|
+
},
|
|
69108
|
+
"W/m²": {
|
|
69109
|
+
dimension: "irradiance",
|
|
69110
|
+
factor: 1,
|
|
69111
|
+
offset: 0
|
|
69112
|
+
},
|
|
69113
|
+
W: {
|
|
69114
|
+
dimension: "power",
|
|
69115
|
+
factor: 1,
|
|
69116
|
+
offset: 0
|
|
69117
|
+
},
|
|
69118
|
+
kW: {
|
|
69119
|
+
dimension: "power",
|
|
69120
|
+
factor: 1e3,
|
|
69121
|
+
offset: 0
|
|
69122
|
+
},
|
|
69123
|
+
VA: {
|
|
69124
|
+
dimension: "apparent-power",
|
|
69125
|
+
factor: 1,
|
|
69126
|
+
offset: 0
|
|
69127
|
+
},
|
|
69128
|
+
Wh: {
|
|
69129
|
+
dimension: "energy",
|
|
69130
|
+
factor: 1,
|
|
69131
|
+
offset: 0
|
|
69132
|
+
},
|
|
69133
|
+
kWh: {
|
|
69134
|
+
dimension: "energy",
|
|
69135
|
+
factor: 1e3,
|
|
69136
|
+
offset: 0
|
|
69137
|
+
},
|
|
69138
|
+
MWh: {
|
|
69139
|
+
dimension: "energy",
|
|
69140
|
+
factor: 1e6,
|
|
69141
|
+
offset: 0
|
|
69142
|
+
},
|
|
69143
|
+
V: {
|
|
69144
|
+
dimension: "voltage",
|
|
69145
|
+
factor: 1,
|
|
69146
|
+
offset: 0
|
|
69147
|
+
},
|
|
69148
|
+
mV: {
|
|
69149
|
+
dimension: "voltage",
|
|
69150
|
+
factor: .001,
|
|
69151
|
+
offset: 0
|
|
69152
|
+
},
|
|
69153
|
+
A: {
|
|
69154
|
+
dimension: "current",
|
|
69155
|
+
factor: 1,
|
|
69156
|
+
offset: 0
|
|
69157
|
+
},
|
|
69158
|
+
mA: {
|
|
69159
|
+
dimension: "current",
|
|
69160
|
+
factor: .001,
|
|
69161
|
+
offset: 0
|
|
69162
|
+
},
|
|
69163
|
+
ppm: {
|
|
69164
|
+
dimension: "concentration-volume",
|
|
69165
|
+
factor: 1,
|
|
69166
|
+
offset: 0
|
|
69167
|
+
},
|
|
69168
|
+
ppb: {
|
|
69169
|
+
dimension: "concentration-volume",
|
|
69170
|
+
factor: .001,
|
|
69171
|
+
offset: 0
|
|
69172
|
+
},
|
|
69173
|
+
"µg/m³": {
|
|
69174
|
+
dimension: "concentration-mass",
|
|
69175
|
+
factor: 1,
|
|
69176
|
+
offset: 0
|
|
69177
|
+
},
|
|
69178
|
+
"mg/m³": {
|
|
69179
|
+
dimension: "concentration-mass",
|
|
69180
|
+
factor: 1e3,
|
|
69181
|
+
offset: 0
|
|
69182
|
+
},
|
|
69183
|
+
kg: {
|
|
69184
|
+
dimension: "mass",
|
|
69185
|
+
factor: 1,
|
|
69186
|
+
offset: 0
|
|
69187
|
+
},
|
|
69188
|
+
g: {
|
|
69189
|
+
dimension: "mass",
|
|
69190
|
+
factor: .001,
|
|
69191
|
+
offset: 0
|
|
69192
|
+
},
|
|
69193
|
+
lb: {
|
|
69194
|
+
dimension: "mass",
|
|
69195
|
+
factor: .453592,
|
|
69196
|
+
offset: 0
|
|
69197
|
+
},
|
|
69198
|
+
oz: {
|
|
69199
|
+
dimension: "mass",
|
|
69200
|
+
factor: .0283495,
|
|
69201
|
+
offset: 0
|
|
69202
|
+
},
|
|
69203
|
+
"%": {
|
|
69204
|
+
dimension: "percentage",
|
|
69205
|
+
factor: 1,
|
|
69206
|
+
offset: 0
|
|
69207
|
+
},
|
|
69208
|
+
L: {
|
|
69209
|
+
dimension: "volume",
|
|
69210
|
+
factor: 1,
|
|
69211
|
+
offset: 0
|
|
69212
|
+
},
|
|
69213
|
+
mL: {
|
|
69214
|
+
dimension: "volume",
|
|
69215
|
+
factor: .001,
|
|
69216
|
+
offset: 0
|
|
69217
|
+
},
|
|
69218
|
+
gal: {
|
|
69219
|
+
dimension: "volume",
|
|
69220
|
+
factor: 3.785411784,
|
|
69221
|
+
offset: 0
|
|
69222
|
+
},
|
|
69223
|
+
s: {
|
|
69224
|
+
dimension: "duration",
|
|
69225
|
+
factor: 1,
|
|
69226
|
+
offset: 0
|
|
69227
|
+
},
|
|
69228
|
+
ms: {
|
|
69229
|
+
dimension: "duration",
|
|
69230
|
+
factor: .001,
|
|
69231
|
+
offset: 0
|
|
69232
|
+
},
|
|
69233
|
+
min: {
|
|
69234
|
+
dimension: "duration",
|
|
69235
|
+
factor: 60,
|
|
69236
|
+
offset: 0
|
|
69237
|
+
},
|
|
69238
|
+
h: {
|
|
69239
|
+
dimension: "duration",
|
|
69240
|
+
factor: 3600,
|
|
69241
|
+
offset: 0
|
|
69242
|
+
},
|
|
69243
|
+
d: {
|
|
69244
|
+
dimension: "duration",
|
|
69245
|
+
factor: 86400,
|
|
69246
|
+
offset: 0
|
|
69247
|
+
},
|
|
69248
|
+
B: {
|
|
69249
|
+
dimension: "information",
|
|
69250
|
+
factor: 1,
|
|
69251
|
+
offset: 0
|
|
69252
|
+
},
|
|
69253
|
+
KiB: {
|
|
69254
|
+
dimension: "information",
|
|
69255
|
+
factor: 1024,
|
|
69256
|
+
offset: 0
|
|
69257
|
+
},
|
|
69258
|
+
MiB: {
|
|
69259
|
+
dimension: "information",
|
|
69260
|
+
factor: 1024 ** 2,
|
|
69261
|
+
offset: 0
|
|
69262
|
+
},
|
|
69263
|
+
GiB: {
|
|
69264
|
+
dimension: "information",
|
|
69265
|
+
factor: 1024 ** 3,
|
|
69266
|
+
offset: 0
|
|
69267
|
+
},
|
|
69268
|
+
TiB: {
|
|
69269
|
+
dimension: "information",
|
|
69270
|
+
factor: 1024 ** 4,
|
|
69271
|
+
offset: 0
|
|
69272
|
+
},
|
|
69273
|
+
kB: {
|
|
69274
|
+
dimension: "information",
|
|
69275
|
+
factor: 1e3,
|
|
69276
|
+
offset: 0
|
|
69277
|
+
},
|
|
69278
|
+
MB: {
|
|
69279
|
+
dimension: "information",
|
|
69280
|
+
factor: 1e6,
|
|
69281
|
+
offset: 0
|
|
69282
|
+
},
|
|
69283
|
+
GB: {
|
|
69284
|
+
dimension: "information",
|
|
69285
|
+
factor: 1e9,
|
|
69286
|
+
offset: 0
|
|
69287
|
+
},
|
|
69288
|
+
"°": {
|
|
69289
|
+
dimension: "angle",
|
|
69290
|
+
factor: 1,
|
|
69291
|
+
offset: 0
|
|
69292
|
+
},
|
|
69293
|
+
dBFS: {
|
|
69294
|
+
dimension: "sound-level",
|
|
69295
|
+
factor: 1,
|
|
69296
|
+
offset: 0
|
|
69297
|
+
},
|
|
69298
|
+
dBm: {
|
|
69299
|
+
dimension: "signal-level",
|
|
69300
|
+
factor: 1,
|
|
69301
|
+
offset: 0
|
|
69302
|
+
},
|
|
69303
|
+
mired: {
|
|
69304
|
+
dimension: "colour-temperature",
|
|
69305
|
+
factor: 1,
|
|
69306
|
+
offset: 0
|
|
69307
|
+
},
|
|
69308
|
+
kbps: {
|
|
69309
|
+
dimension: "bitrate",
|
|
69310
|
+
factor: 1,
|
|
69311
|
+
offset: 0
|
|
69312
|
+
},
|
|
69313
|
+
Mbps: {
|
|
69314
|
+
dimension: "bitrate",
|
|
69315
|
+
factor: 1e3,
|
|
69316
|
+
offset: 0
|
|
69317
|
+
},
|
|
69318
|
+
fps: {
|
|
69319
|
+
dimension: "rate",
|
|
69320
|
+
factor: 1,
|
|
69321
|
+
offset: 0
|
|
69322
|
+
}
|
|
69323
|
+
};
|
|
69324
|
+
/** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
|
|
69325
|
+
function specFor(unit) {
|
|
69326
|
+
return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
|
|
69327
|
+
}
|
|
69328
|
+
/** The dimension a unit belongs to, or `undefined` for an unknown unit. */
|
|
69329
|
+
function unitDimension(unit) {
|
|
69330
|
+
return specFor(unit)?.dimension;
|
|
69331
|
+
}
|
|
69332
|
+
/** Value in `spec`'s unit → its dimension's canonical unit. */
|
|
69333
|
+
function toCanonical(value, spec) {
|
|
69334
|
+
return value * spec.factor + spec.offset;
|
|
69335
|
+
}
|
|
69336
|
+
/** Canonical-unit value → the unit described by `spec`. */
|
|
69337
|
+
function fromCanonical(canonical, spec) {
|
|
69338
|
+
return (canonical - spec.offset) / spec.factor;
|
|
69339
|
+
}
|
|
69340
|
+
/**
|
|
69341
|
+
* Graceful variant for the render path: returns the converted value, or
|
|
69342
|
+
* `undefined` when the conversion is refused (unknown unit / cross-dimension).
|
|
69343
|
+
* Never throws — a render caller falls back to the raw value + source unit.
|
|
69344
|
+
*/
|
|
69345
|
+
function tryConvertUnit(value, from, to) {
|
|
69346
|
+
if (from === to) return value;
|
|
69347
|
+
const fromSpec = specFor(from);
|
|
69348
|
+
if (!fromSpec) return void 0;
|
|
69349
|
+
const toSpec = specFor(to);
|
|
69350
|
+
if (!toSpec) return void 0;
|
|
69351
|
+
if (fromSpec.dimension !== toSpec.dimension) return void 0;
|
|
69352
|
+
return fromCanonical(toCanonical(value, fromSpec), toSpec);
|
|
69353
|
+
}
|
|
69032
69354
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
69033
69355
|
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;
|
|
69034
69356
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
@@ -83698,7 +84020,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83698
84020
|
field: "value",
|
|
83699
84021
|
quantity: t("the reading", "il valore"),
|
|
83700
84022
|
label: t("Reading", "Valore"),
|
|
83701
|
-
unitField: "unit",
|
|
83702
84023
|
icon: "gauge"
|
|
83703
84024
|
}]
|
|
83704
84025
|
},
|
|
@@ -83834,7 +84155,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83834
84155
|
field: "temperature",
|
|
83835
84156
|
quantity: t("the outdoor temperature", "la temperatura esterna"),
|
|
83836
84157
|
label: t("Temperature", "Temperatura"),
|
|
83837
|
-
|
|
84158
|
+
unit: "°C",
|
|
83838
84159
|
icon: "thermometer"
|
|
83839
84160
|
},
|
|
83840
84161
|
{
|
|
@@ -83850,7 +84171,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83850
84171
|
field: "windSpeed",
|
|
83851
84172
|
quantity: t("the wind speed", "la velocità del vento"),
|
|
83852
84173
|
label: t("Wind", "Vento"),
|
|
83853
|
-
|
|
84174
|
+
unit: "m/s",
|
|
83854
84175
|
icon: "wind"
|
|
83855
84176
|
},
|
|
83856
84177
|
{
|
|
@@ -83858,7 +84179,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83858
84179
|
field: "pressure",
|
|
83859
84180
|
quantity: t("the air pressure", "la pressione atmosferica"),
|
|
83860
84181
|
label: t("Pressure", "Pressione"),
|
|
83861
|
-
|
|
84182
|
+
unit: "hPa",
|
|
83862
84183
|
icon: "gauge"
|
|
83863
84184
|
}
|
|
83864
84185
|
]
|
|
@@ -88223,6 +88544,96 @@ DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
|
|
|
88223
88544
|
DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
88224
88545
|
DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
|
|
88225
88546
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
88547
|
+
/**
|
|
88548
|
+
* Convert an upstream number into the stored base of its quantity (D944 §4).
|
|
88549
|
+
* An unknown unit or a unit from another dimension is a refusal: the caller
|
|
88550
|
+
* logs it and publishes null (D393). This function never guesses.
|
|
88551
|
+
*/
|
|
88552
|
+
var DIMENSION_QUANTITY = {
|
|
88553
|
+
temperature: "temperature",
|
|
88554
|
+
pressure: "pressure",
|
|
88555
|
+
speed: "speed",
|
|
88556
|
+
"precipitation-rate": "precipitation-rate",
|
|
88557
|
+
length: "length",
|
|
88558
|
+
area: "area",
|
|
88559
|
+
illuminance: "illuminance",
|
|
88560
|
+
irradiance: "irradiance",
|
|
88561
|
+
power: "power",
|
|
88562
|
+
"apparent-power": "apparent-power",
|
|
88563
|
+
energy: "energy",
|
|
88564
|
+
voltage: "voltage",
|
|
88565
|
+
current: "current",
|
|
88566
|
+
"concentration-volume": "concentration-volume",
|
|
88567
|
+
"concentration-mass": "concentration-mass",
|
|
88568
|
+
mass: "mass",
|
|
88569
|
+
percentage: "percentage",
|
|
88570
|
+
volume: "volume",
|
|
88571
|
+
duration: "duration",
|
|
88572
|
+
information: "information",
|
|
88573
|
+
angle: "angle",
|
|
88574
|
+
"sound-level": "sound-level",
|
|
88575
|
+
"signal-level": "signal-level",
|
|
88576
|
+
"colour-temperature": "colour-temperature",
|
|
88577
|
+
bitrate: "bitrate",
|
|
88578
|
+
rate: "rate"
|
|
88579
|
+
};
|
|
88580
|
+
/**
|
|
88581
|
+
* Quantity implied by a unit symbol. `ppb` and `ms` are the two symbols
|
|
88582
|
+
* whose stored base is not the dimension's canonical unit. `mm` is length;
|
|
88583
|
+
* precipitation is chosen by the caller (a device class), not by the symbol.
|
|
88584
|
+
*/
|
|
88585
|
+
function quantityFromUnit(rawUnit) {
|
|
88586
|
+
const unit = normalizeUnit(rawUnit);
|
|
88587
|
+
if (unit === void 0) return void 0;
|
|
88588
|
+
if (unit === "ppb") return "concentration-ppb";
|
|
88589
|
+
if (unit === "ms") return "duration-ms";
|
|
88590
|
+
const dimension = unitDimension(unit);
|
|
88591
|
+
if (dimension === void 0) return void 0;
|
|
88592
|
+
return DIMENSION_QUANTITY[dimension];
|
|
88593
|
+
}
|
|
88594
|
+
function ingestReading(input) {
|
|
88595
|
+
if (!Number.isFinite(input.value)) return {
|
|
88596
|
+
ok: false,
|
|
88597
|
+
reason: "non-finite"
|
|
88598
|
+
};
|
|
88599
|
+
if (input.quantity === "device") return {
|
|
88600
|
+
ok: false,
|
|
88601
|
+
reason: "unknown-unit"
|
|
88602
|
+
};
|
|
88603
|
+
const raw = input.rawUnit?.trim();
|
|
88604
|
+
if (raw === void 0 || raw.length === 0) return {
|
|
88605
|
+
ok: true,
|
|
88606
|
+
value: input.value
|
|
88607
|
+
};
|
|
88608
|
+
const unit = normalizeUnit(raw);
|
|
88609
|
+
if (unit === void 0) return {
|
|
88610
|
+
ok: false,
|
|
88611
|
+
reason: "unknown-unit"
|
|
88612
|
+
};
|
|
88613
|
+
const base = baseUnitOf(input.quantity);
|
|
88614
|
+
if (base === void 0) {
|
|
88615
|
+
if (unitDimension(unit) !== void 0) return {
|
|
88616
|
+
ok: false,
|
|
88617
|
+
reason: "cross-dimension"
|
|
88618
|
+
};
|
|
88619
|
+
return {
|
|
88620
|
+
ok: false,
|
|
88621
|
+
reason: "unknown-unit"
|
|
88622
|
+
};
|
|
88623
|
+
}
|
|
88624
|
+
const converted = tryConvertUnit(input.value, unit, base);
|
|
88625
|
+
if (converted === void 0) return unitDimension(unit) === void 0 ? {
|
|
88626
|
+
ok: false,
|
|
88627
|
+
reason: "unknown-unit"
|
|
88628
|
+
} : {
|
|
88629
|
+
ok: false,
|
|
88630
|
+
reason: "cross-dimension"
|
|
88631
|
+
};
|
|
88632
|
+
return {
|
|
88633
|
+
ok: true,
|
|
88634
|
+
value: converted
|
|
88635
|
+
};
|
|
88636
|
+
}
|
|
88226
88637
|
var MB = 1024 * 1024;
|
|
88227
88638
|
1024 * MB, 3072 * MB;
|
|
88228
88639
|
//#endregion
|
|
@@ -94609,6 +95020,23 @@ function capForQuantity(quantity) {
|
|
|
94609
95020
|
* the generic `numeric-sensor` cap but carry a typed role, so the UI renders a
|
|
94610
95021
|
* wind / rain / UV / solar glyph instead of a bare hash.
|
|
94611
95022
|
*/
|
|
95023
|
+
/** Ecowitt quantity → the stored reading quantity. Unknown names return null. */
|
|
95024
|
+
function readingQuantityFor(quantity) {
|
|
95025
|
+
switch (quantity) {
|
|
95026
|
+
case "temperature": return "temperature";
|
|
95027
|
+
case "humidity": return "humidity";
|
|
95028
|
+
case "pressure": return "pressure";
|
|
95029
|
+
case "wind_speed": return "speed";
|
|
95030
|
+
case "wind_direction": return "angle";
|
|
95031
|
+
case "precipitation": return "precipitation";
|
|
95032
|
+
case "precipitation_rate": return "precipitation-rate";
|
|
95033
|
+
case "uv": return "index";
|
|
95034
|
+
case "irradiance": return "irradiance";
|
|
95035
|
+
case "lightning_distance": return "length";
|
|
95036
|
+
case "lightning_count": return "count";
|
|
95037
|
+
default: return null;
|
|
95038
|
+
}
|
|
95039
|
+
}
|
|
94612
95040
|
function roleForQuantity(quantity) {
|
|
94613
95041
|
switch (quantity) {
|
|
94614
95042
|
case "temperature": return DeviceRole.TemperatureSensor;
|
|
@@ -94867,13 +95295,6 @@ var ecowittSensorSchema = object({
|
|
|
94867
95295
|
system: literal("ecowitt").optional(),
|
|
94868
95296
|
integrationId: string().optional()
|
|
94869
95297
|
});
|
|
94870
|
-
/**
|
|
94871
|
-
* Slice key the V-battery `numeric-sensor` cap writes/reads. It is the cap's own
|
|
94872
|
-
* name (`'numeric-sensor'`) — the same key the codegen `getStatus` reads — so
|
|
94873
|
-
* the manual `setCapState` here lands in the slice the framework serves. Only
|
|
94874
|
-
* registered when the PRIMARY cap is NOT itself `numeric-sensor` (no collision).
|
|
94875
|
-
*/
|
|
94876
|
-
var BATTERY_VOLTAGE_CAP = numericSensorCapability.name;
|
|
94877
95298
|
var SUPPORTED_CAPS = new Set([
|
|
94878
95299
|
"temperature-sensor",
|
|
94879
95300
|
"humidity-sensor",
|
|
@@ -94892,16 +95313,10 @@ var SUPPORTED_CAPS = new Set([
|
|
|
94892
95313
|
* library discriminates two battery forms via `batteryUnit`:
|
|
94893
95314
|
* - `'%'` (binary/bar batteries, already decoded to a percent) → the typed
|
|
94894
95315
|
* `battery` cap (which models a 0..100 percentage).
|
|
94895
|
-
* - `'V'` (raw voltage, e.g. WS90/WH90/WS85 capacitor + soil/leaf push) →
|
|
94896
|
-
* `
|
|
94897
|
-
*
|
|
94898
|
-
*
|
|
94899
|
-
*
|
|
94900
|
-
* A child registers AT MOST one battery cap. When the child's PRIMARY sensor cap
|
|
94901
|
-
* is itself `numeric-sensor` (e.g. a precipitation channel) a V-battery cannot
|
|
94902
|
-
* also be hosted as a second `numeric-sensor` on the same device (the cap slice
|
|
94903
|
-
* is keyed by cap name) — that rare case is skipped with a debug note rather
|
|
94904
|
-
* than adding a new cap.
|
|
95316
|
+
* - `'V'` (raw voltage, e.g. WS90/WH90/WS85 capacitor + soil/leaf push) →
|
|
95317
|
+
* `battery.volts`. A voltage has no honest percentage, so `percentage`
|
|
95318
|
+
* stays null. The field lives on `battery`, so a numeric primary sensor
|
|
95319
|
+
* and a voltage battery do not share a slice.
|
|
94905
95320
|
*/
|
|
94906
95321
|
var EcowittSensorDevice = class extends BaseDevice {
|
|
94907
95322
|
features = [];
|
|
@@ -94909,12 +95324,12 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
94909
95324
|
gatewayId;
|
|
94910
95325
|
capName;
|
|
94911
95326
|
valueKey;
|
|
95327
|
+
ecowittQuantity;
|
|
94912
95328
|
/**
|
|
94913
95329
|
* Which battery cap (if any) was registered for this sensor:
|
|
94914
95330
|
* - `'percent'` → the typed `battery` cap (percentage),
|
|
94915
|
-
* - `'voltage'` →
|
|
94916
|
-
* - `'none'` → no battery cap
|
|
94917
|
-
* whose primary cap is already `numeric-sensor`).
|
|
95331
|
+
* - `'voltage'` → `battery.volts` (percentage stays null),
|
|
95332
|
+
* - `'none'` → no battery cap.
|
|
94918
95333
|
*/
|
|
94919
95334
|
batteryMode = "none";
|
|
94920
95335
|
snapshotUnsub = null;
|
|
@@ -94926,10 +95341,13 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
94926
95341
|
const cap = capForQuantity(persisted.quantity);
|
|
94927
95342
|
this.capName = SUPPORTED_CAPS.has(cap) ? cap : "numeric-sensor";
|
|
94928
95343
|
this.valueKey = valueFieldForCap(this.capName);
|
|
95344
|
+
this.ecowittQuantity = String(persisted.quantity);
|
|
94929
95345
|
this.online = true;
|
|
95346
|
+
const named = readingQuantityFor(persisted.quantity);
|
|
94930
95347
|
this.updateSourceInfo({
|
|
94931
95348
|
id: this.sensorId,
|
|
94932
|
-
system: "ecowitt"
|
|
95349
|
+
system: "ecowitt",
|
|
95350
|
+
...this.capName === "numeric-sensor" && named !== null ? { quantity: named } : {}
|
|
94933
95351
|
});
|
|
94934
95352
|
}
|
|
94935
95353
|
resolveSensor() {
|
|
@@ -94973,10 +95391,10 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
94973
95391
|
const sensor = this.resolveSensor();
|
|
94974
95392
|
if (sensor === null) return;
|
|
94975
95393
|
const lastFetchedAt = sensor.lastUpdated || Date.now();
|
|
95394
|
+
const stored = this.toBase(sensor.value, sensor.unit);
|
|
94976
95395
|
const slice = {
|
|
94977
|
-
[this.valueKey]: clampForCap(this.capName,
|
|
94978
|
-
lastFetchedAt
|
|
94979
|
-
...sensor.unit.length > 0 ? { unit: sensor.unit } : {}
|
|
95396
|
+
[this.valueKey]: stored === null ? null : clampForCap(this.capName, stored),
|
|
95397
|
+
lastFetchedAt
|
|
94980
95398
|
};
|
|
94981
95399
|
this.runtimeState.setCapState(this.capName, slice);
|
|
94982
95400
|
if (this.batteryMode === "percent" && sensor.batteryUnit === "%" && typeof sensor.battery === "number") {
|
|
@@ -94990,11 +95408,13 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
94990
95408
|
}
|
|
94991
95409
|
if (this.batteryMode === "voltage" && sensor.batteryUnit === "V" && typeof sensor.battery === "number" && Number.isFinite(sensor.battery)) {
|
|
94992
95410
|
const next = {
|
|
94993
|
-
|
|
94994
|
-
|
|
94995
|
-
|
|
95411
|
+
percentage: null,
|
|
95412
|
+
volts: sensor.battery,
|
|
95413
|
+
charging: "none",
|
|
95414
|
+
sleeping: false,
|
|
95415
|
+
lastUpdated: lastFetchedAt
|
|
94996
95416
|
};
|
|
94997
|
-
this.runtimeState.setCapState(
|
|
95417
|
+
this.runtimeState.setCapState("battery", next);
|
|
94998
95418
|
}
|
|
94999
95419
|
}
|
|
95000
95420
|
registerSensorCap() {
|
|
@@ -95047,11 +95467,8 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
95047
95467
|
}
|
|
95048
95468
|
}
|
|
95049
95469
|
/**
|
|
95050
|
-
* Register the
|
|
95051
|
-
*
|
|
95052
|
-
* - `'V'` → `numeric-sensor` cap carrying volts (unit `'V'`), unless the
|
|
95053
|
-
* sensor's primary cap is already `numeric-sensor` (slice-key collision),
|
|
95054
|
-
* in which case it is skipped with a debug note.
|
|
95470
|
+
* Register the `battery` cap. Percent batteries fill `percentage`. Voltage
|
|
95471
|
+
* batteries fill `volts` and leave `percentage` null.
|
|
95055
95472
|
*/
|
|
95056
95473
|
maybeRegisterBatteryCap() {
|
|
95057
95474
|
const sensor = this.resolveSensor();
|
|
@@ -95078,29 +95495,63 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
95078
95495
|
this.runtimeState.setCapState("battery", COLD);
|
|
95079
95496
|
this.batteryMode = "percent";
|
|
95080
95497
|
}
|
|
95081
|
-
/**
|
|
95082
|
-
* Register a `numeric-sensor` (unit `'V'`) cap for a raw-voltage battery. The
|
|
95083
|
-
* voltage has no honest percentage, so the percentage-shaped `battery` cap is
|
|
95084
|
-
* not used. Skipped when the primary cap already occupies the `numeric-sensor`
|
|
95085
|
-
* slice on this device (a second instance would collide).
|
|
95086
|
-
*/
|
|
95498
|
+
/** Voltage batteries share the `battery` cap. `percentage` stays unknown. */
|
|
95087
95499
|
registerVoltageBatteryCap() {
|
|
95088
|
-
if (this.capName === "numeric-sensor") {
|
|
95089
|
-
this.ctx.logger.debug("ecowitt sensor: V-battery not surfaced — primary cap already numeric-sensor", { meta: { sensorId: this.sensorId } });
|
|
95090
|
-
return;
|
|
95091
|
-
}
|
|
95092
95500
|
const COLD = {
|
|
95093
|
-
|
|
95094
|
-
|
|
95095
|
-
|
|
95501
|
+
percentage: null,
|
|
95502
|
+
volts: null,
|
|
95503
|
+
charging: "unknown",
|
|
95504
|
+
sleeping: false,
|
|
95505
|
+
lastUpdated: 0
|
|
95096
95506
|
};
|
|
95097
|
-
this.ctx.registerNativeCap(
|
|
95098
|
-
|
|
95507
|
+
this.ctx.registerNativeCap(batteryCapability, {
|
|
95508
|
+
getStatus: async () => this.runtimeState.getCapState("battery") ?? COLD,
|
|
95509
|
+
wakeForStream: async () => ({
|
|
95510
|
+
awoke: true,
|
|
95511
|
+
durationMs: 0
|
|
95512
|
+
})
|
|
95513
|
+
});
|
|
95514
|
+
this.runtimeState.setCapState("battery", COLD);
|
|
95099
95515
|
this.batteryMode = "voltage";
|
|
95100
95516
|
}
|
|
95101
95517
|
readSlice(fallback) {
|
|
95102
95518
|
return this.runtimeState.getCapState(this.capName) ?? fallback;
|
|
95103
95519
|
}
|
|
95520
|
+
/** Station number → the quantity's base. An unknown unit publishes null. */
|
|
95521
|
+
toBase(value, unit) {
|
|
95522
|
+
const named = readingQuantityFor(this.ecowittQuantity);
|
|
95523
|
+
const fromUnit = unit.length === 0 ? void 0 : quantityFromUnit(unit);
|
|
95524
|
+
const quantity = named ?? (unit.length === 0 ? "none" : fromUnit ?? null);
|
|
95525
|
+
if (quantity === null || quantity === "device") {
|
|
95526
|
+
this.ctx.logger.warn("numeric reading refused", {
|
|
95527
|
+
tags: { deviceId: this.id },
|
|
95528
|
+
meta: {
|
|
95529
|
+
quantity: "device",
|
|
95530
|
+
unit,
|
|
95531
|
+
reason: "unknown-unit"
|
|
95532
|
+
}
|
|
95533
|
+
});
|
|
95534
|
+
return null;
|
|
95535
|
+
}
|
|
95536
|
+
const result = ingestReading({
|
|
95537
|
+
value,
|
|
95538
|
+
rawUnit: unit.length > 0 ? unit : void 0,
|
|
95539
|
+
quantity
|
|
95540
|
+
});
|
|
95541
|
+
if (!result.ok) {
|
|
95542
|
+
this.ctx.logger.warn("numeric reading refused", {
|
|
95543
|
+
tags: { deviceId: this.id },
|
|
95544
|
+
meta: {
|
|
95545
|
+
quantity,
|
|
95546
|
+
unit,
|
|
95547
|
+
reason: result.reason
|
|
95548
|
+
}
|
|
95549
|
+
});
|
|
95550
|
+
return null;
|
|
95551
|
+
}
|
|
95552
|
+
if (this.capName === "numeric-sensor" && this.sourceInfo.quantity !== quantity) this.updateSourceInfo({ quantity });
|
|
95553
|
+
return result.value;
|
|
95554
|
+
}
|
|
95104
95555
|
};
|
|
95105
95556
|
/** Clamp a raw SI value to the cap's slice constraints (humidity/lux are bounded). */
|
|
95106
95557
|
function clampForCap(cap, value) {
|