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