@camstack/addon-provider-dreame 0.2.155 → 0.2.156
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 +1043 -522
- package/dist/addon.mjs +1043 -522
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -5479,7 +5479,7 @@ var ZodIssueCode$1 = {
|
|
|
5479
5479
|
var ZodFirstPartyTypeKind$1;
|
|
5480
5480
|
ZodFirstPartyTypeKind$1 || (ZodFirstPartyTypeKind$1 = {});
|
|
5481
5481
|
//#endregion
|
|
5482
|
-
//#region ../types/dist/sleep-
|
|
5482
|
+
//#region ../types/dist/sleep-CARd6wZF.mjs
|
|
5483
5483
|
/**
|
|
5484
5484
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5485
5485
|
* window to float samples (D455).
|
|
@@ -8033,6 +8033,99 @@ var RecordKindDeclarationSchema = object({
|
|
|
8033
8033
|
}).strict();
|
|
8034
8034
|
/** The kind names: lower-case, dash separated, short enough for an index name. */
|
|
8035
8035
|
var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
|
|
8036
|
+
var READING_QUANTITIES = [
|
|
8037
|
+
"temperature",
|
|
8038
|
+
"pressure",
|
|
8039
|
+
"speed",
|
|
8040
|
+
"precipitation",
|
|
8041
|
+
"precipitation-rate",
|
|
8042
|
+
"length",
|
|
8043
|
+
"area",
|
|
8044
|
+
"illuminance",
|
|
8045
|
+
"irradiance",
|
|
8046
|
+
"power",
|
|
8047
|
+
"energy",
|
|
8048
|
+
"voltage",
|
|
8049
|
+
"current",
|
|
8050
|
+
"apparent-power",
|
|
8051
|
+
"humidity",
|
|
8052
|
+
"percentage",
|
|
8053
|
+
"concentration-volume",
|
|
8054
|
+
"concentration-mass",
|
|
8055
|
+
"concentration-ppb",
|
|
8056
|
+
"volume",
|
|
8057
|
+
"duration",
|
|
8058
|
+
"duration-ms",
|
|
8059
|
+
"information",
|
|
8060
|
+
"sound-level",
|
|
8061
|
+
"signal-level",
|
|
8062
|
+
"colour-temperature",
|
|
8063
|
+
"angle",
|
|
8064
|
+
"bitrate",
|
|
8065
|
+
"rate",
|
|
8066
|
+
"mass",
|
|
8067
|
+
"index",
|
|
8068
|
+
"ratio",
|
|
8069
|
+
"count",
|
|
8070
|
+
"none",
|
|
8071
|
+
"device"
|
|
8072
|
+
];
|
|
8073
|
+
var ReadingQuantitySchema = _enum(READING_QUANTITIES);
|
|
8074
|
+
new Set(READING_QUANTITIES);
|
|
8075
|
+
/**
|
|
8076
|
+
* Stored unit symbol for a quantity. `none` means the number is dimensionless
|
|
8077
|
+
* (or an index / ratio / count). `device` means the cap has no single unit.
|
|
8078
|
+
*/
|
|
8079
|
+
var QUANTITY_BASE = {
|
|
8080
|
+
temperature: "°C",
|
|
8081
|
+
pressure: "hPa",
|
|
8082
|
+
speed: "m/s",
|
|
8083
|
+
precipitation: "mm",
|
|
8084
|
+
"precipitation-rate": "mm/h",
|
|
8085
|
+
length: "m",
|
|
8086
|
+
area: "m²",
|
|
8087
|
+
illuminance: "lx",
|
|
8088
|
+
irradiance: "W/m²",
|
|
8089
|
+
power: "W",
|
|
8090
|
+
energy: "kWh",
|
|
8091
|
+
voltage: "V",
|
|
8092
|
+
current: "A",
|
|
8093
|
+
"apparent-power": "VA",
|
|
8094
|
+
humidity: "%",
|
|
8095
|
+
percentage: "%",
|
|
8096
|
+
"concentration-volume": "ppm",
|
|
8097
|
+
"concentration-mass": "µg/m³",
|
|
8098
|
+
"concentration-ppb": "ppb",
|
|
8099
|
+
volume: "L",
|
|
8100
|
+
duration: "s",
|
|
8101
|
+
"duration-ms": "ms",
|
|
8102
|
+
information: "B",
|
|
8103
|
+
"sound-level": "dBFS",
|
|
8104
|
+
"signal-level": "dBm",
|
|
8105
|
+
"colour-temperature": "mired",
|
|
8106
|
+
angle: "°",
|
|
8107
|
+
bitrate: "kbps",
|
|
8108
|
+
rate: "fps",
|
|
8109
|
+
mass: "kg",
|
|
8110
|
+
index: "none",
|
|
8111
|
+
ratio: "none",
|
|
8112
|
+
count: "none",
|
|
8113
|
+
none: "none",
|
|
8114
|
+
device: "device"
|
|
8115
|
+
};
|
|
8116
|
+
/** The stored unit, or `undefined` when the quantity has no unit symbol. */
|
|
8117
|
+
function baseUnitOf(quantity) {
|
|
8118
|
+
const symbol = QUANTITY_BASE[quantity];
|
|
8119
|
+
if (symbol === "none" || symbol === "device") return void 0;
|
|
8120
|
+
return symbol;
|
|
8121
|
+
}
|
|
8122
|
+
function readingMeta(quantity, extras) {
|
|
8123
|
+
return {
|
|
8124
|
+
quantity,
|
|
8125
|
+
...extras?.precision !== void 0 ? { precision: extras.precision } : {},
|
|
8126
|
+
...extras?.label !== void 0 ? { label: extras.label } : {}
|
|
8127
|
+
};
|
|
8128
|
+
}
|
|
8036
8129
|
/** Shared Zod schemas used across streaming capabilities. */
|
|
8037
8130
|
var CamProfileSchema = _enum([
|
|
8038
8131
|
"high",
|
|
@@ -8060,8 +8153,8 @@ var CamStreamKindSchema = _enum([
|
|
|
8060
8153
|
"derived"
|
|
8061
8154
|
]);
|
|
8062
8155
|
var CamStreamResolutionSchema = object({
|
|
8063
|
-
width: number().int().positive(),
|
|
8064
|
-
height: number().int().positive()
|
|
8156
|
+
width: number().int().positive().meta(readingMeta("none")),
|
|
8157
|
+
height: number().int().positive().meta(readingMeta("none"))
|
|
8065
8158
|
});
|
|
8066
8159
|
var CameraStreamSchema = object({
|
|
8067
8160
|
/** Stable, provider-assigned id unique within the (deviceId) scope. */
|
|
@@ -8708,14 +8801,6 @@ Object.freeze({
|
|
|
8708
8801
|
{
|
|
8709
8802
|
"path": "aqi",
|
|
8710
8803
|
"valueType": "number"
|
|
8711
|
-
},
|
|
8712
|
-
{
|
|
8713
|
-
"path": "unit",
|
|
8714
|
-
"valueType": "string"
|
|
8715
|
-
},
|
|
8716
|
-
{
|
|
8717
|
-
"path": "precision",
|
|
8718
|
-
"valueType": "number"
|
|
8719
8804
|
}
|
|
8720
8805
|
],
|
|
8721
8806
|
"alarm-panel": [
|
|
@@ -8751,20 +8836,10 @@ Object.freeze({
|
|
|
8751
8836
|
"valueType": "boolean"
|
|
8752
8837
|
}
|
|
8753
8838
|
],
|
|
8754
|
-
"ambient-light-sensor": [
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
},
|
|
8759
|
-
{
|
|
8760
|
-
"path": "unit",
|
|
8761
|
-
"valueType": "string"
|
|
8762
|
-
},
|
|
8763
|
-
{
|
|
8764
|
-
"path": "precision",
|
|
8765
|
-
"valueType": "number"
|
|
8766
|
-
}
|
|
8767
|
-
],
|
|
8839
|
+
"ambient-light-sensor": [{
|
|
8840
|
+
"path": "lux",
|
|
8841
|
+
"valueType": "number"
|
|
8842
|
+
}],
|
|
8768
8843
|
"audio-metrics": [
|
|
8769
8844
|
{
|
|
8770
8845
|
"path": "ts",
|
|
@@ -8844,6 +8919,10 @@ Object.freeze({
|
|
|
8844
8919
|
"path": "lastUpdated",
|
|
8845
8920
|
"valueType": "number"
|
|
8846
8921
|
},
|
|
8922
|
+
{
|
|
8923
|
+
"path": "volts",
|
|
8924
|
+
"valueType": "number"
|
|
8925
|
+
},
|
|
8847
8926
|
{
|
|
8848
8927
|
"path": "lastContactAt",
|
|
8849
8928
|
"valueType": "number"
|
|
@@ -9086,10 +9165,6 @@ Object.freeze({
|
|
|
9086
9165
|
"datetime"
|
|
9087
9166
|
]
|
|
9088
9167
|
},
|
|
9089
|
-
{
|
|
9090
|
-
"path": "unit",
|
|
9091
|
-
"valueType": "string"
|
|
9092
|
-
},
|
|
9093
9168
|
{
|
|
9094
9169
|
"path": "min",
|
|
9095
9170
|
"valueType": "number"
|
|
@@ -9101,19 +9176,6 @@ Object.freeze({
|
|
|
9101
9176
|
{
|
|
9102
9177
|
"path": "step",
|
|
9103
9178
|
"valueType": "number"
|
|
9104
|
-
},
|
|
9105
|
-
{
|
|
9106
|
-
"path": "precision",
|
|
9107
|
-
"valueType": "number"
|
|
9108
|
-
},
|
|
9109
|
-
{
|
|
9110
|
-
"path": "format",
|
|
9111
|
-
"valueType": "enum",
|
|
9112
|
-
"enumValues": [
|
|
9113
|
-
"date",
|
|
9114
|
-
"time",
|
|
9115
|
-
"datetime"
|
|
9116
|
-
]
|
|
9117
9179
|
}
|
|
9118
9180
|
],
|
|
9119
9181
|
"cover": [
|
|
@@ -9354,13 +9416,8 @@ Object.freeze({
|
|
|
9354
9416
|
"path": "value",
|
|
9355
9417
|
"valueType": "string"
|
|
9356
9418
|
}, {
|
|
9357
|
-
"path": "
|
|
9358
|
-
"valueType": "
|
|
9359
|
-
"enumValues": [
|
|
9360
|
-
"date",
|
|
9361
|
-
"time",
|
|
9362
|
-
"datetime"
|
|
9363
|
-
]
|
|
9419
|
+
"path": "timestamp",
|
|
9420
|
+
"valueType": "boolean"
|
|
9364
9421
|
}],
|
|
9365
9422
|
"event-emitter": [
|
|
9366
9423
|
{
|
|
@@ -9467,20 +9524,10 @@ Object.freeze({
|
|
|
9467
9524
|
"valueType": "number"
|
|
9468
9525
|
}
|
|
9469
9526
|
],
|
|
9470
|
-
"humidity-sensor": [
|
|
9471
|
-
|
|
9472
|
-
|
|
9473
|
-
|
|
9474
|
-
},
|
|
9475
|
-
{
|
|
9476
|
-
"path": "unit",
|
|
9477
|
-
"valueType": "string"
|
|
9478
|
-
},
|
|
9479
|
-
{
|
|
9480
|
-
"path": "precision",
|
|
9481
|
-
"valueType": "number"
|
|
9482
|
-
}
|
|
9483
|
-
],
|
|
9527
|
+
"humidity-sensor": [{
|
|
9528
|
+
"path": "percent",
|
|
9529
|
+
"valueType": "number"
|
|
9530
|
+
}],
|
|
9484
9531
|
"image": [{
|
|
9485
9532
|
"path": "url",
|
|
9486
9533
|
"valueType": "string"
|
|
@@ -9819,20 +9866,10 @@ Object.freeze({
|
|
|
9819
9866
|
"valueType": "boolean"
|
|
9820
9867
|
}
|
|
9821
9868
|
],
|
|
9822
|
-
"numeric-sensor": [
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
},
|
|
9827
|
-
{
|
|
9828
|
-
"path": "unit",
|
|
9829
|
-
"valueType": "string"
|
|
9830
|
-
},
|
|
9831
|
-
{
|
|
9832
|
-
"path": "precision",
|
|
9833
|
-
"valueType": "number"
|
|
9834
|
-
}
|
|
9835
|
-
],
|
|
9869
|
+
"numeric-sensor": [{
|
|
9870
|
+
"path": "value",
|
|
9871
|
+
"valueType": "number"
|
|
9872
|
+
}],
|
|
9836
9873
|
"pet-feeder": [
|
|
9837
9874
|
{
|
|
9838
9875
|
"path": "foodLevel",
|
|
@@ -9924,14 +9961,6 @@ Object.freeze({
|
|
|
9924
9961
|
{
|
|
9925
9962
|
"path": "powerFactor",
|
|
9926
9963
|
"valueType": "number"
|
|
9927
|
-
},
|
|
9928
|
-
{
|
|
9929
|
-
"path": "unit",
|
|
9930
|
-
"valueType": "string"
|
|
9931
|
-
},
|
|
9932
|
-
{
|
|
9933
|
-
"path": "precision",
|
|
9934
|
-
"valueType": "number"
|
|
9935
9964
|
}
|
|
9936
9965
|
],
|
|
9937
9966
|
"presence": [
|
|
@@ -9960,20 +9989,10 @@ Object.freeze({
|
|
|
9960
9989
|
"valueType": "number"
|
|
9961
9990
|
}
|
|
9962
9991
|
],
|
|
9963
|
-
"pressure-sensor": [
|
|
9964
|
-
|
|
9965
|
-
|
|
9966
|
-
|
|
9967
|
-
},
|
|
9968
|
-
{
|
|
9969
|
-
"path": "unit",
|
|
9970
|
-
"valueType": "string"
|
|
9971
|
-
},
|
|
9972
|
-
{
|
|
9973
|
-
"path": "precision",
|
|
9974
|
-
"valueType": "number"
|
|
9975
|
-
}
|
|
9976
|
-
],
|
|
9992
|
+
"pressure-sensor": [{
|
|
9993
|
+
"path": "hpa",
|
|
9994
|
+
"valueType": "number"
|
|
9995
|
+
}],
|
|
9977
9996
|
"privacy-mask": [{
|
|
9978
9997
|
"path": "enabled",
|
|
9979
9998
|
"valueType": "boolean"
|
|
@@ -10284,20 +10303,10 @@ Object.freeze({
|
|
|
10284
10303
|
"path": "tampered",
|
|
10285
10304
|
"valueType": "boolean"
|
|
10286
10305
|
}],
|
|
10287
|
-
"temperature-sensor": [
|
|
10288
|
-
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
},
|
|
10292
|
-
{
|
|
10293
|
-
"path": "unit",
|
|
10294
|
-
"valueType": "string"
|
|
10295
|
-
},
|
|
10296
|
-
{
|
|
10297
|
-
"path": "precision",
|
|
10298
|
-
"valueType": "number"
|
|
10299
|
-
}
|
|
10300
|
-
],
|
|
10306
|
+
"temperature-sensor": [{
|
|
10307
|
+
"path": "celsius",
|
|
10308
|
+
"valueType": "number"
|
|
10309
|
+
}],
|
|
10301
10310
|
"update": [
|
|
10302
10311
|
{
|
|
10303
10312
|
"path": "currentVersion",
|
|
@@ -10408,10 +10417,6 @@ Object.freeze({
|
|
|
10408
10417
|
"path": "temperature",
|
|
10409
10418
|
"valueType": "number"
|
|
10410
10419
|
},
|
|
10411
|
-
{
|
|
10412
|
-
"path": "temperatureUnit",
|
|
10413
|
-
"valueType": "string"
|
|
10414
|
-
},
|
|
10415
10420
|
{
|
|
10416
10421
|
"path": "humidity",
|
|
10417
10422
|
"valueType": "number"
|
|
@@ -10420,18 +10425,10 @@ Object.freeze({
|
|
|
10420
10425
|
"path": "pressure",
|
|
10421
10426
|
"valueType": "number"
|
|
10422
10427
|
},
|
|
10423
|
-
{
|
|
10424
|
-
"path": "pressureUnit",
|
|
10425
|
-
"valueType": "string"
|
|
10426
|
-
},
|
|
10427
10428
|
{
|
|
10428
10429
|
"path": "windSpeed",
|
|
10429
10430
|
"valueType": "number"
|
|
10430
10431
|
},
|
|
10431
|
-
{
|
|
10432
|
-
"path": "windSpeedUnit",
|
|
10433
|
-
"valueType": "string"
|
|
10434
|
-
},
|
|
10435
10432
|
{
|
|
10436
10433
|
"path": "windBearing",
|
|
10437
10434
|
"valueType": "number"
|
|
@@ -17909,7 +17906,8 @@ var CompositionProblemSchema = object({
|
|
|
17909
17906
|
"item-field-reserved",
|
|
17910
17907
|
"target-missing",
|
|
17911
17908
|
"target-unreadable",
|
|
17912
|
-
"not-replaceable"
|
|
17909
|
+
"not-replaceable",
|
|
17910
|
+
"native-capability-replace"
|
|
17913
17911
|
]),
|
|
17914
17912
|
path: string(),
|
|
17915
17913
|
message: string()
|
|
@@ -17967,9 +17965,11 @@ var CompositionFieldPlanSchema = object({
|
|
|
17967
17965
|
reads: array(CompositionFieldReadSchema)
|
|
17968
17966
|
});
|
|
17969
17967
|
/**
|
|
17970
|
-
* What a feature does to its target (D663): `new` builds a capability on a
|
|
17971
|
-
* device; on an existing device it `add`s a capability the device lacks
|
|
17972
|
-
*
|
|
17968
|
+
* What a feature does to its target (D663, D944): `new` builds a capability on a
|
|
17969
|
+
* new device; on an existing device it `add`s a capability the device lacks.
|
|
17970
|
+
* Replacing a capability the device already serves is refused
|
|
17971
|
+
* (`native-capability-replace`), not planned. `replace` remains on the claim
|
|
17972
|
+
* wire for a claim the hub already holds.
|
|
17973
17973
|
*/
|
|
17974
17974
|
var CompositionFeatureModeSchema = _enum([
|
|
17975
17975
|
"new",
|
|
@@ -19855,12 +19855,12 @@ method(object({ deviceId: number().int().nonnegative() }), DeviceAdminLinkSchema
|
|
|
19855
19855
|
* providers detect renames by matching `uniqueId` and patch `id`
|
|
19856
19856
|
* in-place so dispatch keeps working.
|
|
19857
19857
|
*
|
|
19858
|
-
* **
|
|
19859
|
-
*
|
|
19860
|
-
*
|
|
19861
|
-
*
|
|
19862
|
-
* (`ha-domain-mapping`)
|
|
19863
|
-
* stored after that point.
|
|
19858
|
+
* **Display unit and precision are NOT stored here.** They are a read-time
|
|
19859
|
+
* preference (D944). A polymorphic reading stores its `quantity` here once,
|
|
19860
|
+
* at adopt, and a date/time value stores `dateTimeShape` the same way —
|
|
19861
|
+
* neither is repeated on each sample. The `device_class` tag is an
|
|
19862
|
+
* HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
|
|
19863
|
+
* to derive the CamStack `DeviceRole`; it is never stored after that point.
|
|
19864
19864
|
*
|
|
19865
19865
|
* The `raw` escape hatch carries provider-specific attributes that don't
|
|
19866
19866
|
* fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
|
|
@@ -19874,6 +19874,14 @@ var SourceInfoSchema = object({
|
|
|
19874
19874
|
/** Immutable upstream identifier when available (HA `unique_id`, … ).
|
|
19875
19875
|
* Used to detect rename when `id` changes. */
|
|
19876
19876
|
uniqueId: string().optional(),
|
|
19877
|
+
/** Quantity of a polymorphic reading, written once at adopt (D944). */
|
|
19878
|
+
quantity: ReadingQuantitySchema.optional(),
|
|
19879
|
+
/** Date / time / datetime shape of a string reading, written once at adopt. */
|
|
19880
|
+
dateTimeShape: _enum([
|
|
19881
|
+
"date",
|
|
19882
|
+
"time",
|
|
19883
|
+
"datetime"
|
|
19884
|
+
]).optional(),
|
|
19877
19885
|
/** Provider-specific extras that don't fit the structured slots.
|
|
19878
19886
|
* Whitelist what goes here per provider — do NOT dump entire HA
|
|
19879
19887
|
* attribute blobs (would explode the meta JSON column). */
|
|
@@ -19919,6 +19927,80 @@ function mergeSourceInfo(prev, patch) {
|
|
|
19919
19927
|
return next;
|
|
19920
19928
|
}
|
|
19921
19929
|
/**
|
|
19930
|
+
* Coerce common unit-of-measurement string variants to a canonical form
|
|
19931
|
+
* so the UI's display templates can rely on stable strings. Pure
|
|
19932
|
+
* passthrough for unknown units — kept conservative on purpose, the goal
|
|
19933
|
+
* is to normalise the long tail of HA `unit_of_measurement` values that
|
|
19934
|
+
* have multiple historical spellings (`'°C'` vs `'C'` vs `'celsius'`).
|
|
19935
|
+
*
|
|
19936
|
+
* Extend the map as we encounter real-world data. Returning the raw
|
|
19937
|
+
* input for unknown values is intentional: providers should never
|
|
19938
|
+
* lose information silently here.
|
|
19939
|
+
*/
|
|
19940
|
+
function normalizeUnit(raw) {
|
|
19941
|
+
if (raw == null) return void 0;
|
|
19942
|
+
const trimmed = raw.trim();
|
|
19943
|
+
if (trimmed === "") return void 0;
|
|
19944
|
+
const lower = trimmed.toLowerCase();
|
|
19945
|
+
if (lower === "°c" || lower === "c" || lower === "celsius") return "°C";
|
|
19946
|
+
if (lower === "°f" || lower === "f" || lower === "fahrenheit") return "°F";
|
|
19947
|
+
if (lower === "k" || lower === "kelvin") return "K";
|
|
19948
|
+
if (lower === "%" || lower === "percent" || lower === "percentage") return "%";
|
|
19949
|
+
if (lower === "lx" || lower === "lux") return "lx";
|
|
19950
|
+
if (lower === "hpa" || lower === "hectopascal") return "hPa";
|
|
19951
|
+
if (lower === "kpa") return "kPa";
|
|
19952
|
+
if (lower === "pa") return "Pa";
|
|
19953
|
+
if (lower === "mbar" || lower === "millibar") return "mbar";
|
|
19954
|
+
if (lower === "w" || lower === "watt") return "W";
|
|
19955
|
+
if (lower === "kw" || lower === "kilowatt") return "kW";
|
|
19956
|
+
if (lower === "wh" || lower === "watt-hour") return "Wh";
|
|
19957
|
+
if (lower === "kwh" || lower === "kilowatt-hour") return "kWh";
|
|
19958
|
+
if (lower === "v" || lower === "volt") return "V";
|
|
19959
|
+
if (lower === "a" || lower === "amp" || lower === "ampere") return "A";
|
|
19960
|
+
if (lower === "ma" || lower === "milliampere") return "mA";
|
|
19961
|
+
if (lower === "va") return "VA";
|
|
19962
|
+
if (lower === "ppm") return "ppm";
|
|
19963
|
+
if (lower === "ppb") return "ppb";
|
|
19964
|
+
if (lower === "µg/m³" || lower === "ug/m3" || lower === "µg/m3") return "µg/m³";
|
|
19965
|
+
if (lower === "km/h" || lower === "kph") return "km/h";
|
|
19966
|
+
if (lower === "mph") return "mph";
|
|
19967
|
+
if (lower === "m/s") return "m/s";
|
|
19968
|
+
if (lower === "m") return "m";
|
|
19969
|
+
if (lower === "m²" || lower === "m2") return "m²";
|
|
19970
|
+
if (lower === "mm") return "mm";
|
|
19971
|
+
if (lower === "cm") return "cm";
|
|
19972
|
+
if (lower === "km") return "km";
|
|
19973
|
+
if (lower === "in" || lower === "inch" || lower === "inches") return "in";
|
|
19974
|
+
if (lower === "ft" || lower === "foot" || lower === "feet") return "ft";
|
|
19975
|
+
if (lower === "kg") return "kg";
|
|
19976
|
+
if (lower === "g") return "g";
|
|
19977
|
+
if (lower === "l" || lower === "liter" || lower === "litre" || lower === "liters" || lower === "litres") return "L";
|
|
19978
|
+
if (lower === "ml") return "mL";
|
|
19979
|
+
if (lower === "gal" || lower === "gallon" || lower === "gallons") return "gal";
|
|
19980
|
+
if (lower === "s" || lower === "sec" || lower === "second" || lower === "seconds") return "s";
|
|
19981
|
+
if (lower === "ms") return "ms";
|
|
19982
|
+
if (lower === "min" || lower === "minute" || lower === "minutes") return "min";
|
|
19983
|
+
if (lower === "h" || lower === "hr" || lower === "hour" || lower === "hours") return "h";
|
|
19984
|
+
if (lower === "d" || lower === "day" || lower === "days") return "d";
|
|
19985
|
+
if (lower === "b" || lower === "byte" || lower === "bytes") return "B";
|
|
19986
|
+
if (lower === "kib") return "KiB";
|
|
19987
|
+
if (lower === "mib") return "MiB";
|
|
19988
|
+
if (lower === "gib") return "GiB";
|
|
19989
|
+
if (lower === "tib") return "TiB";
|
|
19990
|
+
if (lower === "kb") return "kB";
|
|
19991
|
+
if (lower === "mb") return "MB";
|
|
19992
|
+
if (lower === "gb") return "GB";
|
|
19993
|
+
if (lower === "dbm") return "dBm";
|
|
19994
|
+
if (lower === "dbfs") return "dBFS";
|
|
19995
|
+
if (lower === "inhg") return "inHg";
|
|
19996
|
+
if (lower === "mm/h") return "mm/h";
|
|
19997
|
+
if (lower === "w/m²" || lower === "w/m2") return "W/m²";
|
|
19998
|
+
if (lower === "°" || lower === "deg" || lower === "degree" || lower === "degrees") return "°";
|
|
19999
|
+
if (lower === "kbps") return "kbps";
|
|
20000
|
+
if (lower === "fps") return "fps";
|
|
20001
|
+
return trimmed;
|
|
20002
|
+
}
|
|
20003
|
+
/**
|
|
19922
20004
|
* Key under which `SourceInfo` lives inside `DeviceMeta.metadata`. The
|
|
19923
20005
|
* blob is a free-form `Record<string, unknown>` shared with hardware-
|
|
19924
20006
|
* identity fields (`manufacturer`, `model`, `firmware`, …); SourceInfo
|
|
@@ -40995,7 +41077,8 @@ method(object({
|
|
|
40995
41077
|
"enum"
|
|
40996
41078
|
]),
|
|
40997
41079
|
enumValues: array(string()).optional(),
|
|
40998
|
-
item: boolean().optional()
|
|
41080
|
+
item: boolean().optional(),
|
|
41081
|
+
quantity: ReadingQuantitySchema.optional()
|
|
40999
41082
|
})).readonly(),
|
|
41000
41083
|
itemArray: object({
|
|
41001
41084
|
path: string(),
|
|
@@ -44851,8 +44934,8 @@ var MaskPolygonShapeSchema = object({
|
|
|
44851
44934
|
/** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
|
|
44852
44935
|
var MaskGridShapeSchema = object({
|
|
44853
44936
|
kind: literal("grid"),
|
|
44854
|
-
gridWidth: number(),
|
|
44855
|
-
gridHeight: number(),
|
|
44937
|
+
gridWidth: number().meta(readingMeta("none")),
|
|
44938
|
+
gridHeight: number().meta(readingMeta("none")),
|
|
44856
44939
|
cells: array(boolean())
|
|
44857
44940
|
});
|
|
44858
44941
|
discriminatedUnion("kind", [
|
|
@@ -56332,27 +56415,17 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
56332
56415
|
*/
|
|
56333
56416
|
var AirQualitySensorStatusSchema = object({
|
|
56334
56417
|
/** Carbon dioxide concentration in ppm. */
|
|
56335
|
-
co2Ppm: number().min(0).optional(),
|
|
56418
|
+
co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
56336
56419
|
/** Total volatile organic compounds in ppb. */
|
|
56337
|
-
vocPpb: number().min(0).optional(),
|
|
56420
|
+
vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
56338
56421
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
56339
|
-
pm25: number().min(0).optional(),
|
|
56422
|
+
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56340
56423
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
56341
|
-
pm10: number().min(0).optional(),
|
|
56424
|
+
pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56342
56425
|
/** Composite AQI value (typically 0..500). */
|
|
56343
|
-
aqi: number().optional(),
|
|
56426
|
+
aqi: number().optional().meta(readingMeta("index")),
|
|
56344
56427
|
/** Ms epoch when the slice was last updated. */
|
|
56345
|
-
lastFetchedAt: number()
|
|
56346
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
56347
|
-
* `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
|
|
56348
|
-
* upstream `sensor.*` entity surfaces ONE device_class, so one unit
|
|
56349
|
-
* per slice is unambiguous. */
|
|
56350
|
-
unit: string().optional(),
|
|
56351
|
-
/** Suggested decimal places for numeric display.
|
|
56352
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
56353
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
56354
|
-
* auto-formatting when absent. */
|
|
56355
|
-
precision: number().int().min(0).max(10).optional()
|
|
56428
|
+
lastFetchedAt: number()
|
|
56356
56429
|
});
|
|
56357
56430
|
var airQualitySensorCapability = {
|
|
56358
56431
|
name: "air-quality-sensor",
|
|
@@ -56389,12 +56462,12 @@ var airQualitySensorCapability = {
|
|
|
56389
56462
|
},
|
|
56390
56463
|
pm25: {
|
|
56391
56464
|
kind: "gauge",
|
|
56392
|
-
unit: "
|
|
56465
|
+
unit: "µg/m³",
|
|
56393
56466
|
deadband: 1
|
|
56394
56467
|
},
|
|
56395
56468
|
pm10: {
|
|
56396
56469
|
kind: "gauge",
|
|
56397
|
-
unit: "
|
|
56470
|
+
unit: "µg/m³",
|
|
56398
56471
|
deadband: 1
|
|
56399
56472
|
},
|
|
56400
56473
|
aqi: {
|
|
@@ -56412,22 +56485,13 @@ var airQualitySensorCapability = {
|
|
|
56412
56485
|
* entries with `device_class: illuminance`.
|
|
56413
56486
|
*/
|
|
56414
56487
|
var AmbientLightSensorStatusSchema = object({
|
|
56415
|
-
/** Current illuminance in lux (lx). */
|
|
56416
|
-
lux: number().min(0).meta({
|
|
56417
|
-
|
|
56488
|
+
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
56489
|
+
lux: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
56490
|
+
precision: 0,
|
|
56418
56491
|
label: "Illuminance"
|
|
56419
|
-
}),
|
|
56492
|
+
})),
|
|
56420
56493
|
/** Ms epoch when the slice was last updated. */
|
|
56421
|
-
lastFetchedAt: number()
|
|
56422
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
56423
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
56424
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
56425
|
-
unit: string().optional(),
|
|
56426
|
-
/** Suggested decimal places for numeric display.
|
|
56427
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
56428
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
56429
|
-
* auto-formatting when absent. */
|
|
56430
|
-
precision: number().int().min(0).max(10).optional()
|
|
56494
|
+
lastFetchedAt: number()
|
|
56431
56495
|
});
|
|
56432
56496
|
var ambientLightSensorCapability = {
|
|
56433
56497
|
name: "ambient-light-sensor",
|
|
@@ -56459,12 +56523,7 @@ var ambientLightSensorCapability = {
|
|
|
56459
56523
|
} } },
|
|
56460
56524
|
/** Clock fields: written, but excluded from the compare that decides
|
|
56461
56525
|
* whether persisting is worth a SQLite commit. */
|
|
56462
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
56463
|
-
customize: {
|
|
56464
|
-
version: 1,
|
|
56465
|
-
advanced: "derive",
|
|
56466
|
-
targets: []
|
|
56467
|
-
}
|
|
56526
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
56468
56527
|
};
|
|
56469
56528
|
/**
|
|
56470
56529
|
* Per-class audio metrics aggregated over a sliding window.
|
|
@@ -56691,10 +56750,7 @@ var BatteryStatusSchema = object({
|
|
|
56691
56750
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
56692
56751
|
* declines to band a non-finite reading, which is the correct shape.
|
|
56693
56752
|
*/
|
|
56694
|
-
percentage: number().min(0).max(100).nullable().meta({
|
|
56695
|
-
unit: "%",
|
|
56696
|
-
label: "Charge"
|
|
56697
|
-
}),
|
|
56753
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
56698
56754
|
/**
|
|
56699
56755
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
56700
56756
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -56721,7 +56777,14 @@ var BatteryStatusSchema = object({
|
|
|
56721
56777
|
*/
|
|
56722
56778
|
sleeping: boolean().meta({ label: "Sleeping" }),
|
|
56723
56779
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
56724
|
-
lastUpdated: number(),
|
|
56780
|
+
lastUpdated: number().meta(readingMeta("none")),
|
|
56781
|
+
/**
|
|
56782
|
+
* Battery voltage in volts, for a cell the firmware reports as voltage
|
|
56783
|
+
* rather than a percentage (Ecowitt capacitor packs). Absent on cameras
|
|
56784
|
+
* that only report charge. A second reading on this cap, not a second
|
|
56785
|
+
* `numeric-sensor` on the same device.
|
|
56786
|
+
*/
|
|
56787
|
+
volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
56725
56788
|
/**
|
|
56726
56789
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
56727
56790
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -56773,7 +56836,6 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
56773
56836
|
/** Generic Customize target for the percentage reading (D936). */
|
|
56774
56837
|
var batteryCustomize = {
|
|
56775
56838
|
version: 1,
|
|
56776
|
-
advanced: "derive",
|
|
56777
56839
|
targets: [{
|
|
56778
56840
|
id: "percentage",
|
|
56779
56841
|
cardinality: "scalar",
|
|
@@ -56782,7 +56844,7 @@ var batteryCustomize = {
|
|
|
56782
56844
|
stateField: "percentage"
|
|
56783
56845
|
},
|
|
56784
56846
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
56785
|
-
apply: "add
|
|
56847
|
+
apply: "add",
|
|
56786
56848
|
path: "percentage",
|
|
56787
56849
|
sources: [{
|
|
56788
56850
|
kind: "field",
|
|
@@ -56903,10 +56965,16 @@ onStatusChanged: { data: object({
|
|
|
56903
56965
|
/**
|
|
56904
56966
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
56905
56967
|
*/
|
|
56906
|
-
history: { fields: {
|
|
56907
|
-
|
|
56908
|
-
|
|
56909
|
-
|
|
56968
|
+
history: { fields: {
|
|
56969
|
+
percentage: {
|
|
56970
|
+
kind: "gauge",
|
|
56971
|
+
unit: "%"
|
|
56972
|
+
},
|
|
56973
|
+
volts: {
|
|
56974
|
+
kind: "gauge",
|
|
56975
|
+
unit: "V"
|
|
56976
|
+
}
|
|
56977
|
+
} },
|
|
56910
56978
|
customize: batteryCustomize,
|
|
56911
56979
|
/** Clock fields: written, but excluded from the compare that decides
|
|
56912
56980
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -56953,12 +57021,7 @@ var binaryCapability = {
|
|
|
56953
57021
|
history: { fields: { on: { kind: "boolean" } } },
|
|
56954
57022
|
/** Clock fields: written, but excluded from the compare that decides
|
|
56955
57023
|
* whether persisting is worth a SQLite commit. */
|
|
56956
|
-
volatileStateFields: ["lastChangedAt"]
|
|
56957
|
-
customize: {
|
|
56958
|
-
version: 1,
|
|
56959
|
-
advanced: "derive",
|
|
56960
|
-
targets: []
|
|
56961
|
-
}
|
|
57024
|
+
volatileStateFields: ["lastChangedAt"]
|
|
56962
57025
|
};
|
|
56963
57026
|
/**
|
|
56964
57027
|
* Dimmable-light brightness control. Co-exists with `switch` on the
|
|
@@ -56974,7 +57037,7 @@ var binaryCapability = {
|
|
|
56974
57037
|
*/
|
|
56975
57038
|
var BrightnessStatusSchema = object({
|
|
56976
57039
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
56977
|
-
percentage: number().min(0).max(100),
|
|
57040
|
+
percentage: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
56978
57041
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
56979
57042
|
lastChangedAt: number()
|
|
56980
57043
|
});
|
|
@@ -57445,21 +57508,21 @@ var ClimateControlStatusSchema = object({
|
|
|
57445
57508
|
/** Single setpoint in Celsius. Used by single-target modes
|
|
57446
57509
|
* (heat / cool / dry). Null when the device is in a range mode
|
|
57447
57510
|
* (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
|
|
57448
|
-
target: number().nullable(),
|
|
57511
|
+
target: number().nullable().meta(readingMeta("temperature")),
|
|
57449
57512
|
/** Upper bound of the dual setpoint in Celsius. Populated only
|
|
57450
57513
|
* in `heat_cool` mode. */
|
|
57451
|
-
targetHigh: number().nullable(),
|
|
57514
|
+
targetHigh: number().nullable().meta(readingMeta("temperature")),
|
|
57452
57515
|
/** Lower bound of the dual setpoint in Celsius. Populated only
|
|
57453
57516
|
* in `heat_cool` mode. */
|
|
57454
|
-
targetLow: number().nullable(),
|
|
57517
|
+
targetLow: number().nullable().meta(readingMeta("temperature")),
|
|
57455
57518
|
/** Target relative humidity (0..100). Null when the device has no
|
|
57456
57519
|
* humidity control. */
|
|
57457
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
57520
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
57458
57521
|
/** Current measured temperature in Celsius. Read-only — pushed by
|
|
57459
57522
|
* the device's internal sensor. */
|
|
57460
|
-
currentTemp: number().nullable(),
|
|
57523
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
57461
57524
|
/** Current measured relative humidity (0..100). Read-only. */
|
|
57462
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
57525
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
57463
57526
|
/** Vertical louver swing on/off. Null when the device has no vertical
|
|
57464
57527
|
* swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
|
|
57465
57528
|
swingVertical: boolean().nullable(),
|
|
@@ -57467,11 +57530,11 @@ var ClimateControlStatusSchema = object({
|
|
|
57467
57530
|
* horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
|
|
57468
57531
|
swingHorizontal: boolean().nullable(),
|
|
57469
57532
|
/** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
57470
|
-
minTemp: number().nullable().optional(),
|
|
57533
|
+
minTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
57471
57534
|
/** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
57472
|
-
maxTemp: number().nullable().optional(),
|
|
57535
|
+
maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
57473
57536
|
/** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
|
|
57474
|
-
tempStep: number().positive().nullable().optional(),
|
|
57537
|
+
tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
|
|
57475
57538
|
/** Ms epoch when the slice was last updated (push or command). */
|
|
57476
57539
|
lastFetchedAt: number()
|
|
57477
57540
|
});
|
|
@@ -57613,17 +57676,17 @@ var climateControlCapability = {
|
|
|
57613
57676
|
* modes to expose in the picker without a slice fetch.
|
|
57614
57677
|
*/
|
|
57615
57678
|
var RgbTripletSchema = object({
|
|
57616
|
-
r: number().int().min(0).max(255),
|
|
57617
|
-
g: number().int().min(0).max(255),
|
|
57618
|
-
b: number().int().min(0).max(255)
|
|
57679
|
+
r: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
57680
|
+
g: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
57681
|
+
b: number().int().min(0).max(255).meta(readingMeta("none"))
|
|
57619
57682
|
});
|
|
57620
57683
|
var HsvTripletSchema = object({
|
|
57621
57684
|
/** Hue in degrees, 0..360. */
|
|
57622
|
-
h: number().min(0).max(360),
|
|
57685
|
+
h: number().min(0).max(360).meta(readingMeta("none")),
|
|
57623
57686
|
/** Saturation as 0..100 inclusive. */
|
|
57624
|
-
s: number().min(0).max(100),
|
|
57687
|
+
s: number().min(0).max(100).meta(readingMeta("none")),
|
|
57625
57688
|
/** Value/brightness as 0..100 inclusive. */
|
|
57626
|
-
v: number().min(0).max(100)
|
|
57689
|
+
v: number().min(0).max(100).meta(readingMeta("none"))
|
|
57627
57690
|
});
|
|
57628
57691
|
var ColorInputSchema = discriminatedUnion("mode", [
|
|
57629
57692
|
object({
|
|
@@ -57652,7 +57715,7 @@ var ColorStatusSchema = object({
|
|
|
57652
57715
|
/** Populated when `mode === 'hsv'`. */
|
|
57653
57716
|
hsv: HsvTripletSchema.optional(),
|
|
57654
57717
|
/** Populated when `mode === 'mired'`. */
|
|
57655
|
-
mireds: number().int().optional(),
|
|
57718
|
+
mireds: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
57656
57719
|
/** Ms epoch of the last operator-driven change. */
|
|
57657
57720
|
lastChangedAt: number()
|
|
57658
57721
|
});
|
|
@@ -57859,18 +57922,12 @@ _enum([
|
|
|
57859
57922
|
function consumableKindOf(item) {
|
|
57860
57923
|
return item.kind ?? "wear";
|
|
57861
57924
|
}
|
|
57862
|
-
/**
|
|
57863
|
-
|
|
57864
|
-
|
|
57865
|
-
"days",
|
|
57866
|
-
"hours",
|
|
57867
|
-
"cycles"
|
|
57868
|
-
]);
|
|
57869
|
-
/** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
|
|
57870
|
-
* days). `value` null = unknown (D393). */
|
|
57925
|
+
/** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
|
|
57926
|
+
var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
|
|
57927
|
+
/** Remaining life. `value` null = unknown (D393). Duration is seconds. */
|
|
57871
57928
|
var ConsumableRemainingSchema = object({
|
|
57872
|
-
value: number().min(0).nullable(),
|
|
57873
|
-
|
|
57929
|
+
value: number().min(0).nullable().meta(readingMeta("device")),
|
|
57930
|
+
kind: ConsumableRemainingKindSchema
|
|
57874
57931
|
});
|
|
57875
57932
|
/** A single consumable item. Either a continuous `level` (remaining
|
|
57876
57933
|
* life %, or fill % for a `supply` / `waste` item) or a discrete `status`
|
|
@@ -57883,7 +57940,7 @@ var ConsumableItemSchema = object({
|
|
|
57883
57940
|
/** Display name. */
|
|
57884
57941
|
label: string().min(1),
|
|
57885
57942
|
/** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
|
|
57886
|
-
level: number().min(0).max(100).nullable(),
|
|
57943
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
57887
57944
|
/** The provider's discrete verdict when known (binary mode). */
|
|
57888
57945
|
status: ConsumableStatusSchema.nullable(),
|
|
57889
57946
|
/** What the item is — decides whether low or full is bad. Absent = `wear`. */
|
|
@@ -57965,8 +58022,8 @@ var consumablesCustomize = {
|
|
|
57965
58022
|
transforms: [{ kind: "identity" }]
|
|
57966
58023
|
}],
|
|
57967
58024
|
literals: [{
|
|
57968
|
-
path: "remaining.
|
|
57969
|
-
values: [...
|
|
58025
|
+
path: "remaining.kind",
|
|
58026
|
+
values: [...ConsumableRemainingKindSchema.options]
|
|
57970
58027
|
}]
|
|
57971
58028
|
},
|
|
57972
58029
|
{
|
|
@@ -58095,7 +58152,6 @@ reset: method(object({
|
|
|
58095
58152
|
},
|
|
58096
58153
|
"remaining.value": {
|
|
58097
58154
|
kind: "gauge",
|
|
58098
|
-
unitFrom: "remaining.unit",
|
|
58099
58155
|
deadband: 1,
|
|
58100
58156
|
displayName: "Remaining"
|
|
58101
58157
|
}
|
|
@@ -58165,10 +58221,9 @@ var contactCapability = {
|
|
|
58165
58221
|
* (empty array for the other kinds). It lives in the slice because
|
|
58166
58222
|
* HA `select.*` entities may update their options at runtime.
|
|
58167
58223
|
*
|
|
58168
|
-
*
|
|
58169
|
-
*
|
|
58170
|
-
*
|
|
58171
|
-
* push — consistent with `options` and the sensor-unit approach.
|
|
58224
|
+
* Numeric `min` / `max` / `step` live in the slice and share the device
|
|
58225
|
+
* quantity (`SourceInfo.quantity`, D944). Display precision is a read-time
|
|
58226
|
+
* preference. A datetime control's shape is `SourceInfo.dateTimeShape`.
|
|
58172
58227
|
*/
|
|
58173
58228
|
var ControlStatusSchema = object({
|
|
58174
58229
|
kind: _enum([
|
|
@@ -58180,39 +58235,17 @@ var ControlStatusSchema = object({
|
|
|
58180
58235
|
/** Current value. Numeric kind → number; select/text/datetime → string.
|
|
58181
58236
|
* Datetime values are ISO 8601 strings (full timestamp, date-only,
|
|
58182
58237
|
* or time-only — HA accepts all three forms). */
|
|
58183
|
-
value: union([number(), string()]),
|
|
58238
|
+
value: union([number().meta(readingMeta("device")), string()]),
|
|
58184
58239
|
/** Acceptable values when `kind === 'select'`. Empty for other kinds. */
|
|
58185
58240
|
options: array(string()),
|
|
58186
58241
|
/** Ms epoch when the slice was last updated. */
|
|
58187
58242
|
lastChangedAt: number(),
|
|
58188
|
-
/**
|
|
58189
|
-
|
|
58190
|
-
|
|
58191
|
-
|
|
58192
|
-
/**
|
|
58193
|
-
|
|
58194
|
-
min: number().optional(),
|
|
58195
|
-
/** Maximum allowed value. Populated live from HA `attributes.max`.
|
|
58196
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58197
|
-
max: number().optional(),
|
|
58198
|
-
/** Step increment. Populated live from HA `attributes.step`.
|
|
58199
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58200
|
-
step: number().optional(),
|
|
58201
|
-
/** Suggested decimal places for numeric display. Populated live from HA
|
|
58202
|
-
* `attributes.suggested_display_precision`, or derived from `step` when the
|
|
58203
|
-
* HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
|
|
58204
|
-
* other kinds. Falls back to auto-formatting when absent. */
|
|
58205
|
-
precision: number().int().min(0).max(10).optional(),
|
|
58206
|
-
/** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
|
|
58207
|
-
* 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
|
|
58208
|
-
* (UI defaults to 'datetime'). Derived live from the device's HA domain
|
|
58209
|
-
* (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
|
|
58210
|
-
* `has_date`/`has_time` attributes on each push. */
|
|
58211
|
-
format: _enum([
|
|
58212
|
-
"date",
|
|
58213
|
-
"time",
|
|
58214
|
-
"datetime"
|
|
58215
|
-
]).optional()
|
|
58243
|
+
/** Minimum allowed value, in the device quantity's base unit. */
|
|
58244
|
+
min: number().optional().meta(readingMeta("device")),
|
|
58245
|
+
/** Maximum allowed value, in the device quantity's base unit. */
|
|
58246
|
+
max: number().optional().meta(readingMeta("device")),
|
|
58247
|
+
/** Step increment, in the device quantity's base unit. */
|
|
58248
|
+
step: number().optional().meta(readingMeta("device"))
|
|
58216
58249
|
});
|
|
58217
58250
|
var ControlSetValueInputSchema = discriminatedUnion("kind", [
|
|
58218
58251
|
object({
|
|
@@ -58278,9 +58311,9 @@ var CoverStatusSchema = object({
|
|
|
58278
58311
|
]),
|
|
58279
58312
|
/** 0 = fully closed, 100 = fully open. Null when the device has
|
|
58280
58313
|
* no intermediate position surface. */
|
|
58281
|
-
position: number().min(0).max(100).nullable(),
|
|
58314
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58282
58315
|
/** 0 = closed slats, 100 = open slats. Null when no tilt support. */
|
|
58283
|
-
tiltPosition: number().min(0).max(100).nullable(),
|
|
58316
|
+
tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58284
58317
|
/** Ms epoch when the slice was last updated. */
|
|
58285
58318
|
lastChangedAt: number()
|
|
58286
58319
|
});
|
|
@@ -58383,9 +58416,9 @@ var NormalizedRangeSchema$1 = object({
|
|
|
58383
58416
|
var DayNightStatusSchema = object({
|
|
58384
58417
|
mode: DayNightModeSchema,
|
|
58385
58418
|
/** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
|
|
58386
|
-
sensitivity: number().optional(),
|
|
58419
|
+
sensitivity: number().optional().meta(readingMeta("none")),
|
|
58387
58420
|
/** Delay before the IR-cut filter flips, in seconds. */
|
|
58388
|
-
switchDelaySec: number().optional(),
|
|
58421
|
+
switchDelaySec: number().optional().meta(readingMeta("duration")),
|
|
58389
58422
|
lastFetchedAt: number()
|
|
58390
58423
|
});
|
|
58391
58424
|
/**
|
|
@@ -58603,11 +58636,11 @@ var DishwasherStatusSchema = object({
|
|
|
58603
58636
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
58604
58637
|
availablePrograms: array(string()),
|
|
58605
58638
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
58606
|
-
remainingSeconds: number().int().nonnegative().nullable(),
|
|
58639
|
+
remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
58607
58640
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
58608
|
-
delayedStartSeconds: number().int().nonnegative().nullable(),
|
|
58641
|
+
delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
58609
58642
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
58610
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
58643
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58611
58644
|
/** True when the door is reported open; null when the door state is not known. */
|
|
58612
58645
|
doorOpen: boolean().nullable(),
|
|
58613
58646
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -58631,7 +58664,7 @@ var DishwasherStatusSchema = object({
|
|
|
58631
58664
|
/** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
|
|
58632
58665
|
machineCareDue: boolean().nullable(),
|
|
58633
58666
|
/** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
|
|
58634
|
-
machineCareRemainingRuns: number().int().nonnegative().nullable(),
|
|
58667
|
+
machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
|
|
58635
58668
|
/** Option states: null when the appliance does not expose the option or has not said. */
|
|
58636
58669
|
halfLoad: boolean().nullable(),
|
|
58637
58670
|
hygienePlus: boolean().nullable(),
|
|
@@ -58646,7 +58679,7 @@ var DishwasherStatusSchema = object({
|
|
|
58646
58679
|
/** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
|
|
58647
58680
|
activeEvents: array(string()),
|
|
58648
58681
|
/** Numeric error code when the appliance reports one; null otherwise. */
|
|
58649
|
-
errorCode: number().nullable(),
|
|
58682
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
58650
58683
|
/** Name of the raised alert-level event while the appliance is in error; null otherwise. */
|
|
58651
58684
|
errorLabel: string().nullable(),
|
|
58652
58685
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
@@ -58877,7 +58910,7 @@ var DoorbellStatusSchema = object({
|
|
|
58877
58910
|
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
58878
58911
|
lastPressedAt: number().nullable(),
|
|
58879
58912
|
/** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
|
|
58880
|
-
pressCountSinceStart: number()
|
|
58913
|
+
pressCountSinceStart: number().meta(readingMeta("count"))
|
|
58881
58914
|
});
|
|
58882
58915
|
var DoorbellPressEventSchema = object({
|
|
58883
58916
|
deviceId: number(),
|
|
@@ -58942,17 +58975,7 @@ onPressed: {
|
|
|
58942
58975
|
history: "none",
|
|
58943
58976
|
customize: doorbellCustomize
|
|
58944
58977
|
};
|
|
58945
|
-
|
|
58946
|
-
* Enum-state sensor — a string value picked from a finite option set.
|
|
58947
|
-
* Drives HA `sensor` entries with `state_class: enum` (HVAC action
|
|
58948
|
-
* states, weather conditions, contact-source channels, mode strings).
|
|
58949
|
-
* The option set is stable across observations; it is persisted in the
|
|
58950
|
-
* device config blob (via the control slice at adoption) and is NOT
|
|
58951
|
-
* stored in `sourceInfo`.
|
|
58952
|
-
*/
|
|
58953
|
-
/** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
|
|
58954
|
-
* enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
|
|
58955
|
-
var EnumSensorDateTimeFormatSchema = _enum([
|
|
58978
|
+
_enum([
|
|
58956
58979
|
"date",
|
|
58957
58980
|
"time",
|
|
58958
58981
|
"datetime"
|
|
@@ -58960,12 +58983,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
|
|
|
58960
58983
|
var EnumSensorStatusSchema = object({
|
|
58961
58984
|
value: string(),
|
|
58962
58985
|
/**
|
|
58963
|
-
* Set
|
|
58964
|
-
* as
|
|
58965
|
-
*
|
|
58966
|
-
* a time-only sensor → `'time'`.
|
|
58986
|
+
* Set when `value` is a timestamp rather than an enum state, so history
|
|
58987
|
+
* does not learn ISO strings as states. The display shape (date / time /
|
|
58988
|
+
* datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
|
|
58967
58989
|
*/
|
|
58968
|
-
|
|
58990
|
+
timestamp: boolean().optional(),
|
|
58969
58991
|
/** Ms epoch when the slice was last updated. */
|
|
58970
58992
|
lastFetchedAt: number()
|
|
58971
58993
|
});
|
|
@@ -58992,11 +59014,11 @@ var enumSensorCapability = {
|
|
|
58992
59014
|
* History (D850): the value as an `enum` series (an appliance's program or
|
|
58993
59015
|
* operation state, an HVAC action). The option set is per device, so the
|
|
58994
59016
|
* value list is LEARNED as values are first seen (bounded). A sensor with a
|
|
58995
|
-
* `
|
|
59017
|
+
* `timestamp` carries a clock, not a state, and is not recorded.
|
|
58996
59018
|
*/
|
|
58997
59019
|
history: { fields: { value: {
|
|
58998
59020
|
kind: "enum",
|
|
58999
|
-
skipWhenSet: "
|
|
59021
|
+
skipWhenSet: "timestamp"
|
|
59000
59022
|
} } },
|
|
59001
59023
|
/** Clock fields: written, but excluded from the compare that decides
|
|
59002
59024
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -59016,13 +59038,13 @@ var EventFireSchema = object({
|
|
|
59016
59038
|
deviceId: number(),
|
|
59017
59039
|
eventType: string(),
|
|
59018
59040
|
data: record(string(), unknown()).nullable(),
|
|
59019
|
-
timestamp: number(),
|
|
59020
|
-
seq: number()
|
|
59041
|
+
timestamp: number().meta(readingMeta("none")),
|
|
59042
|
+
seq: number().meta(readingMeta("count"))
|
|
59021
59043
|
});
|
|
59022
59044
|
var EventEmitterStatusSchema = object({
|
|
59023
59045
|
eventTypes: array(string()),
|
|
59024
59046
|
lastEvent: EventFireSchema.nullable(),
|
|
59025
|
-
eventCountSinceStart: number()
|
|
59047
|
+
eventCountSinceStart: number().meta(readingMeta("count"))
|
|
59026
59048
|
});
|
|
59027
59049
|
var eventEmitterCapability = {
|
|
59028
59050
|
name: "event-emitter",
|
|
@@ -59435,10 +59457,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
59435
59457
|
var FanControlStatusSchema = object({
|
|
59436
59458
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
59437
59459
|
* speed surface (single-speed fan). */
|
|
59438
|
-
percentage: number().min(0).max(100).nullable(),
|
|
59460
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59439
59461
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
59440
59462
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
59441
|
-
percentageStep: number().positive().optional(),
|
|
59463
|
+
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
59442
59464
|
/** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
|
|
59443
59465
|
* — empty string when no preset is active or supported. */
|
|
59444
59466
|
preset: string(),
|
|
@@ -59559,7 +59581,7 @@ var FeatureProbeStatusSchema = object({
|
|
|
59559
59581
|
/** Camera/firmware model string. `null` when the firmware doesn't expose it. */
|
|
59560
59582
|
model: string().nullable(),
|
|
59561
59583
|
/** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
|
|
59562
|
-
channelCount: number().nullable(),
|
|
59584
|
+
channelCount: number().nullable().meta(readingMeta("count")),
|
|
59563
59585
|
/**
|
|
59564
59586
|
* Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
|
|
59565
59587
|
* completes — drivers' `getAccessoryChildren()` should treat zero as
|
|
@@ -59684,7 +59706,7 @@ var gasCapability = {
|
|
|
59684
59706
|
};
|
|
59685
59707
|
var HeatingValveStatusSchema = object({
|
|
59686
59708
|
/** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
|
|
59687
|
-
position: number().min(0).max(100).nullable(),
|
|
59709
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59688
59710
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
59689
59711
|
lastFetchedAt: number()
|
|
59690
59712
|
});
|
|
@@ -59727,9 +59749,9 @@ var HumidifierStatusSchema = object({
|
|
|
59727
59749
|
/** Whether the humidifier is currently on. */
|
|
59728
59750
|
on: boolean(),
|
|
59729
59751
|
/** Current measured relative humidity (0..100). Null when not reported. */
|
|
59730
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
59752
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
59731
59753
|
/** Target relative humidity (0..100). Null when no setpoint surface. */
|
|
59732
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
59754
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
59733
59755
|
/** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
|
|
59734
59756
|
* device has no mode surface. */
|
|
59735
59757
|
mode: string().nullable(),
|
|
@@ -59739,9 +59761,9 @@ var HumidifierStatusSchema = object({
|
|
|
59739
59761
|
* `idle` / `off`). Null when the device doesn't report it. */
|
|
59740
59762
|
action: string().nullable(),
|
|
59741
59763
|
/** HA `min_humidity` attribute. Null → UI uses 0. */
|
|
59742
|
-
minHumidity: number().nullable(),
|
|
59764
|
+
minHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
59743
59765
|
/** HA `max_humidity` attribute. Null → UI uses 100. */
|
|
59744
|
-
maxHumidity: number().nullable(),
|
|
59766
|
+
maxHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
59745
59767
|
/** Ms epoch when the slice was last updated. */
|
|
59746
59768
|
lastChangedAt: number()
|
|
59747
59769
|
});
|
|
@@ -59811,22 +59833,13 @@ var humidifierCapability = {
|
|
|
59811
59833
|
* `sourceInfo`.
|
|
59812
59834
|
*/
|
|
59813
59835
|
var HumiditySensorStatusSchema = object({
|
|
59814
|
-
/** Current relative humidity, 0..100. */
|
|
59815
|
-
percent: number().min(0).max(100).meta({
|
|
59816
|
-
|
|
59836
|
+
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
59837
|
+
percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
59838
|
+
precision: 0,
|
|
59817
59839
|
label: "Humidity"
|
|
59818
|
-
}),
|
|
59840
|
+
})),
|
|
59819
59841
|
/** Ms epoch when the slice was last updated. */
|
|
59820
|
-
lastFetchedAt: number()
|
|
59821
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
59822
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
59823
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
59824
|
-
unit: string().optional(),
|
|
59825
|
-
/** Suggested decimal places for numeric display.
|
|
59826
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
59827
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
59828
|
-
* auto-formatting when absent. */
|
|
59829
|
-
precision: number().int().min(0).max(10).optional()
|
|
59842
|
+
lastFetchedAt: number()
|
|
59830
59843
|
});
|
|
59831
59844
|
var humiditySensorCapability = {
|
|
59832
59845
|
name: "humidity-sensor",
|
|
@@ -59857,12 +59870,7 @@ var humiditySensorCapability = {
|
|
|
59857
59870
|
} } },
|
|
59858
59871
|
/** Clock fields: written, but excluded from the compare that decides
|
|
59859
59872
|
* whether persisting is worth a SQLite commit. */
|
|
59860
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
59861
|
-
customize: {
|
|
59862
|
-
version: 1,
|
|
59863
|
-
advanced: "derive",
|
|
59864
|
-
targets: []
|
|
59865
|
-
}
|
|
59873
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
59866
59874
|
};
|
|
59867
59875
|
/**
|
|
59868
59876
|
* Image display cap. Models a single still image exposed by an integration —
|
|
@@ -59883,7 +59891,7 @@ var ImageStatusSchema = object({
|
|
|
59883
59891
|
* entity exposes no `entity_picture` (yet). */
|
|
59884
59892
|
url: string().nullable(),
|
|
59885
59893
|
/** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
|
|
59886
|
-
lastUpdated: number().nullable()
|
|
59894
|
+
lastUpdated: number().nullable().meta(readingMeta("none"))
|
|
59887
59895
|
});
|
|
59888
59896
|
var imageCapability = {
|
|
59889
59897
|
name: "image",
|
|
@@ -59971,19 +59979,19 @@ var NormalizedRangeSchema = object({
|
|
|
59971
59979
|
*/
|
|
59972
59980
|
var ImageSettingsStatusSchema = object({
|
|
59973
59981
|
/** Normalized 0–100. */
|
|
59974
|
-
brightness: number().optional(),
|
|
59982
|
+
brightness: number().optional().meta(readingMeta("none")),
|
|
59975
59983
|
/** Normalized 0–100. */
|
|
59976
|
-
contrast: number().optional(),
|
|
59984
|
+
contrast: number().optional().meta(readingMeta("none")),
|
|
59977
59985
|
/** Normalized 0–100. */
|
|
59978
|
-
saturation: number().optional(),
|
|
59986
|
+
saturation: number().optional().meta(readingMeta("none")),
|
|
59979
59987
|
/** Normalized 0–100. */
|
|
59980
|
-
sharpness: number().optional(),
|
|
59988
|
+
sharpness: number().optional().meta(readingMeta("none")),
|
|
59981
59989
|
mirror: boolean().optional(),
|
|
59982
59990
|
flip: boolean().optional(),
|
|
59983
59991
|
rotate: ImageRotateSchema.optional(),
|
|
59984
59992
|
whiteBalance: WhiteBalanceModeSchema.optional(),
|
|
59985
59993
|
/** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
|
|
59986
|
-
warmth: number().optional(),
|
|
59994
|
+
warmth: number().optional().meta(readingMeta("none")),
|
|
59987
59995
|
exposureMode: ExposureModeSchema.optional(),
|
|
59988
59996
|
backlightMode: BacklightModeSchema.optional(),
|
|
59989
59997
|
lastFetchedAt: number()
|
|
@@ -60262,10 +60270,10 @@ var IntercomAbilitySchema = object({
|
|
|
60262
60270
|
/** Codecs accepted by firmware, in preference order. Driver-authored strings. */
|
|
60263
60271
|
codecs: array(string()),
|
|
60264
60272
|
/** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
|
|
60265
|
-
sampleRate: number(),
|
|
60273
|
+
sampleRate: number().meta(readingMeta("none")),
|
|
60266
60274
|
duplex: _enum(["half", "full"]),
|
|
60267
60275
|
/** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
|
|
60268
|
-
maxBacklogMs: number()
|
|
60276
|
+
maxBacklogMs: number().meta(readingMeta("duration-ms"))
|
|
60269
60277
|
});
|
|
60270
60278
|
var IntercomStatusSchema = object({
|
|
60271
60279
|
/** True while a talk session is open. */
|
|
@@ -60492,13 +60500,13 @@ var LawnMowerControlStatusSchema = object({
|
|
|
60492
60500
|
activity: LawnMowerActivitySchema,
|
|
60493
60501
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
60494
60502
|
* reading. */
|
|
60495
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
60503
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60496
60504
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
60497
60505
|
* task is active / progress is unavailable. */
|
|
60498
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
60506
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60499
60507
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
60500
60508
|
* or null when unknown. */
|
|
60501
|
-
currentCode: number().nullable(),
|
|
60509
|
+
currentCode: number().nullable().meta(readingMeta("none")),
|
|
60502
60510
|
/** Human label for {@link currentCode}, or null when undecodable. */
|
|
60503
60511
|
currentCodeLabel: string().nullable(),
|
|
60504
60512
|
/** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
|
|
@@ -61031,7 +61039,7 @@ var MediaPlayerStatusSchema = object({
|
|
|
61031
61039
|
state: MediaPlayerStateSchema,
|
|
61032
61040
|
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
61033
61041
|
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
61034
|
-
volumeLevel: number().min(0).max(100).nullable(),
|
|
61042
|
+
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61035
61043
|
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
61036
61044
|
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
61037
61045
|
isMuted: boolean().nullable(),
|
|
@@ -61044,10 +61052,10 @@ var MediaPlayerStatusSchema = object({
|
|
|
61044
61052
|
currentMedia: MediaInfoSchema.nullable(),
|
|
61045
61053
|
/** Current playback position in ms. Null when not seekable or
|
|
61046
61054
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
61047
|
-
positionMs: number().int().nonnegative().nullable(),
|
|
61055
|
+
positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61048
61056
|
/** Total duration of the current media in ms. Null when unknown
|
|
61049
61057
|
* (live stream) or nothing is playing. */
|
|
61050
|
-
durationMs: number().int().nonnegative().nullable(),
|
|
61058
|
+
durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61051
61059
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
61052
61060
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
61053
61061
|
shuffle: boolean().nullable(),
|
|
@@ -61356,7 +61364,7 @@ var motionCustomize = {
|
|
|
61356
61364
|
eventName: "onMotionChanged"
|
|
61357
61365
|
},
|
|
61358
61366
|
deviceTypes: [DeviceType.Camera],
|
|
61359
|
-
apply: "
|
|
61367
|
+
apply: "add",
|
|
61360
61368
|
applyAuthority: {
|
|
61361
61369
|
kind: "camera-sensors",
|
|
61362
61370
|
timeoutField: "motionCooldownMs"
|
|
@@ -61428,7 +61436,7 @@ var MotionStatusSchema = object({
|
|
|
61428
61436
|
* it, every provider seeded it at boot, and every session left it
|
|
61429
61437
|
* `detected: true` after its detach.
|
|
61430
61438
|
*/
|
|
61431
|
-
autoClearAfterMs: number().nullable()
|
|
61439
|
+
autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
|
|
61432
61440
|
});
|
|
61433
61441
|
/**
|
|
61434
61442
|
* Payload of `motion.onMotionChanged` event + the corresponding bus
|
|
@@ -61610,7 +61618,7 @@ var MotionZoneRegionSchema = object({
|
|
|
61610
61618
|
* the grid region(s). */
|
|
61611
61619
|
var MotionZoneStatusSchema = object({
|
|
61612
61620
|
enabled: boolean(),
|
|
61613
|
-
sensitivity: number(),
|
|
61621
|
+
sensitivity: number().meta(readingMeta("none")),
|
|
61614
61622
|
/** Grid region(s). Today exactly one `grid` shape. */
|
|
61615
61623
|
regions: array(MotionZoneRegionSchema),
|
|
61616
61624
|
lastFetchedAt: number()
|
|
@@ -62117,7 +62125,7 @@ var NavigationStatusSchema = object({
|
|
|
62117
62125
|
following: boolean(),
|
|
62118
62126
|
flash: boolean(),
|
|
62119
62127
|
lightMode: NavigationLightModeSchema,
|
|
62120
|
-
lightLevel: number().min(40).max(100),
|
|
62128
|
+
lightLevel: number().min(40).max(100).meta(readingMeta("none")),
|
|
62121
62129
|
/** Ms epoch when the slice was last updated. */
|
|
62122
62130
|
lastChangedAt: number()
|
|
62123
62131
|
});
|
|
@@ -62251,13 +62259,13 @@ var NetworkLinkStatusSchema = object({
|
|
|
62251
62259
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
62252
62260
|
* SKIP a null rather than coerce it.
|
|
62253
62261
|
*/
|
|
62254
|
-
signalPercent: number().min(0).max(100).nullable(),
|
|
62262
|
+
signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62255
62263
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
62256
|
-
rssiDbm: number().optional(),
|
|
62264
|
+
rssiDbm: number().optional().meta(readingMeta("signal-level")),
|
|
62257
62265
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
62258
62266
|
ssid: string().optional(),
|
|
62259
62267
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
62260
|
-
lastUpdated: number()
|
|
62268
|
+
lastUpdated: number().meta(readingMeta("none"))
|
|
62261
62269
|
});
|
|
62262
62270
|
/** The slice a provider seeds before its first read: nothing is known yet. */
|
|
62263
62271
|
var NETWORK_LINK_UNKNOWN = {
|
|
@@ -62653,7 +62661,7 @@ var NotifierStatusSchema = object({
|
|
|
62653
62661
|
lastError: string().nullable(),
|
|
62654
62662
|
/** Number of deliveries currently buffered server-side (rate-limited
|
|
62655
62663
|
* notifiers may queue). 0 when the provider sends synchronously. */
|
|
62656
|
-
queueDepth: number().int().nonnegative(),
|
|
62664
|
+
queueDepth: number().int().nonnegative().meta(readingMeta("count")),
|
|
62657
62665
|
/** Per-feature capability matrix — authoritative source for the
|
|
62658
62666
|
* compose-form field gating. */
|
|
62659
62667
|
supports: NotifierSupportsSchema
|
|
@@ -62745,11 +62753,9 @@ onSent: { data: object({
|
|
|
62745
62753
|
* cap fits (HA `sensor` whose `device_class` we don't have a typed cap
|
|
62746
62754
|
* for: water flow, distance, weight, frequency, signal strength, …).
|
|
62747
62755
|
*
|
|
62748
|
-
* The
|
|
62749
|
-
*
|
|
62750
|
-
*
|
|
62751
|
-
* The UI reads them directly from the slice — they are the single source
|
|
62752
|
-
* of truth for generic numeric rendering and do NOT live on `sourceInfo`.
|
|
62756
|
+
* The quantity is not on this slice. It is chosen when the device is
|
|
62757
|
+
* adopted and stored once on `SourceInfo.quantity` (D944). `value` is in
|
|
62758
|
+
* that quantity's base unit. `null` is a refused sample (D393).
|
|
62753
62759
|
*
|
|
62754
62760
|
* Prefer the typed alternatives (`temperature-sensor`, `humidity-sensor`,
|
|
62755
62761
|
* `ambient-light-sensor`, `pressure-sensor`, `power-meter`,
|
|
@@ -62758,15 +62764,8 @@ onSent: { data: object({
|
|
|
62758
62764
|
* numeric values land here.
|
|
62759
62765
|
*/
|
|
62760
62766
|
var NumericSensorStatusSchema = object({
|
|
62761
|
-
|
|
62762
|
-
|
|
62763
|
-
* Populated live from the upstream source on each state push.
|
|
62764
|
-
* Absent for unitless sensors. */
|
|
62765
|
-
unit: string().optional(),
|
|
62766
|
-
/** Suggested decimal places for numeric display.
|
|
62767
|
-
* Populated live from the upstream source when provided.
|
|
62768
|
-
* Falls back to auto-formatting when absent. */
|
|
62769
|
-
precision: number().int().min(0).max(10).optional(),
|
|
62767
|
+
/** Reading in the device quantity's base unit. `null` when refused (D393). */
|
|
62768
|
+
value: number().nullable().meta(readingMeta("device")),
|
|
62770
62769
|
/** Ms epoch when the slice was last updated. */
|
|
62771
62770
|
lastFetchedAt: number()
|
|
62772
62771
|
});
|
|
@@ -62790,11 +62789,11 @@ var numericSensorCapability = {
|
|
|
62790
62789
|
*/
|
|
62791
62790
|
durability: "restored",
|
|
62792
62791
|
/**
|
|
62793
|
-
* History (D850): recorded series. The unit is
|
|
62792
|
+
* History (D850): recorded series. The unit is the device quantity's base,
|
|
62793
|
+
* resolved when the series is read — the cap has no single unit.
|
|
62794
62794
|
*/
|
|
62795
62795
|
history: { fields: { value: {
|
|
62796
62796
|
kind: "gauge",
|
|
62797
|
-
unitFrom: "unit",
|
|
62798
62797
|
minIntervalMs: 3e4
|
|
62799
62798
|
} } },
|
|
62800
62799
|
/** Clock fields: written, but excluded from the compare that decides
|
|
@@ -63086,23 +63085,26 @@ var PetFeederStatusSchema = object({
|
|
|
63086
63085
|
/** Food currently in the bowl (grams). Null when the device has not
|
|
63087
63086
|
* reported a reading yet. On dual-hopper models this is the combined
|
|
63088
63087
|
* bowl reading; per-hopper levels live in `food1`/`food2`. */
|
|
63089
|
-
foodLevel: number().nullable(),
|
|
63088
|
+
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
63090
63089
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
63091
63090
|
* single-hopper models. */
|
|
63092
|
-
food1: number().nullable(),
|
|
63091
|
+
food1: number().nullable().meta(readingMeta("none")),
|
|
63093
63092
|
/** Hopper-2 food level (grams) on dual-hopper feeders; null on
|
|
63094
63093
|
* single-hopper models. */
|
|
63095
|
-
food2: number().nullable(),
|
|
63094
|
+
food2: number().nullable().meta(readingMeta("none")),
|
|
63096
63095
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
63097
63096
|
* (`device_class: problem`, on = low). True when the bowl is empty /
|
|
63098
63097
|
* below the feeder's low threshold. */
|
|
63099
63098
|
lowFood: boolean(),
|
|
63100
63099
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
63101
63100
|
* device has no battery reading. */
|
|
63102
|
-
batteryPower: number().min(0).max(100).nullable(),
|
|
63103
|
-
/**
|
|
63104
|
-
*
|
|
63105
|
-
|
|
63101
|
+
batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63102
|
+
/**
|
|
63103
|
+
* Desiccant life remaining, stored in seconds (duration base, D944).
|
|
63104
|
+
* Providers convert from the days the feeder reports. Null when the
|
|
63105
|
+
* model has no desiccant sensor.
|
|
63106
|
+
*/
|
|
63107
|
+
desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
|
|
63106
63108
|
/** True while a feed is in progress. */
|
|
63107
63109
|
feeding: boolean(),
|
|
63108
63110
|
/** Decoded connectivity / power status (HA petkit device-status enum).
|
|
@@ -63113,7 +63115,7 @@ var PetFeederStatusSchema = object({
|
|
|
63113
63115
|
* with `errorCode` for consumers that want the raw integer. */
|
|
63114
63116
|
error: string().nullable(),
|
|
63115
63117
|
/** Raw device error code (0 / null = no error). */
|
|
63116
|
-
errorCode: number().nullable(),
|
|
63118
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
63117
63119
|
/** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
|
|
63118
63120
|
isDualHopper: boolean(),
|
|
63119
63121
|
/** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
|
|
@@ -63123,7 +63125,7 @@ var PetFeederStatusSchema = object({
|
|
|
63123
63125
|
/** Play a chime when dispensing. */
|
|
63124
63126
|
feedSound: boolean(),
|
|
63125
63127
|
/** Speaker / prompt volume level (device-scaled integer). */
|
|
63126
|
-
volume: number(),
|
|
63128
|
+
volume: number().meta(readingMeta("none")),
|
|
63127
63129
|
/** Ms epoch when the slice was last refreshed from the cloud. */
|
|
63128
63130
|
lastFetchedAt: number()
|
|
63129
63131
|
});
|
|
@@ -63242,7 +63244,7 @@ var petFeederCapability = {
|
|
|
63242
63244
|
},
|
|
63243
63245
|
desiccantLeftDays: {
|
|
63244
63246
|
kind: "gauge",
|
|
63245
|
-
unit: "
|
|
63247
|
+
unit: "s"
|
|
63246
63248
|
}
|
|
63247
63249
|
} }
|
|
63248
63250
|
};
|
|
@@ -63643,36 +63645,19 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
63643
63645
|
*/
|
|
63644
63646
|
var PowerMeterStatusSchema = object({
|
|
63645
63647
|
/** Instantaneous power draw in watts. */
|
|
63646
|
-
watts: number().optional().meta({
|
|
63647
|
-
unit: "W",
|
|
63648
|
-
label: "Power"
|
|
63649
|
-
}),
|
|
63648
|
+
watts: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
63650
63649
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
63651
|
-
kwhTotal: number().optional().meta({
|
|
63652
|
-
unit: "kWh",
|
|
63653
|
-
label: "Energy"
|
|
63654
|
-
}),
|
|
63650
|
+
kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
63655
63651
|
/** Voltage in volts. */
|
|
63656
|
-
volts: number().optional(),
|
|
63652
|
+
volts: number().optional().meta(readingMeta("voltage")),
|
|
63657
63653
|
/** Current in amperes. */
|
|
63658
|
-
amps: number().optional(),
|
|
63659
|
-
/** Apparent power in volt-amperes
|
|
63660
|
-
va: number().optional(),
|
|
63654
|
+
amps: number().optional().meta(readingMeta("current")),
|
|
63655
|
+
/** Apparent power in volt-amperes. */
|
|
63656
|
+
va: number().optional().meta(readingMeta("apparent-power")),
|
|
63661
63657
|
/** Power factor (0..1) if reported. */
|
|
63662
|
-
powerFactor: number().min(0).max(1).optional(),
|
|
63658
|
+
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
63663
63659
|
/** Ms epoch when the slice was last updated. */
|
|
63664
|
-
lastFetchedAt: number()
|
|
63665
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
63666
|
-
* `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
|
|
63667
|
-
* Each upstream `sensor.*` entity surfaces ONE device_class, so one
|
|
63668
|
-
* unit per slice is unambiguous. The UI prefers this over the role's
|
|
63669
|
-
* canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
|
|
63670
|
-
unit: string().optional(),
|
|
63671
|
-
/** Suggested decimal places for numeric display.
|
|
63672
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
63673
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
63674
|
-
* auto-formatting when absent. */
|
|
63675
|
-
precision: number().int().min(0).max(10).optional()
|
|
63660
|
+
lastFetchedAt: number()
|
|
63676
63661
|
});
|
|
63677
63662
|
var powerMeterCapability = {
|
|
63678
63663
|
name: "power-meter",
|
|
@@ -63736,12 +63721,7 @@ var powerMeterCapability = {
|
|
|
63736
63721
|
} },
|
|
63737
63722
|
/** Clock fields: written, but excluded from the compare that decides
|
|
63738
63723
|
* whether persisting is worth a SQLite commit. */
|
|
63739
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
63740
|
-
customize: {
|
|
63741
|
-
version: 1,
|
|
63742
|
-
advanced: "derive",
|
|
63743
|
-
targets: []
|
|
63744
|
-
}
|
|
63724
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
63745
63725
|
};
|
|
63746
63726
|
/**
|
|
63747
63727
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
@@ -63761,11 +63741,11 @@ var powerMeterCapability = {
|
|
|
63761
63741
|
*/
|
|
63762
63742
|
var GpsLocationSchema = object({
|
|
63763
63743
|
/** Latitude in decimal degrees, -90..90. */
|
|
63764
|
-
latitude: number().min(-90).max(90),
|
|
63744
|
+
latitude: number().min(-90).max(90).meta(readingMeta("angle")),
|
|
63765
63745
|
/** Longitude in decimal degrees, -180..180. */
|
|
63766
|
-
longitude: number().min(-180).max(180),
|
|
63746
|
+
longitude: number().min(-180).max(180).meta(readingMeta("angle")),
|
|
63767
63747
|
/** Reported accuracy in meters (lower = better). */
|
|
63768
|
-
accuracyMeters: number().nonnegative()
|
|
63748
|
+
accuracyMeters: number().nonnegative().meta(readingMeta("length"))
|
|
63769
63749
|
});
|
|
63770
63750
|
var PresenceStatusSchema = object({
|
|
63771
63751
|
/** `home` / `not_home` / any user-defined zone name. */
|
|
@@ -63777,7 +63757,7 @@ var PresenceStatusSchema = object({
|
|
|
63777
63757
|
gps: GpsLocationSchema.nullable(),
|
|
63778
63758
|
/** Optional battery level of the tracking device (0..100). Null
|
|
63779
63759
|
* when not reported. */
|
|
63780
|
-
batteryPercent: number().min(0).max(100).nullable(),
|
|
63760
|
+
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63781
63761
|
/** Ms epoch when the slice was last updated. */
|
|
63782
63762
|
lastChangedAt: number()
|
|
63783
63763
|
});
|
|
@@ -63827,19 +63807,10 @@ var presenceCapability = {
|
|
|
63827
63807
|
* `sourceInfo`.
|
|
63828
63808
|
*/
|
|
63829
63809
|
var PressureSensorStatusSchema = object({
|
|
63830
|
-
/** Current pressure in hPa. */
|
|
63831
|
-
hpa: number(),
|
|
63810
|
+
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
63811
|
+
hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
63832
63812
|
/** Ms epoch when the slice was last updated. */
|
|
63833
|
-
lastFetchedAt: number()
|
|
63834
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
63835
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
63836
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
63837
|
-
unit: string().optional(),
|
|
63838
|
-
/** Suggested decimal places for numeric display.
|
|
63839
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
63840
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
63841
|
-
* auto-formatting when absent. */
|
|
63842
|
-
precision: number().int().min(0).max(10).optional()
|
|
63813
|
+
lastFetchedAt: number()
|
|
63843
63814
|
});
|
|
63844
63815
|
var pressureSensorCapability = {
|
|
63845
63816
|
name: "pressure-sensor",
|
|
@@ -64253,8 +64224,8 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
|
|
|
64253
64224
|
}), _void(), { kind: "mutation" });
|
|
64254
64225
|
var PtzAutotrackSettingsSchema = object({
|
|
64255
64226
|
targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
|
|
64256
|
-
stopDelaySeconds: number().int().min(0).max(300),
|
|
64257
|
-
disappearDelaySeconds: number().int().min(0).max(300)
|
|
64227
|
+
stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
|
|
64228
|
+
disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
|
|
64258
64229
|
});
|
|
64259
64230
|
/**
|
|
64260
64231
|
* One option the camera advertises as a valid `targetType`. Drivers
|
|
@@ -64416,7 +64387,7 @@ var RunningStateStatusSchema = object({
|
|
|
64416
64387
|
/** null = unknown — never coerced to false (D393). */
|
|
64417
64388
|
running: boolean().nullable(),
|
|
64418
64389
|
/** Epoch ms the current boolean began; null while `running` is null. */
|
|
64419
|
-
since: number().nullable(),
|
|
64390
|
+
since: number().nullable().meta(readingMeta("none")),
|
|
64420
64391
|
source: RunningStateSourceSchema.meta({ label: "Source" }),
|
|
64421
64392
|
phase: string().nullable(),
|
|
64422
64393
|
/** null until a reading has been published (D393). */
|
|
@@ -64431,7 +64402,6 @@ var RUNNING_DEVICE_TYPES = Object.values(DeviceType);
|
|
|
64431
64402
|
/** Generic Customize target for the running boolean (D936). */
|
|
64432
64403
|
var runningCustomize = {
|
|
64433
64404
|
version: 1,
|
|
64434
|
-
advanced: "derive",
|
|
64435
64405
|
targets: [{
|
|
64436
64406
|
id: "running",
|
|
64437
64407
|
cardinality: "scalar",
|
|
@@ -64451,7 +64421,7 @@ var runningCustomize = {
|
|
|
64451
64421
|
DeviceType.Thermostat,
|
|
64452
64422
|
DeviceType.PetFeeder
|
|
64453
64423
|
],
|
|
64454
|
-
apply: "add
|
|
64424
|
+
apply: "add",
|
|
64455
64425
|
path: "running",
|
|
64456
64426
|
sources: [
|
|
64457
64427
|
{
|
|
@@ -65715,9 +65685,9 @@ var RecordTriggerSchema = _enum([
|
|
|
65715
65685
|
*/
|
|
65716
65686
|
var RecordWindowSchema = object({
|
|
65717
65687
|
trigger: RecordTriggerSchema,
|
|
65718
|
-
day: number().int().min(0).max(6),
|
|
65719
|
-
startMinute: number().int().min(0).max(1439),
|
|
65720
|
-
endMinute: number().int().min(1).max(1440)
|
|
65688
|
+
day: number().int().min(0).max(6).meta(readingMeta("none")),
|
|
65689
|
+
startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
|
|
65690
|
+
endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
|
|
65721
65691
|
});
|
|
65722
65692
|
/** Status of one physical volume, as the camera itself describes it. */
|
|
65723
65693
|
var OnboardStorageVolumeSchema = object({
|
|
@@ -65793,10 +65763,10 @@ var RecordingOnboardStatusSchema = object({
|
|
|
65793
65763
|
/** Master "record to the card at all" switch. */
|
|
65794
65764
|
enabled: boolean().nullable(),
|
|
65795
65765
|
overwriteWhenFull: boolean().nullable(),
|
|
65796
|
-
preRecordSec: number().nullable(),
|
|
65797
|
-
postRecordSec: number().nullable(),
|
|
65766
|
+
preRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
65767
|
+
postRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
65798
65768
|
/** Length of one recorded file, in minutes. */
|
|
65799
|
-
segmentMinutes: number().nullable(),
|
|
65769
|
+
segmentMinutes: number().nullable().meta(readingMeta("none")),
|
|
65800
65770
|
/** The primary track's weekly windows, flattened. */
|
|
65801
65771
|
windows: array(RecordWindowSchema),
|
|
65802
65772
|
/**
|
|
@@ -65812,7 +65782,7 @@ var RecordingOnboardStatusSchema = object({
|
|
|
65812
65782
|
* provider reporting a non-zero count also reports the schedule as not
|
|
65813
65783
|
* writable.
|
|
65814
65784
|
*/
|
|
65815
|
-
unreadableWindows: number(),
|
|
65785
|
+
unreadableWindows: number().meta(readingMeta("count")),
|
|
65816
65786
|
/**
|
|
65817
65787
|
* The camera is scheduled to record and has NO usable storage.
|
|
65818
65788
|
*
|
|
@@ -66657,7 +66627,7 @@ var CamStreamDescriptorSchema = object({
|
|
|
66657
66627
|
url: string().optional(),
|
|
66658
66628
|
codec: string().optional(),
|
|
66659
66629
|
resolution: CamStreamResolutionSchema.optional(),
|
|
66660
|
-
fps: number().positive().optional(),
|
|
66630
|
+
fps: number().positive().optional().meta(readingMeta("rate")),
|
|
66661
66631
|
label: string().optional(),
|
|
66662
66632
|
/** Device-level features (e.g. `battery-operated`) — drives broker policy. */
|
|
66663
66633
|
deviceFeatures: array(string()).optional(),
|
|
@@ -66744,18 +66714,18 @@ var StreamProfileSchema = _enum([
|
|
|
66744
66714
|
]);
|
|
66745
66715
|
/** The current encoding config of one stream profile. */
|
|
66746
66716
|
var StreamProfileConfigSchema = object({
|
|
66747
|
-
width: number(),
|
|
66748
|
-
height: number(),
|
|
66717
|
+
width: number().meta(readingMeta("none")),
|
|
66718
|
+
height: number().meta(readingMeta("none")),
|
|
66749
66719
|
codec: _enum(["h264", "h265"]),
|
|
66750
|
-
framerate: number(),
|
|
66751
|
-
bitrate: number(),
|
|
66720
|
+
framerate: number().meta(readingMeta("rate")),
|
|
66721
|
+
bitrate: number().meta(readingMeta("bitrate")),
|
|
66752
66722
|
bitrateMode: _enum(["vbr", "cbr"]).optional(),
|
|
66753
66723
|
encoderProfile: _enum([
|
|
66754
66724
|
"high",
|
|
66755
66725
|
"main",
|
|
66756
66726
|
"baseline"
|
|
66757
66727
|
]).optional(),
|
|
66758
|
-
gop: number().optional(),
|
|
66728
|
+
gop: number().optional().meta(readingMeta("none")),
|
|
66759
66729
|
/**
|
|
66760
66730
|
* Whether THIS profile currently carries an audio track. READ-ONLY here.
|
|
66761
66731
|
*
|
|
@@ -66976,12 +66946,7 @@ var switchCapability = {
|
|
|
66976
66946
|
label: "On",
|
|
66977
66947
|
kind: "boolean"
|
|
66978
66948
|
}
|
|
66979
|
-
}] }
|
|
66980
|
-
customize: {
|
|
66981
|
-
version: 1,
|
|
66982
|
-
advanced: "derive",
|
|
66983
|
-
targets: []
|
|
66984
|
-
}
|
|
66949
|
+
}] }
|
|
66985
66950
|
};
|
|
66986
66951
|
/** C0 controls, DEL and the C1 range: none of them belongs in a name. */
|
|
66987
66952
|
function hasControlCharacter(value) {
|
|
@@ -67564,35 +67529,21 @@ var tamperCapability = {
|
|
|
67564
67529
|
* entries with `device_class: temperature` and any future native
|
|
67565
67530
|
* thermometer.
|
|
67566
67531
|
*
|
|
67567
|
-
*
|
|
67568
|
-
*
|
|
67569
|
-
* (climate-control's `currentTemp`, energy analytics) can compose
|
|
67570
|
-
* without per-source unit fixups. The canonical display unit (`°C`) is
|
|
67571
|
-
* a descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
67572
|
-
* `sourceInfo`.
|
|
67532
|
+
* The number is Celsius (D944). Providers convert on ingest. A failed
|
|
67533
|
+
* conversion is `null`, never a guess. Display unit is a read-time preference.
|
|
67573
67534
|
*
|
|
67574
67535
|
* Status `lastFetchedAt` lets staleness-aware consumers detect a
|
|
67575
67536
|
* frozen feed (provider hung) distinct from a "temperature hasn't
|
|
67576
67537
|
* changed" steady state.
|
|
67577
67538
|
*/
|
|
67578
67539
|
var TemperatureSensorStatusSchema = object({
|
|
67579
|
-
/** Current temperature in Celsius. */
|
|
67580
|
-
celsius: number().meta({
|
|
67581
|
-
|
|
67540
|
+
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
67541
|
+
celsius: number().nullable().meta(readingMeta("temperature", {
|
|
67542
|
+
precision: 1,
|
|
67582
67543
|
label: "Temperature"
|
|
67583
|
-
}),
|
|
67544
|
+
})),
|
|
67584
67545
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
67585
|
-
lastFetchedAt: number()
|
|
67586
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
67587
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
67588
|
-
* role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
|
|
67589
|
-
* Absent → the UI falls back to the role's canonical unit. */
|
|
67590
|
-
unit: string().optional(),
|
|
67591
|
-
/** Suggested decimal places for numeric display.
|
|
67592
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
67593
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
67594
|
-
* auto-formatting when absent. */
|
|
67595
|
-
precision: number().int().min(0).max(10).optional()
|
|
67546
|
+
lastFetchedAt: number()
|
|
67596
67547
|
});
|
|
67597
67548
|
var temperatureSensorCapability = {
|
|
67598
67549
|
name: "temperature-sensor",
|
|
@@ -67623,12 +67574,7 @@ var temperatureSensorCapability = {
|
|
|
67623
67574
|
} } },
|
|
67624
67575
|
/** Clock fields: written, but excluded from the compare that decides
|
|
67625
67576
|
* whether persisting is worth a SQLite commit. */
|
|
67626
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
67627
|
-
customize: {
|
|
67628
|
-
version: 1,
|
|
67629
|
-
advanced: "derive",
|
|
67630
|
-
targets: []
|
|
67631
|
-
}
|
|
67577
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
67632
67578
|
};
|
|
67633
67579
|
/**
|
|
67634
67580
|
* toast — system-scoped singleton capability that streams toast
|
|
@@ -67911,16 +67857,16 @@ var VacuumControlStatusSchema = object({
|
|
|
67911
67857
|
]),
|
|
67912
67858
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
67913
67859
|
* reading. */
|
|
67914
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
67860
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
67915
67861
|
/** Current fan-speed token (provider-verbatim). Null when unknown or
|
|
67916
67862
|
* the vacuum has no speed control. */
|
|
67917
67863
|
fanSpeed: string().nullable(),
|
|
67918
67864
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
67919
67865
|
availableFanSpeeds: array(string()),
|
|
67920
67866
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
67921
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
67867
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
67922
67868
|
/** Current error code (0 / null = no error). */
|
|
67923
|
-
errorCode: number().nullable(),
|
|
67869
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
67924
67870
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
67925
67871
|
errorLabel: string().nullable(),
|
|
67926
67872
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -67998,7 +67944,7 @@ var ValveStatusSchema = object({
|
|
|
67998
67944
|
]),
|
|
67999
67945
|
/** 0 = fully closed, 100 = fully open. Null when the device has no
|
|
68000
67946
|
* intermediate position surface. */
|
|
68001
|
-
position: number().min(0).max(100).nullable(),
|
|
67947
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68002
67948
|
/** Ms epoch when the slice was last updated. */
|
|
68003
67949
|
lastChangedAt: number()
|
|
68004
67950
|
});
|
|
@@ -68106,9 +68052,9 @@ var vibrationCapability = {
|
|
|
68106
68052
|
*/
|
|
68107
68053
|
var WaterHeaterStatusSchema = object({
|
|
68108
68054
|
/** Current measured temperature. Null when not reported. */
|
|
68109
|
-
currentTemp: number().nullable(),
|
|
68055
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68110
68056
|
/** Target temperature setpoint. Null when no setpoint surface. */
|
|
68111
|
-
targetTemp: number().nullable(),
|
|
68057
|
+
targetTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68112
68058
|
/** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
|
|
68113
68059
|
* `heat_pump` / `high_demand` / `performance` / `off`). Null when the
|
|
68114
68060
|
* device reports an unknown state. */
|
|
@@ -68119,9 +68065,9 @@ var WaterHeaterStatusSchema = object({
|
|
|
68119
68065
|
* has no away surface. */
|
|
68120
68066
|
away: boolean().nullable(),
|
|
68121
68067
|
/** HA `min_temp` attribute. Null when not reported. */
|
|
68122
|
-
minTemp: number().nullable(),
|
|
68068
|
+
minTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68123
68069
|
/** HA `max_temp` attribute. Null when not reported. */
|
|
68124
|
-
maxTemp: number().nullable(),
|
|
68070
|
+
maxTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68125
68071
|
/** Ms epoch when the slice was last updated. */
|
|
68126
68072
|
lastChangedAt: number()
|
|
68127
68073
|
});
|
|
@@ -68206,25 +68152,18 @@ var waterHeaterCapability = {
|
|
|
68206
68152
|
* Forecast deferred — current conditions only for v1.
|
|
68207
68153
|
*/
|
|
68208
68154
|
var WeatherStatusSchema = object({
|
|
68209
|
-
/** Verbatim
|
|
68210
|
-
* when no condition has been reported yet. */
|
|
68155
|
+
/** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
|
|
68211
68156
|
condition: string().nullable(),
|
|
68212
|
-
/** Current temperature in
|
|
68213
|
-
temperature: number().nullable(),
|
|
68214
|
-
/** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
|
|
68215
|
-
temperatureUnit: string().nullable(),
|
|
68157
|
+
/** Current temperature in Celsius. Null when not provided or refused. */
|
|
68158
|
+
temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
|
|
68216
68159
|
/** Relative humidity (0..100). Null when not provided. */
|
|
68217
|
-
humidity: number().min(0).max(100).nullable(),
|
|
68218
|
-
/** Barometric pressure in
|
|
68219
|
-
pressure: number().nullable(),
|
|
68220
|
-
/**
|
|
68221
|
-
|
|
68222
|
-
/** Wind speed in the reported unit. Null when not provided. */
|
|
68223
|
-
windSpeed: number().nullable(),
|
|
68224
|
-
/** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
|
|
68225
|
-
windSpeedUnit: string().nullable(),
|
|
68160
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
68161
|
+
/** Barometric pressure in hPa. Null when not provided or refused. */
|
|
68162
|
+
pressure: number().nullable().meta(readingMeta("pressure")),
|
|
68163
|
+
/** Wind speed in metres per second. Null when not provided or refused. */
|
|
68164
|
+
windSpeed: number().nullable().meta(readingMeta("speed")),
|
|
68226
68165
|
/** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
|
|
68227
|
-
windBearing: number().nullable(),
|
|
68166
|
+
windBearing: number().nullable().meta(readingMeta("angle")),
|
|
68228
68167
|
/** Ms epoch when the slice was last updated. */
|
|
68229
68168
|
lastFetchedAt: number()
|
|
68230
68169
|
});
|
|
@@ -68252,12 +68191,12 @@ var weatherCapability = {
|
|
|
68252
68191
|
*/
|
|
68253
68192
|
durability: "session",
|
|
68254
68193
|
/**
|
|
68255
|
-
* History (D850): recorded series.
|
|
68194
|
+
* History (D850): recorded series. Numbers are the quantity base (D944).
|
|
68256
68195
|
*/
|
|
68257
68196
|
history: { fields: {
|
|
68258
68197
|
temperature: {
|
|
68259
68198
|
kind: "gauge",
|
|
68260
|
-
|
|
68199
|
+
unit: "°C",
|
|
68261
68200
|
deadband: .2
|
|
68262
68201
|
},
|
|
68263
68202
|
humidity: {
|
|
@@ -68267,12 +68206,12 @@ var weatherCapability = {
|
|
|
68267
68206
|
},
|
|
68268
68207
|
pressure: {
|
|
68269
68208
|
kind: "gauge",
|
|
68270
|
-
|
|
68209
|
+
unit: "hPa",
|
|
68271
68210
|
deadband: .5
|
|
68272
68211
|
},
|
|
68273
68212
|
windSpeed: {
|
|
68274
68213
|
kind: "gauge",
|
|
68275
|
-
|
|
68214
|
+
unit: "m/s",
|
|
68276
68215
|
deadband: .5
|
|
68277
68216
|
}
|
|
68278
68217
|
} }
|
|
@@ -69203,6 +69142,404 @@ function runningFromLawnMower(activity, previous, fetchedAt, transitionAt) {
|
|
|
69203
69142
|
if (activity === "paused") return nativeSlice(false, "paused", previous, transitionAtMs, fetchedAt, null);
|
|
69204
69143
|
return nativeSlice(false, activity, previous, transitionAtMs, fetchedAt, null);
|
|
69205
69144
|
}
|
|
69145
|
+
/**
|
|
69146
|
+
* Frozen unit → spec table. `•` in the doc marks each dimension's canonical
|
|
69147
|
+
* unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
|
|
69148
|
+
* `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
|
|
69149
|
+
* decimals). K derives from `°C = K − 273.15`.
|
|
69150
|
+
*/
|
|
69151
|
+
var UNIT_TABLE = {
|
|
69152
|
+
"°C": {
|
|
69153
|
+
dimension: "temperature",
|
|
69154
|
+
factor: 1,
|
|
69155
|
+
offset: 0
|
|
69156
|
+
},
|
|
69157
|
+
"°F": {
|
|
69158
|
+
dimension: "temperature",
|
|
69159
|
+
factor: 5 / 9,
|
|
69160
|
+
offset: -160 / 9
|
|
69161
|
+
},
|
|
69162
|
+
K: {
|
|
69163
|
+
dimension: "temperature",
|
|
69164
|
+
factor: 1,
|
|
69165
|
+
offset: -273.15
|
|
69166
|
+
},
|
|
69167
|
+
hPa: {
|
|
69168
|
+
dimension: "pressure",
|
|
69169
|
+
factor: 1,
|
|
69170
|
+
offset: 0
|
|
69171
|
+
},
|
|
69172
|
+
kPa: {
|
|
69173
|
+
dimension: "pressure",
|
|
69174
|
+
factor: 10,
|
|
69175
|
+
offset: 0
|
|
69176
|
+
},
|
|
69177
|
+
Pa: {
|
|
69178
|
+
dimension: "pressure",
|
|
69179
|
+
factor: .01,
|
|
69180
|
+
offset: 0
|
|
69181
|
+
},
|
|
69182
|
+
mbar: {
|
|
69183
|
+
dimension: "pressure",
|
|
69184
|
+
factor: 1,
|
|
69185
|
+
offset: 0
|
|
69186
|
+
},
|
|
69187
|
+
bar: {
|
|
69188
|
+
dimension: "pressure",
|
|
69189
|
+
factor: 1e3,
|
|
69190
|
+
offset: 0
|
|
69191
|
+
},
|
|
69192
|
+
inHg: {
|
|
69193
|
+
dimension: "pressure",
|
|
69194
|
+
factor: 33.8639,
|
|
69195
|
+
offset: 0
|
|
69196
|
+
},
|
|
69197
|
+
mmHg: {
|
|
69198
|
+
dimension: "pressure",
|
|
69199
|
+
factor: 1.33322,
|
|
69200
|
+
offset: 0
|
|
69201
|
+
},
|
|
69202
|
+
psi: {
|
|
69203
|
+
dimension: "pressure",
|
|
69204
|
+
factor: 68.9476,
|
|
69205
|
+
offset: 0
|
|
69206
|
+
},
|
|
69207
|
+
"m/s": {
|
|
69208
|
+
dimension: "speed",
|
|
69209
|
+
factor: 1,
|
|
69210
|
+
offset: 0
|
|
69211
|
+
},
|
|
69212
|
+
"km/h": {
|
|
69213
|
+
dimension: "speed",
|
|
69214
|
+
factor: 1 / 3.6,
|
|
69215
|
+
offset: 0
|
|
69216
|
+
},
|
|
69217
|
+
mph: {
|
|
69218
|
+
dimension: "speed",
|
|
69219
|
+
factor: .44704,
|
|
69220
|
+
offset: 0
|
|
69221
|
+
},
|
|
69222
|
+
kn: {
|
|
69223
|
+
dimension: "speed",
|
|
69224
|
+
factor: .514444,
|
|
69225
|
+
offset: 0
|
|
69226
|
+
},
|
|
69227
|
+
"mm/h": {
|
|
69228
|
+
dimension: "precipitation-rate",
|
|
69229
|
+
factor: 1,
|
|
69230
|
+
offset: 0
|
|
69231
|
+
},
|
|
69232
|
+
"in/h": {
|
|
69233
|
+
dimension: "precipitation-rate",
|
|
69234
|
+
factor: 25.4,
|
|
69235
|
+
offset: 0
|
|
69236
|
+
},
|
|
69237
|
+
m: {
|
|
69238
|
+
dimension: "length",
|
|
69239
|
+
factor: 1,
|
|
69240
|
+
offset: 0
|
|
69241
|
+
},
|
|
69242
|
+
mm: {
|
|
69243
|
+
dimension: "length",
|
|
69244
|
+
factor: .001,
|
|
69245
|
+
offset: 0
|
|
69246
|
+
},
|
|
69247
|
+
cm: {
|
|
69248
|
+
dimension: "length",
|
|
69249
|
+
factor: .01,
|
|
69250
|
+
offset: 0
|
|
69251
|
+
},
|
|
69252
|
+
km: {
|
|
69253
|
+
dimension: "length",
|
|
69254
|
+
factor: 1e3,
|
|
69255
|
+
offset: 0
|
|
69256
|
+
},
|
|
69257
|
+
in: {
|
|
69258
|
+
dimension: "length",
|
|
69259
|
+
factor: .0254,
|
|
69260
|
+
offset: 0
|
|
69261
|
+
},
|
|
69262
|
+
ft: {
|
|
69263
|
+
dimension: "length",
|
|
69264
|
+
factor: .3048,
|
|
69265
|
+
offset: 0
|
|
69266
|
+
},
|
|
69267
|
+
mi: {
|
|
69268
|
+
dimension: "length",
|
|
69269
|
+
factor: 1609.344,
|
|
69270
|
+
offset: 0
|
|
69271
|
+
},
|
|
69272
|
+
"m²": {
|
|
69273
|
+
dimension: "area",
|
|
69274
|
+
factor: 1,
|
|
69275
|
+
offset: 0
|
|
69276
|
+
},
|
|
69277
|
+
lx: {
|
|
69278
|
+
dimension: "illuminance",
|
|
69279
|
+
factor: 1,
|
|
69280
|
+
offset: 0
|
|
69281
|
+
},
|
|
69282
|
+
"W/m²": {
|
|
69283
|
+
dimension: "irradiance",
|
|
69284
|
+
factor: 1,
|
|
69285
|
+
offset: 0
|
|
69286
|
+
},
|
|
69287
|
+
W: {
|
|
69288
|
+
dimension: "power",
|
|
69289
|
+
factor: 1,
|
|
69290
|
+
offset: 0
|
|
69291
|
+
},
|
|
69292
|
+
kW: {
|
|
69293
|
+
dimension: "power",
|
|
69294
|
+
factor: 1e3,
|
|
69295
|
+
offset: 0
|
|
69296
|
+
},
|
|
69297
|
+
VA: {
|
|
69298
|
+
dimension: "apparent-power",
|
|
69299
|
+
factor: 1,
|
|
69300
|
+
offset: 0
|
|
69301
|
+
},
|
|
69302
|
+
Wh: {
|
|
69303
|
+
dimension: "energy",
|
|
69304
|
+
factor: 1,
|
|
69305
|
+
offset: 0
|
|
69306
|
+
},
|
|
69307
|
+
kWh: {
|
|
69308
|
+
dimension: "energy",
|
|
69309
|
+
factor: 1e3,
|
|
69310
|
+
offset: 0
|
|
69311
|
+
},
|
|
69312
|
+
MWh: {
|
|
69313
|
+
dimension: "energy",
|
|
69314
|
+
factor: 1e6,
|
|
69315
|
+
offset: 0
|
|
69316
|
+
},
|
|
69317
|
+
V: {
|
|
69318
|
+
dimension: "voltage",
|
|
69319
|
+
factor: 1,
|
|
69320
|
+
offset: 0
|
|
69321
|
+
},
|
|
69322
|
+
mV: {
|
|
69323
|
+
dimension: "voltage",
|
|
69324
|
+
factor: .001,
|
|
69325
|
+
offset: 0
|
|
69326
|
+
},
|
|
69327
|
+
A: {
|
|
69328
|
+
dimension: "current",
|
|
69329
|
+
factor: 1,
|
|
69330
|
+
offset: 0
|
|
69331
|
+
},
|
|
69332
|
+
mA: {
|
|
69333
|
+
dimension: "current",
|
|
69334
|
+
factor: .001,
|
|
69335
|
+
offset: 0
|
|
69336
|
+
},
|
|
69337
|
+
ppm: {
|
|
69338
|
+
dimension: "concentration-volume",
|
|
69339
|
+
factor: 1,
|
|
69340
|
+
offset: 0
|
|
69341
|
+
},
|
|
69342
|
+
ppb: {
|
|
69343
|
+
dimension: "concentration-volume",
|
|
69344
|
+
factor: .001,
|
|
69345
|
+
offset: 0
|
|
69346
|
+
},
|
|
69347
|
+
"µg/m³": {
|
|
69348
|
+
dimension: "concentration-mass",
|
|
69349
|
+
factor: 1,
|
|
69350
|
+
offset: 0
|
|
69351
|
+
},
|
|
69352
|
+
"mg/m³": {
|
|
69353
|
+
dimension: "concentration-mass",
|
|
69354
|
+
factor: 1e3,
|
|
69355
|
+
offset: 0
|
|
69356
|
+
},
|
|
69357
|
+
kg: {
|
|
69358
|
+
dimension: "mass",
|
|
69359
|
+
factor: 1,
|
|
69360
|
+
offset: 0
|
|
69361
|
+
},
|
|
69362
|
+
g: {
|
|
69363
|
+
dimension: "mass",
|
|
69364
|
+
factor: .001,
|
|
69365
|
+
offset: 0
|
|
69366
|
+
},
|
|
69367
|
+
lb: {
|
|
69368
|
+
dimension: "mass",
|
|
69369
|
+
factor: .453592,
|
|
69370
|
+
offset: 0
|
|
69371
|
+
},
|
|
69372
|
+
oz: {
|
|
69373
|
+
dimension: "mass",
|
|
69374
|
+
factor: .0283495,
|
|
69375
|
+
offset: 0
|
|
69376
|
+
},
|
|
69377
|
+
"%": {
|
|
69378
|
+
dimension: "percentage",
|
|
69379
|
+
factor: 1,
|
|
69380
|
+
offset: 0
|
|
69381
|
+
},
|
|
69382
|
+
L: {
|
|
69383
|
+
dimension: "volume",
|
|
69384
|
+
factor: 1,
|
|
69385
|
+
offset: 0
|
|
69386
|
+
},
|
|
69387
|
+
mL: {
|
|
69388
|
+
dimension: "volume",
|
|
69389
|
+
factor: .001,
|
|
69390
|
+
offset: 0
|
|
69391
|
+
},
|
|
69392
|
+
gal: {
|
|
69393
|
+
dimension: "volume",
|
|
69394
|
+
factor: 3.785411784,
|
|
69395
|
+
offset: 0
|
|
69396
|
+
},
|
|
69397
|
+
s: {
|
|
69398
|
+
dimension: "duration",
|
|
69399
|
+
factor: 1,
|
|
69400
|
+
offset: 0
|
|
69401
|
+
},
|
|
69402
|
+
ms: {
|
|
69403
|
+
dimension: "duration",
|
|
69404
|
+
factor: .001,
|
|
69405
|
+
offset: 0
|
|
69406
|
+
},
|
|
69407
|
+
min: {
|
|
69408
|
+
dimension: "duration",
|
|
69409
|
+
factor: 60,
|
|
69410
|
+
offset: 0
|
|
69411
|
+
},
|
|
69412
|
+
h: {
|
|
69413
|
+
dimension: "duration",
|
|
69414
|
+
factor: 3600,
|
|
69415
|
+
offset: 0
|
|
69416
|
+
},
|
|
69417
|
+
d: {
|
|
69418
|
+
dimension: "duration",
|
|
69419
|
+
factor: 86400,
|
|
69420
|
+
offset: 0
|
|
69421
|
+
},
|
|
69422
|
+
B: {
|
|
69423
|
+
dimension: "information",
|
|
69424
|
+
factor: 1,
|
|
69425
|
+
offset: 0
|
|
69426
|
+
},
|
|
69427
|
+
KiB: {
|
|
69428
|
+
dimension: "information",
|
|
69429
|
+
factor: 1024,
|
|
69430
|
+
offset: 0
|
|
69431
|
+
},
|
|
69432
|
+
MiB: {
|
|
69433
|
+
dimension: "information",
|
|
69434
|
+
factor: 1024 ** 2,
|
|
69435
|
+
offset: 0
|
|
69436
|
+
},
|
|
69437
|
+
GiB: {
|
|
69438
|
+
dimension: "information",
|
|
69439
|
+
factor: 1024 ** 3,
|
|
69440
|
+
offset: 0
|
|
69441
|
+
},
|
|
69442
|
+
TiB: {
|
|
69443
|
+
dimension: "information",
|
|
69444
|
+
factor: 1024 ** 4,
|
|
69445
|
+
offset: 0
|
|
69446
|
+
},
|
|
69447
|
+
kB: {
|
|
69448
|
+
dimension: "information",
|
|
69449
|
+
factor: 1e3,
|
|
69450
|
+
offset: 0
|
|
69451
|
+
},
|
|
69452
|
+
MB: {
|
|
69453
|
+
dimension: "information",
|
|
69454
|
+
factor: 1e6,
|
|
69455
|
+
offset: 0
|
|
69456
|
+
},
|
|
69457
|
+
GB: {
|
|
69458
|
+
dimension: "information",
|
|
69459
|
+
factor: 1e9,
|
|
69460
|
+
offset: 0
|
|
69461
|
+
},
|
|
69462
|
+
"°": {
|
|
69463
|
+
dimension: "angle",
|
|
69464
|
+
factor: 1,
|
|
69465
|
+
offset: 0
|
|
69466
|
+
},
|
|
69467
|
+
dBFS: {
|
|
69468
|
+
dimension: "sound-level",
|
|
69469
|
+
factor: 1,
|
|
69470
|
+
offset: 0
|
|
69471
|
+
},
|
|
69472
|
+
dBm: {
|
|
69473
|
+
dimension: "signal-level",
|
|
69474
|
+
factor: 1,
|
|
69475
|
+
offset: 0
|
|
69476
|
+
},
|
|
69477
|
+
mired: {
|
|
69478
|
+
dimension: "colour-temperature",
|
|
69479
|
+
factor: 1,
|
|
69480
|
+
offset: 0
|
|
69481
|
+
},
|
|
69482
|
+
kbps: {
|
|
69483
|
+
dimension: "bitrate",
|
|
69484
|
+
factor: 1,
|
|
69485
|
+
offset: 0
|
|
69486
|
+
},
|
|
69487
|
+
Mbps: {
|
|
69488
|
+
dimension: "bitrate",
|
|
69489
|
+
factor: 1e3,
|
|
69490
|
+
offset: 0
|
|
69491
|
+
},
|
|
69492
|
+
fps: {
|
|
69493
|
+
dimension: "rate",
|
|
69494
|
+
factor: 1,
|
|
69495
|
+
offset: 0
|
|
69496
|
+
}
|
|
69497
|
+
};
|
|
69498
|
+
/** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
|
|
69499
|
+
function specFor(unit) {
|
|
69500
|
+
return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
|
|
69501
|
+
}
|
|
69502
|
+
/** The dimension a unit belongs to, or `undefined` for an unknown unit. */
|
|
69503
|
+
function unitDimension(unit) {
|
|
69504
|
+
return specFor(unit)?.dimension;
|
|
69505
|
+
}
|
|
69506
|
+
/** Value in `spec`'s unit → its dimension's canonical unit. */
|
|
69507
|
+
function toCanonical(value, spec) {
|
|
69508
|
+
return value * spec.factor + spec.offset;
|
|
69509
|
+
}
|
|
69510
|
+
/** Canonical-unit value → the unit described by `spec`. */
|
|
69511
|
+
function fromCanonical(canonical, spec) {
|
|
69512
|
+
return (canonical - spec.offset) / spec.factor;
|
|
69513
|
+
}
|
|
69514
|
+
/**
|
|
69515
|
+
* Graceful variant for the render path: returns the converted value, or
|
|
69516
|
+
* `undefined` when the conversion is refused (unknown unit / cross-dimension).
|
|
69517
|
+
* Never throws — a render caller falls back to the raw value + source unit.
|
|
69518
|
+
*/
|
|
69519
|
+
function tryConvertUnit(value, from, to) {
|
|
69520
|
+
if (from === to) return value;
|
|
69521
|
+
const fromSpec = specFor(from);
|
|
69522
|
+
if (!fromSpec) return void 0;
|
|
69523
|
+
const toSpec = specFor(to);
|
|
69524
|
+
if (!toSpec) return void 0;
|
|
69525
|
+
if (fromSpec.dimension !== toSpec.dimension) return void 0;
|
|
69526
|
+
return fromCanonical(toCanonical(value, fromSpec), toSpec);
|
|
69527
|
+
}
|
|
69528
|
+
/**
|
|
69529
|
+
* Every known unit spelling in a dimension (table insertion order — canonical
|
|
69530
|
+
* unit first). Powers the UI's same-dimension unit picker.
|
|
69531
|
+
*/
|
|
69532
|
+
/**
|
|
69533
|
+
* Convert a difference, not a reading. Affine units (°F) shift an absolute
|
|
69534
|
+
* value by their offset; a step of 1°F is 5/9°C, so the offset is cancelled.
|
|
69535
|
+
*/
|
|
69536
|
+
function tryConvertDelta(value, from, to) {
|
|
69537
|
+
if (from === to) return value;
|
|
69538
|
+
const origin = tryConvertUnit(0, from, to);
|
|
69539
|
+
const shifted = tryConvertUnit(value, from, to);
|
|
69540
|
+
if (origin === void 0 || shifted === void 0) return void 0;
|
|
69541
|
+
return shifted - origin;
|
|
69542
|
+
}
|
|
69206
69543
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
69207
69544
|
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;
|
|
69208
69545
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
@@ -83888,7 +84225,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83888
84225
|
field: "value",
|
|
83889
84226
|
quantity: t("the reading", "il valore"),
|
|
83890
84227
|
label: t("Reading", "Valore"),
|
|
83891
|
-
unitField: "unit",
|
|
83892
84228
|
icon: "gauge"
|
|
83893
84229
|
}]
|
|
83894
84230
|
},
|
|
@@ -84024,7 +84360,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84024
84360
|
field: "temperature",
|
|
84025
84361
|
quantity: t("the outdoor temperature", "la temperatura esterna"),
|
|
84026
84362
|
label: t("Temperature", "Temperatura"),
|
|
84027
|
-
|
|
84363
|
+
unit: "°C",
|
|
84028
84364
|
icon: "thermometer"
|
|
84029
84365
|
},
|
|
84030
84366
|
{
|
|
@@ -84040,7 +84376,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84040
84376
|
field: "windSpeed",
|
|
84041
84377
|
quantity: t("the wind speed", "la velocità del vento"),
|
|
84042
84378
|
label: t("Wind", "Vento"),
|
|
84043
|
-
|
|
84379
|
+
unit: "m/s",
|
|
84044
84380
|
icon: "wind"
|
|
84045
84381
|
},
|
|
84046
84382
|
{
|
|
@@ -84048,7 +84384,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84048
84384
|
field: "pressure",
|
|
84049
84385
|
quantity: t("the air pressure", "la pressione atmosferica"),
|
|
84050
84386
|
label: t("Pressure", "Pressione"),
|
|
84051
|
-
|
|
84387
|
+
unit: "hPa",
|
|
84052
84388
|
icon: "gauge"
|
|
84053
84389
|
}
|
|
84054
84390
|
]
|
|
@@ -88413,6 +88749,96 @@ DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
|
|
|
88413
88749
|
DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
88414
88750
|
DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
|
|
88415
88751
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
88752
|
+
/**
|
|
88753
|
+
* Convert an upstream number into the stored base of its quantity (D944 §4).
|
|
88754
|
+
* An unknown unit or a unit from another dimension is a refusal: the caller
|
|
88755
|
+
* logs it and publishes null (D393). This function never guesses.
|
|
88756
|
+
*/
|
|
88757
|
+
var DIMENSION_QUANTITY = {
|
|
88758
|
+
temperature: "temperature",
|
|
88759
|
+
pressure: "pressure",
|
|
88760
|
+
speed: "speed",
|
|
88761
|
+
"precipitation-rate": "precipitation-rate",
|
|
88762
|
+
length: "length",
|
|
88763
|
+
area: "area",
|
|
88764
|
+
illuminance: "illuminance",
|
|
88765
|
+
irradiance: "irradiance",
|
|
88766
|
+
power: "power",
|
|
88767
|
+
"apparent-power": "apparent-power",
|
|
88768
|
+
energy: "energy",
|
|
88769
|
+
voltage: "voltage",
|
|
88770
|
+
current: "current",
|
|
88771
|
+
"concentration-volume": "concentration-volume",
|
|
88772
|
+
"concentration-mass": "concentration-mass",
|
|
88773
|
+
mass: "mass",
|
|
88774
|
+
percentage: "percentage",
|
|
88775
|
+
volume: "volume",
|
|
88776
|
+
duration: "duration",
|
|
88777
|
+
information: "information",
|
|
88778
|
+
angle: "angle",
|
|
88779
|
+
"sound-level": "sound-level",
|
|
88780
|
+
"signal-level": "signal-level",
|
|
88781
|
+
"colour-temperature": "colour-temperature",
|
|
88782
|
+
bitrate: "bitrate",
|
|
88783
|
+
rate: "rate"
|
|
88784
|
+
};
|
|
88785
|
+
/**
|
|
88786
|
+
* Quantity implied by a unit symbol. `ppb` and `ms` are the two symbols
|
|
88787
|
+
* whose stored base is not the dimension's canonical unit. `mm` is length;
|
|
88788
|
+
* precipitation is chosen by the caller (a device class), not by the symbol.
|
|
88789
|
+
*/
|
|
88790
|
+
function quantityFromUnit(rawUnit) {
|
|
88791
|
+
const unit = normalizeUnit(rawUnit);
|
|
88792
|
+
if (unit === void 0) return void 0;
|
|
88793
|
+
if (unit === "ppb") return "concentration-ppb";
|
|
88794
|
+
if (unit === "ms") return "duration-ms";
|
|
88795
|
+
const dimension = unitDimension(unit);
|
|
88796
|
+
if (dimension === void 0) return void 0;
|
|
88797
|
+
return DIMENSION_QUANTITY[dimension];
|
|
88798
|
+
}
|
|
88799
|
+
function ingestReading(input) {
|
|
88800
|
+
if (!Number.isFinite(input.value)) return {
|
|
88801
|
+
ok: false,
|
|
88802
|
+
reason: "non-finite"
|
|
88803
|
+
};
|
|
88804
|
+
if (input.quantity === "device") return {
|
|
88805
|
+
ok: false,
|
|
88806
|
+
reason: "unknown-unit"
|
|
88807
|
+
};
|
|
88808
|
+
const raw = input.rawUnit?.trim();
|
|
88809
|
+
if (raw === void 0 || raw.length === 0) return {
|
|
88810
|
+
ok: true,
|
|
88811
|
+
value: input.value
|
|
88812
|
+
};
|
|
88813
|
+
const unit = normalizeUnit(raw);
|
|
88814
|
+
if (unit === void 0) return {
|
|
88815
|
+
ok: false,
|
|
88816
|
+
reason: "unknown-unit"
|
|
88817
|
+
};
|
|
88818
|
+
const base = baseUnitOf(input.quantity);
|
|
88819
|
+
if (base === void 0) {
|
|
88820
|
+
if (unitDimension(unit) !== void 0) return {
|
|
88821
|
+
ok: false,
|
|
88822
|
+
reason: "cross-dimension"
|
|
88823
|
+
};
|
|
88824
|
+
return {
|
|
88825
|
+
ok: false,
|
|
88826
|
+
reason: "unknown-unit"
|
|
88827
|
+
};
|
|
88828
|
+
}
|
|
88829
|
+
const converted = tryConvertUnit(input.value, unit, base);
|
|
88830
|
+
if (converted === void 0) return unitDimension(unit) === void 0 ? {
|
|
88831
|
+
ok: false,
|
|
88832
|
+
reason: "unknown-unit"
|
|
88833
|
+
} : {
|
|
88834
|
+
ok: false,
|
|
88835
|
+
reason: "cross-dimension"
|
|
88836
|
+
};
|
|
88837
|
+
return {
|
|
88838
|
+
ok: true,
|
|
88839
|
+
value: converted
|
|
88840
|
+
};
|
|
88841
|
+
}
|
|
88416
88842
|
var MB = 1024 * 1024;
|
|
88417
88843
|
1024 * MB, 3072 * MB;
|
|
88418
88844
|
//#endregion
|
|
@@ -191109,7 +191535,7 @@ var CONSUMABLES_COLD_START = {
|
|
|
191109
191535
|
lastFetchedAt: 0
|
|
191110
191536
|
};
|
|
191111
191537
|
function itemEqual(a, b) {
|
|
191112
|
-
return a.key === b.key && a.label === b.label && a.level === b.level && a.status === b.status && a.lastResetAt === b.lastResetAt && a.resettable === b.resettable && a.kind === b.kind && a.remaining?.value === b.remaining?.value && a.remaining?.
|
|
191538
|
+
return a.key === b.key && a.label === b.label && a.level === b.level && a.status === b.status && a.lastResetAt === b.lastResetAt && a.resettable === b.resettable && a.kind === b.kind && a.remaining?.value === b.remaining?.value && a.remaining?.kind === b.remaining?.kind;
|
|
191113
191539
|
}
|
|
191114
191540
|
function itemsEqual(a, b) {
|
|
191115
191541
|
return a.length === b.length && a.every((item, i) => {
|
|
@@ -191807,6 +192233,40 @@ var DreameMowerSubstateDevice = class extends DreameChildDevice {
|
|
|
191807
192233
|
}
|
|
191808
192234
|
};
|
|
191809
192235
|
//#endregion
|
|
192236
|
+
//#region src/dreame-reading.ts
|
|
192237
|
+
/** Catalog unit → the quantity stored on the device. `times` is a count. */
|
|
192238
|
+
function dreameQuantity(unit) {
|
|
192239
|
+
if (unit === void 0 || unit.length === 0) return "none";
|
|
192240
|
+
if (unit === "times") return "count";
|
|
192241
|
+
const learned = quantityFromUnit(unit);
|
|
192242
|
+
if (learned === void 0 || learned === "device") return null;
|
|
192243
|
+
return learned;
|
|
192244
|
+
}
|
|
192245
|
+
/** Upstream number → base. A count ignores the word `times`. */
|
|
192246
|
+
function dreameToBase(value, unit, quantity) {
|
|
192247
|
+
const result = ingestReading({
|
|
192248
|
+
value,
|
|
192249
|
+
rawUnit: quantity === "count" ? void 0 : unit,
|
|
192250
|
+
quantity
|
|
192251
|
+
});
|
|
192252
|
+
return result.ok ? result.value : null;
|
|
192253
|
+
}
|
|
192254
|
+
/** Base number → the unit the robot still speaks. */
|
|
192255
|
+
function dreameToUpstream(value, unit, quantity) {
|
|
192256
|
+
const base = baseUnitOf(quantity);
|
|
192257
|
+
const upstream = unit === void 0 ? void 0 : normalizeUnit(unit);
|
|
192258
|
+
if (base === void 0 || upstream === void 0 || upstream === base) return value;
|
|
192259
|
+
return tryConvertUnit(value, base, upstream) ?? null;
|
|
192260
|
+
}
|
|
192261
|
+
/** A step is a difference, so an affine offset must not move it. */
|
|
192262
|
+
function dreameStep(step, unit, quantity) {
|
|
192263
|
+
if (step === void 0) return void 0;
|
|
192264
|
+
const base = baseUnitOf(quantity);
|
|
192265
|
+
const from = unit === void 0 ? void 0 : normalizeUnit(unit);
|
|
192266
|
+
if (base === void 0 || from === void 0 || from === base) return step;
|
|
192267
|
+
return tryConvertDelta(step, from, base);
|
|
192268
|
+
}
|
|
192269
|
+
//#endregion
|
|
191810
192270
|
//#region src/devices/dreame-number-device.ts
|
|
191811
192271
|
var CAP_NAME$5 = "control";
|
|
191812
192272
|
/** Persisted config for a generic number child: the shared Dreame child config
|
|
@@ -191831,22 +192291,25 @@ var dreameNumberSchema = dreameChildSchema.extend({
|
|
|
191831
192291
|
var DreameNumberDevice = class extends DreameChildDevice {
|
|
191832
192292
|
#siid;
|
|
191833
192293
|
#piid;
|
|
191834
|
-
#min;
|
|
191835
|
-
#max;
|
|
191836
|
-
#step;
|
|
191837
192294
|
#unit;
|
|
192295
|
+
#quantity;
|
|
192296
|
+
#baseMin;
|
|
192297
|
+
#baseMax;
|
|
192298
|
+
#baseStep;
|
|
191838
192299
|
constructor(ctx) {
|
|
191839
192300
|
super(ctx, DeviceRole.NumericControl);
|
|
191840
192301
|
const cfg = dreameNumberSchema.parse(ctx.persistedConfig ?? {});
|
|
191841
192302
|
this.#siid = cfg.siid;
|
|
191842
192303
|
this.#piid = cfg.piid;
|
|
191843
|
-
this.#min = cfg.min;
|
|
191844
|
-
this.#max = cfg.max;
|
|
191845
|
-
this.#step = cfg.step;
|
|
191846
192304
|
this.#unit = cfg.unit;
|
|
192305
|
+
this.#quantity = dreameQuantity(cfg.unit);
|
|
192306
|
+
this.#baseMin = this.#toBase(cfg.min) ?? cfg.min;
|
|
192307
|
+
this.#baseMax = this.#toBase(cfg.max) ?? cfg.max;
|
|
192308
|
+
this.#baseStep = this.#quantity === null ? cfg.step : dreameStep(cfg.step, cfg.unit, this.#quantity);
|
|
191847
192309
|
this.updateSourceInfo({
|
|
191848
192310
|
id: `${this.dreameDeviceId}:number:${cfg.siid}-${cfg.piid}`,
|
|
191849
|
-
system: "dreame"
|
|
192311
|
+
system: "dreame",
|
|
192312
|
+
...this.#quantity !== null ? { quantity: this.#quantity } : {}
|
|
191850
192313
|
});
|
|
191851
192314
|
}
|
|
191852
192315
|
/** Read the configured property's numeric value, or null when absent. */
|
|
@@ -191854,41 +192317,68 @@ var DreameNumberDevice = class extends DreameChildDevice {
|
|
|
191854
192317
|
const v = this.resolveHandle()?.getProperty(this.#siid, this.#piid)?.value;
|
|
191855
192318
|
return typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
191856
192319
|
}
|
|
191857
|
-
#
|
|
192320
|
+
#toBase(value) {
|
|
192321
|
+
if (this.#quantity === null) return null;
|
|
192322
|
+
return dreameToBase(value, this.#unit, this.#quantity);
|
|
192323
|
+
}
|
|
192324
|
+
#slice(upstream) {
|
|
192325
|
+
const value = this.#toBase(upstream);
|
|
192326
|
+
if (value === null) {
|
|
192327
|
+
this.ctx.logger.warn("numeric reading refused", {
|
|
192328
|
+
tags: { deviceId: this.id },
|
|
192329
|
+
meta: {
|
|
192330
|
+
quantity: this.#quantity ?? "device",
|
|
192331
|
+
unit: this.#unit ?? "",
|
|
192332
|
+
reason: "unknown-unit"
|
|
192333
|
+
}
|
|
192334
|
+
});
|
|
192335
|
+
return null;
|
|
192336
|
+
}
|
|
191858
192337
|
return {
|
|
191859
192338
|
kind: "numeric",
|
|
191860
192339
|
value,
|
|
191861
192340
|
options: [],
|
|
191862
192341
|
lastChangedAt: Date.now(),
|
|
191863
|
-
min: this.#
|
|
191864
|
-
max: this.#
|
|
191865
|
-
step: this.#
|
|
191866
|
-
...this.#unit !== void 0 ? { unit: this.#unit } : {}
|
|
192342
|
+
min: this.#baseMin,
|
|
192343
|
+
max: this.#baseMax,
|
|
192344
|
+
...this.#baseStep !== void 0 ? { step: this.#baseStep } : {}
|
|
191867
192345
|
};
|
|
191868
192346
|
}
|
|
191869
192347
|
registerControlCap() {
|
|
191870
192348
|
this.ctx.registerNativeCap(controlCapability, {
|
|
191871
|
-
getStatus: async () => this.runtimeState.getCapState(CAP_NAME$5) ??
|
|
192349
|
+
getStatus: async () => this.runtimeState.getCapState(CAP_NAME$5) ?? {
|
|
192350
|
+
kind: "numeric",
|
|
192351
|
+
value: 0,
|
|
192352
|
+
options: [],
|
|
192353
|
+
lastChangedAt: 0
|
|
192354
|
+
},
|
|
191872
192355
|
setValue: async ({ control }) => {
|
|
191873
192356
|
if (control.kind !== "numeric") throw new Error(`DreameNumberDevice: expected a numeric value (device=${this.dreameDeviceId}, ${this.#siid}/${this.#piid})`);
|
|
192357
|
+
if (this.#quantity === null) throw new Error(`DreameNumberDevice: unknown unit (device=${this.dreameDeviceId}, ${this.#siid}/${this.#piid})`);
|
|
191874
192358
|
const handle = this.resolveHandle();
|
|
191875
192359
|
if (handle === null) throw new Error(`DreameNumberDevice: handle unavailable (device=${this.dreameDeviceId}, ${this.#siid}/${this.#piid})`);
|
|
191876
|
-
const
|
|
192360
|
+
const upstream = dreameToUpstream(Math.min(this.#baseMax, Math.max(this.#baseMin, control.value)), this.#unit, this.#quantity);
|
|
192361
|
+
if (upstream === null) throw new Error(`DreameNumberDevice: cannot convert back (device=${this.dreameDeviceId}, ${this.#siid}/${this.#piid})`);
|
|
191877
192362
|
await handle.setProperty({
|
|
191878
192363
|
siid: this.#siid,
|
|
191879
192364
|
piid: this.#piid,
|
|
191880
|
-
value:
|
|
192365
|
+
value: upstream
|
|
191881
192366
|
});
|
|
191882
|
-
|
|
192367
|
+
const slice = this.#slice(upstream);
|
|
192368
|
+
if (slice !== null) this.runtimeState.setCapState(CAP_NAME$5, slice);
|
|
191883
192369
|
}
|
|
191884
192370
|
});
|
|
191885
|
-
|
|
192371
|
+
const initial = this.#read();
|
|
192372
|
+
const slice = initial === null ? null : this.#slice(initial);
|
|
192373
|
+
if (slice !== null) this.runtimeState.setCapState(CAP_NAME$5, slice);
|
|
191886
192374
|
}
|
|
191887
192375
|
recomputeSlice() {
|
|
191888
192376
|
const value = this.#read();
|
|
191889
192377
|
if (value === null) return;
|
|
191890
|
-
|
|
191891
|
-
|
|
192378
|
+
const slice = this.#slice(value);
|
|
192379
|
+
if (slice === null) return;
|
|
192380
|
+
if (this.runtimeState.getCapState(CAP_NAME$5)?.value === slice.value) return;
|
|
192381
|
+
this.runtimeState.setCapState(CAP_NAME$5, slice);
|
|
191892
192382
|
}
|
|
191893
192383
|
};
|
|
191894
192384
|
//#endregion
|
|
@@ -191919,9 +192409,11 @@ var DreameNumericSensorDevice = class extends DreameChildDevice {
|
|
|
191919
192409
|
this.#siid = cfg.siid;
|
|
191920
192410
|
this.#piid = cfg.piid;
|
|
191921
192411
|
this.#unit = cfg.unit;
|
|
192412
|
+
const quantity = dreameQuantity(cfg.unit);
|
|
191922
192413
|
this.updateSourceInfo({
|
|
191923
192414
|
id: `${this.dreameDeviceId}:sensor:${cfg.siid}-${cfg.piid}`,
|
|
191924
|
-
system: "dreame"
|
|
192415
|
+
system: "dreame",
|
|
192416
|
+
...quantity !== null ? { quantity } : {}
|
|
191925
192417
|
});
|
|
191926
192418
|
}
|
|
191927
192419
|
/** Read the configured property's numeric value from the shared handle, or null. */
|
|
@@ -191930,22 +192422,51 @@ var DreameNumericSensorDevice = class extends DreameChildDevice {
|
|
|
191930
192422
|
return typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
191931
192423
|
}
|
|
191932
192424
|
#slice(value) {
|
|
192425
|
+
const quantity = dreameQuantity(this.#unit);
|
|
192426
|
+
if (quantity === null) {
|
|
192427
|
+
this.ctx.logger.warn("numeric reading refused", {
|
|
192428
|
+
tags: { deviceId: this.id },
|
|
192429
|
+
meta: {
|
|
192430
|
+
quantity: "device",
|
|
192431
|
+
unit: this.#unit ?? "",
|
|
192432
|
+
reason: "unknown-unit"
|
|
192433
|
+
}
|
|
192434
|
+
});
|
|
192435
|
+
return null;
|
|
192436
|
+
}
|
|
192437
|
+
const stored = dreameToBase(value, this.#unit, quantity);
|
|
192438
|
+
if (stored === null) {
|
|
192439
|
+
this.ctx.logger.warn("numeric reading refused", {
|
|
192440
|
+
tags: { deviceId: this.id },
|
|
192441
|
+
meta: {
|
|
192442
|
+
quantity,
|
|
192443
|
+
unit: this.#unit ?? "",
|
|
192444
|
+
reason: "unknown-unit"
|
|
192445
|
+
}
|
|
192446
|
+
});
|
|
192447
|
+
return null;
|
|
192448
|
+
}
|
|
191933
192449
|
return {
|
|
191934
|
-
value,
|
|
191935
|
-
...this.#unit !== void 0 ? { unit: this.#unit } : {},
|
|
192450
|
+
value: stored,
|
|
191936
192451
|
lastFetchedAt: Date.now()
|
|
191937
192452
|
};
|
|
191938
192453
|
}
|
|
191939
192454
|
registerControlCap() {
|
|
191940
|
-
this.ctx.registerNativeCap(numericSensorCapability, { getStatus: async () => this.runtimeState.getCapState(CAP_NAME$4) ??
|
|
192455
|
+
this.ctx.registerNativeCap(numericSensorCapability, { getStatus: async () => this.runtimeState.getCapState(CAP_NAME$4) ?? {
|
|
192456
|
+
value: 0,
|
|
192457
|
+
lastFetchedAt: 0
|
|
192458
|
+
} });
|
|
191941
192459
|
const initial = this.#readValue();
|
|
191942
|
-
|
|
192460
|
+
const slice = initial === null ? null : this.#slice(initial);
|
|
192461
|
+
if (slice !== null) this.runtimeState.setCapState(CAP_NAME$4, slice);
|
|
191943
192462
|
}
|
|
191944
192463
|
recomputeSlice() {
|
|
191945
192464
|
const value = this.#readValue();
|
|
191946
192465
|
if (value === null) return;
|
|
191947
|
-
|
|
191948
|
-
|
|
192466
|
+
const slice = this.#slice(value);
|
|
192467
|
+
if (slice === null) return;
|
|
192468
|
+
if (this.runtimeState.getCapState(CAP_NAME$4)?.value === slice.value) return;
|
|
192469
|
+
this.runtimeState.setCapState(CAP_NAME$4, slice);
|
|
191949
192470
|
}
|
|
191950
192471
|
};
|
|
191951
192472
|
//#endregion
|