@camstack/addon-provider-rademacher 0.2.146 → 0.2.148
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/CHANGELOG.md +21 -0
- package/README.md +34 -0
- package/dist/addon.js +1607 -522
- package/dist/addon.mjs +1607 -522
- package/package.json +4 -2
package/dist/addon.js
CHANGED
|
@@ -984,7 +984,7 @@ var Rademacher = class {
|
|
|
984
984
|
}
|
|
985
985
|
};
|
|
986
986
|
//#endregion
|
|
987
|
-
//#region ../types/dist/event-category-
|
|
987
|
+
//#region ../types/dist/event-category-CT-hz5Rb.mjs
|
|
988
988
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
989
989
|
EventCategory["SystemBoot"] = "system.boot";
|
|
990
990
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -1310,6 +1310,14 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
1310
1310
|
/** The recovery edge of {@link PipelineMotionNoisy}: the motion fell back, or a detection arrived. */
|
|
1311
1311
|
EventCategory["PipelineMotionCalm"] = "pipeline.motion-calm";
|
|
1312
1312
|
/**
|
|
1313
|
+
* A camera's package LLM confirm has failed closed three times in a row
|
|
1314
|
+
* (D931, 2026-10-26). One edge per camera until
|
|
1315
|
+
* {@link PipelinePackageLlmAnswered}.
|
|
1316
|
+
*/
|
|
1317
|
+
EventCategory["PipelinePackageLlmSilent"] = "pipeline.package-llm-silent";
|
|
1318
|
+
/** The recovery edge of {@link PipelinePackageLlmSilent}: a confirm returned yes or no. */
|
|
1319
|
+
EventCategory["PipelinePackageLlmAnswered"] = "pipeline.package-llm-answered";
|
|
1320
|
+
/**
|
|
1313
1321
|
* Per-camera pipeline config was mutated by the orchestrator
|
|
1314
1322
|
* (a settings change via `setAgentInferenceDevices` /
|
|
1315
1323
|
* `setCameraStepToggle` / `setCameraStepSettings`). Orchestrator
|
|
@@ -6419,7 +6427,7 @@ var ZodIssueCode = {
|
|
|
6419
6427
|
var ZodFirstPartyTypeKind;
|
|
6420
6428
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
6421
6429
|
//#endregion
|
|
6422
|
-
//#region ../types/dist/sleep
|
|
6430
|
+
//#region ../types/dist/sleep--jXS9hWo.mjs
|
|
6423
6431
|
/**
|
|
6424
6432
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6425
6433
|
* window to float samples (D455).
|
|
@@ -8973,6 +8981,99 @@ var RecordKindDeclarationSchema = object({
|
|
|
8973
8981
|
}).strict();
|
|
8974
8982
|
/** The kind names: lower-case, dash separated, short enough for an index name. */
|
|
8975
8983
|
var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
|
|
8984
|
+
var READING_QUANTITIES = [
|
|
8985
|
+
"temperature",
|
|
8986
|
+
"pressure",
|
|
8987
|
+
"speed",
|
|
8988
|
+
"precipitation",
|
|
8989
|
+
"precipitation-rate",
|
|
8990
|
+
"length",
|
|
8991
|
+
"area",
|
|
8992
|
+
"illuminance",
|
|
8993
|
+
"irradiance",
|
|
8994
|
+
"power",
|
|
8995
|
+
"energy",
|
|
8996
|
+
"voltage",
|
|
8997
|
+
"current",
|
|
8998
|
+
"apparent-power",
|
|
8999
|
+
"humidity",
|
|
9000
|
+
"percentage",
|
|
9001
|
+
"concentration-volume",
|
|
9002
|
+
"concentration-mass",
|
|
9003
|
+
"concentration-ppb",
|
|
9004
|
+
"volume",
|
|
9005
|
+
"duration",
|
|
9006
|
+
"duration-ms",
|
|
9007
|
+
"information",
|
|
9008
|
+
"sound-level",
|
|
9009
|
+
"signal-level",
|
|
9010
|
+
"colour-temperature",
|
|
9011
|
+
"angle",
|
|
9012
|
+
"bitrate",
|
|
9013
|
+
"rate",
|
|
9014
|
+
"mass",
|
|
9015
|
+
"index",
|
|
9016
|
+
"ratio",
|
|
9017
|
+
"count",
|
|
9018
|
+
"none",
|
|
9019
|
+
"device"
|
|
9020
|
+
];
|
|
9021
|
+
var ReadingQuantitySchema = _enum(READING_QUANTITIES);
|
|
9022
|
+
new Set(READING_QUANTITIES);
|
|
9023
|
+
/**
|
|
9024
|
+
* Stored unit symbol for a quantity. `none` means the number is dimensionless
|
|
9025
|
+
* (or an index / ratio / count). `device` means the cap has no single unit.
|
|
9026
|
+
*/
|
|
9027
|
+
var QUANTITY_BASE = {
|
|
9028
|
+
temperature: "°C",
|
|
9029
|
+
pressure: "hPa",
|
|
9030
|
+
speed: "m/s",
|
|
9031
|
+
precipitation: "mm",
|
|
9032
|
+
"precipitation-rate": "mm/h",
|
|
9033
|
+
length: "m",
|
|
9034
|
+
area: "m²",
|
|
9035
|
+
illuminance: "lx",
|
|
9036
|
+
irradiance: "W/m²",
|
|
9037
|
+
power: "W",
|
|
9038
|
+
energy: "kWh",
|
|
9039
|
+
voltage: "V",
|
|
9040
|
+
current: "A",
|
|
9041
|
+
"apparent-power": "VA",
|
|
9042
|
+
humidity: "%",
|
|
9043
|
+
percentage: "%",
|
|
9044
|
+
"concentration-volume": "ppm",
|
|
9045
|
+
"concentration-mass": "µg/m³",
|
|
9046
|
+
"concentration-ppb": "ppb",
|
|
9047
|
+
volume: "L",
|
|
9048
|
+
duration: "s",
|
|
9049
|
+
"duration-ms": "ms",
|
|
9050
|
+
information: "B",
|
|
9051
|
+
"sound-level": "dBFS",
|
|
9052
|
+
"signal-level": "dBm",
|
|
9053
|
+
"colour-temperature": "mired",
|
|
9054
|
+
angle: "°",
|
|
9055
|
+
bitrate: "kbps",
|
|
9056
|
+
rate: "fps",
|
|
9057
|
+
mass: "kg",
|
|
9058
|
+
index: "none",
|
|
9059
|
+
ratio: "none",
|
|
9060
|
+
count: "none",
|
|
9061
|
+
none: "none",
|
|
9062
|
+
device: "device"
|
|
9063
|
+
};
|
|
9064
|
+
/** The stored unit, or `undefined` when the quantity has no unit symbol. */
|
|
9065
|
+
function baseUnitOf(quantity) {
|
|
9066
|
+
const symbol = QUANTITY_BASE[quantity];
|
|
9067
|
+
if (symbol === "none" || symbol === "device") return void 0;
|
|
9068
|
+
return symbol;
|
|
9069
|
+
}
|
|
9070
|
+
function readingMeta(quantity, extras) {
|
|
9071
|
+
return {
|
|
9072
|
+
quantity,
|
|
9073
|
+
...extras?.precision !== void 0 ? { precision: extras.precision } : {},
|
|
9074
|
+
...extras?.label !== void 0 ? { label: extras.label } : {}
|
|
9075
|
+
};
|
|
9076
|
+
}
|
|
8976
9077
|
/** Shared Zod schemas used across streaming capabilities. */
|
|
8977
9078
|
var CamProfileSchema = _enum([
|
|
8978
9079
|
"high",
|
|
@@ -9000,8 +9101,8 @@ var CamStreamKindSchema = _enum([
|
|
|
9000
9101
|
"derived"
|
|
9001
9102
|
]);
|
|
9002
9103
|
var CamStreamResolutionSchema = object({
|
|
9003
|
-
width: number().int().positive(),
|
|
9004
|
-
height: number().int().positive()
|
|
9104
|
+
width: number().int().positive().meta(readingMeta("none")),
|
|
9105
|
+
height: number().int().positive().meta(readingMeta("none"))
|
|
9005
9106
|
});
|
|
9006
9107
|
var CameraStreamSchema = object({
|
|
9007
9108
|
/** Stable, provider-assigned id unique within the (deviceId) scope. */
|
|
@@ -9519,6 +9620,9 @@ var CAP_NAMES = Object.freeze([
|
|
|
9519
9620
|
"filesystem-browse",
|
|
9520
9621
|
"flood",
|
|
9521
9622
|
"gas",
|
|
9623
|
+
"group-active",
|
|
9624
|
+
"group-commands",
|
|
9625
|
+
"group-status",
|
|
9522
9626
|
"heating-valve",
|
|
9523
9627
|
"humidifier",
|
|
9524
9628
|
"humidity-sensor",
|
|
@@ -9539,6 +9643,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9539
9643
|
"login-method",
|
|
9540
9644
|
"media-player",
|
|
9541
9645
|
"mesh-network",
|
|
9646
|
+
"meter",
|
|
9542
9647
|
"metrics-provider",
|
|
9543
9648
|
"model-convert",
|
|
9544
9649
|
"model-distributor",
|
|
@@ -9648,14 +9753,6 @@ Object.freeze({
|
|
|
9648
9753
|
{
|
|
9649
9754
|
"path": "aqi",
|
|
9650
9755
|
"valueType": "number"
|
|
9651
|
-
},
|
|
9652
|
-
{
|
|
9653
|
-
"path": "unit",
|
|
9654
|
-
"valueType": "string"
|
|
9655
|
-
},
|
|
9656
|
-
{
|
|
9657
|
-
"path": "precision",
|
|
9658
|
-
"valueType": "number"
|
|
9659
9756
|
}
|
|
9660
9757
|
],
|
|
9661
9758
|
"alarm-panel": [
|
|
@@ -9691,20 +9788,10 @@ Object.freeze({
|
|
|
9691
9788
|
"valueType": "boolean"
|
|
9692
9789
|
}
|
|
9693
9790
|
],
|
|
9694
|
-
"ambient-light-sensor": [
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
},
|
|
9699
|
-
{
|
|
9700
|
-
"path": "unit",
|
|
9701
|
-
"valueType": "string"
|
|
9702
|
-
},
|
|
9703
|
-
{
|
|
9704
|
-
"path": "precision",
|
|
9705
|
-
"valueType": "number"
|
|
9706
|
-
}
|
|
9707
|
-
],
|
|
9791
|
+
"ambient-light-sensor": [{
|
|
9792
|
+
"path": "lux",
|
|
9793
|
+
"valueType": "number"
|
|
9794
|
+
}],
|
|
9708
9795
|
"audio-metrics": [
|
|
9709
9796
|
{
|
|
9710
9797
|
"path": "ts",
|
|
@@ -9784,6 +9871,10 @@ Object.freeze({
|
|
|
9784
9871
|
"path": "lastUpdated",
|
|
9785
9872
|
"valueType": "number"
|
|
9786
9873
|
},
|
|
9874
|
+
{
|
|
9875
|
+
"path": "volts",
|
|
9876
|
+
"valueType": "number"
|
|
9877
|
+
},
|
|
9787
9878
|
{
|
|
9788
9879
|
"path": "lastContactAt",
|
|
9789
9880
|
"valueType": "number"
|
|
@@ -10026,10 +10117,6 @@ Object.freeze({
|
|
|
10026
10117
|
"datetime"
|
|
10027
10118
|
]
|
|
10028
10119
|
},
|
|
10029
|
-
{
|
|
10030
|
-
"path": "unit",
|
|
10031
|
-
"valueType": "string"
|
|
10032
|
-
},
|
|
10033
10120
|
{
|
|
10034
10121
|
"path": "min",
|
|
10035
10122
|
"valueType": "number"
|
|
@@ -10041,19 +10128,6 @@ Object.freeze({
|
|
|
10041
10128
|
{
|
|
10042
10129
|
"path": "step",
|
|
10043
10130
|
"valueType": "number"
|
|
10044
|
-
},
|
|
10045
|
-
{
|
|
10046
|
-
"path": "precision",
|
|
10047
|
-
"valueType": "number"
|
|
10048
|
-
},
|
|
10049
|
-
{
|
|
10050
|
-
"path": "format",
|
|
10051
|
-
"valueType": "enum",
|
|
10052
|
-
"enumValues": [
|
|
10053
|
-
"date",
|
|
10054
|
-
"time",
|
|
10055
|
-
"datetime"
|
|
10056
|
-
]
|
|
10057
10131
|
}
|
|
10058
10132
|
],
|
|
10059
10133
|
"cover": [
|
|
@@ -10294,13 +10368,8 @@ Object.freeze({
|
|
|
10294
10368
|
"path": "value",
|
|
10295
10369
|
"valueType": "string"
|
|
10296
10370
|
}, {
|
|
10297
|
-
"path": "
|
|
10298
|
-
"valueType": "
|
|
10299
|
-
"enumValues": [
|
|
10300
|
-
"date",
|
|
10301
|
-
"time",
|
|
10302
|
-
"datetime"
|
|
10303
|
-
]
|
|
10371
|
+
"path": "timestamp",
|
|
10372
|
+
"valueType": "boolean"
|
|
10304
10373
|
}],
|
|
10305
10374
|
"event-emitter": [
|
|
10306
10375
|
{
|
|
@@ -10373,6 +10442,24 @@ Object.freeze({
|
|
|
10373
10442
|
"path": "detected",
|
|
10374
10443
|
"valueType": "boolean"
|
|
10375
10444
|
}],
|
|
10445
|
+
"group-active": [{
|
|
10446
|
+
"path": "active",
|
|
10447
|
+
"valueType": "boolean"
|
|
10448
|
+
}],
|
|
10449
|
+
"group-status": [
|
|
10450
|
+
{
|
|
10451
|
+
"path": "included",
|
|
10452
|
+
"valueType": "number"
|
|
10453
|
+
},
|
|
10454
|
+
{
|
|
10455
|
+
"path": "excluded",
|
|
10456
|
+
"valueType": "number"
|
|
10457
|
+
},
|
|
10458
|
+
{
|
|
10459
|
+
"path": "total",
|
|
10460
|
+
"valueType": "number"
|
|
10461
|
+
}
|
|
10462
|
+
],
|
|
10376
10463
|
"heating-valve": [{
|
|
10377
10464
|
"path": "position",
|
|
10378
10465
|
"valueType": "number"
|
|
@@ -10407,20 +10494,10 @@ Object.freeze({
|
|
|
10407
10494
|
"valueType": "number"
|
|
10408
10495
|
}
|
|
10409
10496
|
],
|
|
10410
|
-
"humidity-sensor": [
|
|
10411
|
-
|
|
10412
|
-
|
|
10413
|
-
|
|
10414
|
-
},
|
|
10415
|
-
{
|
|
10416
|
-
"path": "unit",
|
|
10417
|
-
"valueType": "string"
|
|
10418
|
-
},
|
|
10419
|
-
{
|
|
10420
|
-
"path": "precision",
|
|
10421
|
-
"valueType": "number"
|
|
10422
|
-
}
|
|
10423
|
-
],
|
|
10497
|
+
"humidity-sensor": [{
|
|
10498
|
+
"path": "percent",
|
|
10499
|
+
"valueType": "number"
|
|
10500
|
+
}],
|
|
10424
10501
|
"image": [{
|
|
10425
10502
|
"path": "url",
|
|
10426
10503
|
"valueType": "string"
|
|
@@ -10628,6 +10705,10 @@ Object.freeze({
|
|
|
10628
10705
|
]
|
|
10629
10706
|
}
|
|
10630
10707
|
],
|
|
10708
|
+
"meter": [{
|
|
10709
|
+
"path": "value",
|
|
10710
|
+
"valueType": "number"
|
|
10711
|
+
}],
|
|
10631
10712
|
"motion": [
|
|
10632
10713
|
{
|
|
10633
10714
|
"path": "detected",
|
|
@@ -10759,20 +10840,10 @@ Object.freeze({
|
|
|
10759
10840
|
"valueType": "boolean"
|
|
10760
10841
|
}
|
|
10761
10842
|
],
|
|
10762
|
-
"numeric-sensor": [
|
|
10763
|
-
|
|
10764
|
-
|
|
10765
|
-
|
|
10766
|
-
},
|
|
10767
|
-
{
|
|
10768
|
-
"path": "unit",
|
|
10769
|
-
"valueType": "string"
|
|
10770
|
-
},
|
|
10771
|
-
{
|
|
10772
|
-
"path": "precision",
|
|
10773
|
-
"valueType": "number"
|
|
10774
|
-
}
|
|
10775
|
-
],
|
|
10843
|
+
"numeric-sensor": [{
|
|
10844
|
+
"path": "value",
|
|
10845
|
+
"valueType": "number"
|
|
10846
|
+
}],
|
|
10776
10847
|
"pet-feeder": [
|
|
10777
10848
|
{
|
|
10778
10849
|
"path": "foodLevel",
|
|
@@ -10864,14 +10935,6 @@ Object.freeze({
|
|
|
10864
10935
|
{
|
|
10865
10936
|
"path": "powerFactor",
|
|
10866
10937
|
"valueType": "number"
|
|
10867
|
-
},
|
|
10868
|
-
{
|
|
10869
|
-
"path": "unit",
|
|
10870
|
-
"valueType": "string"
|
|
10871
|
-
},
|
|
10872
|
-
{
|
|
10873
|
-
"path": "precision",
|
|
10874
|
-
"valueType": "number"
|
|
10875
10938
|
}
|
|
10876
10939
|
],
|
|
10877
10940
|
"presence": [
|
|
@@ -10900,20 +10963,10 @@ Object.freeze({
|
|
|
10900
10963
|
"valueType": "number"
|
|
10901
10964
|
}
|
|
10902
10965
|
],
|
|
10903
|
-
"pressure-sensor": [
|
|
10904
|
-
|
|
10905
|
-
|
|
10906
|
-
|
|
10907
|
-
},
|
|
10908
|
-
{
|
|
10909
|
-
"path": "unit",
|
|
10910
|
-
"valueType": "string"
|
|
10911
|
-
},
|
|
10912
|
-
{
|
|
10913
|
-
"path": "precision",
|
|
10914
|
-
"valueType": "number"
|
|
10915
|
-
}
|
|
10916
|
-
],
|
|
10966
|
+
"pressure-sensor": [{
|
|
10967
|
+
"path": "hpa",
|
|
10968
|
+
"valueType": "number"
|
|
10969
|
+
}],
|
|
10917
10970
|
"privacy-mask": [{
|
|
10918
10971
|
"path": "enabled",
|
|
10919
10972
|
"valueType": "boolean"
|
|
@@ -11224,20 +11277,10 @@ Object.freeze({
|
|
|
11224
11277
|
"path": "tampered",
|
|
11225
11278
|
"valueType": "boolean"
|
|
11226
11279
|
}],
|
|
11227
|
-
"temperature-sensor": [
|
|
11228
|
-
|
|
11229
|
-
|
|
11230
|
-
|
|
11231
|
-
},
|
|
11232
|
-
{
|
|
11233
|
-
"path": "unit",
|
|
11234
|
-
"valueType": "string"
|
|
11235
|
-
},
|
|
11236
|
-
{
|
|
11237
|
-
"path": "precision",
|
|
11238
|
-
"valueType": "number"
|
|
11239
|
-
}
|
|
11240
|
-
],
|
|
11280
|
+
"temperature-sensor": [{
|
|
11281
|
+
"path": "celsius",
|
|
11282
|
+
"valueType": "number"
|
|
11283
|
+
}],
|
|
11241
11284
|
"update": [
|
|
11242
11285
|
{
|
|
11243
11286
|
"path": "currentVersion",
|
|
@@ -11348,10 +11391,6 @@ Object.freeze({
|
|
|
11348
11391
|
"path": "temperature",
|
|
11349
11392
|
"valueType": "number"
|
|
11350
11393
|
},
|
|
11351
|
-
{
|
|
11352
|
-
"path": "temperatureUnit",
|
|
11353
|
-
"valueType": "string"
|
|
11354
|
-
},
|
|
11355
11394
|
{
|
|
11356
11395
|
"path": "humidity",
|
|
11357
11396
|
"valueType": "number"
|
|
@@ -11360,18 +11399,10 @@ Object.freeze({
|
|
|
11360
11399
|
"path": "pressure",
|
|
11361
11400
|
"valueType": "number"
|
|
11362
11401
|
},
|
|
11363
|
-
{
|
|
11364
|
-
"path": "pressureUnit",
|
|
11365
|
-
"valueType": "string"
|
|
11366
|
-
},
|
|
11367
11402
|
{
|
|
11368
11403
|
"path": "windSpeed",
|
|
11369
11404
|
"valueType": "number"
|
|
11370
11405
|
},
|
|
11371
|
-
{
|
|
11372
|
-
"path": "windSpeedUnit",
|
|
11373
|
-
"valueType": "string"
|
|
11374
|
-
},
|
|
11375
11406
|
{
|
|
11376
11407
|
"path": "windBearing",
|
|
11377
11408
|
"valueType": "number"
|
|
@@ -11421,7 +11452,7 @@ object({
|
|
|
11421
11452
|
params: record(string(), union([string(), number()])).optional()
|
|
11422
11453
|
});
|
|
11423
11454
|
//#endregion
|
|
11424
|
-
//#region ../types/dist/err-msg-
|
|
11455
|
+
//#region ../types/dist/err-msg-DIorGYeC.mjs
|
|
11425
11456
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
11426
11457
|
DeviceType["Camera"] = "camera";
|
|
11427
11458
|
DeviceType["Hub"] = "hub";
|
|
@@ -11515,8 +11546,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
11515
11546
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
11516
11547
|
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
11517
11548
|
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
11518
|
-
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
11549
|
+
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
11550
|
+
* A plug-built dishwasher carries running-state and power instead. */
|
|
11519
11551
|
DeviceType["Dishwasher"] = "dishwasher";
|
|
11552
|
+
/** Washing machine. A plug-built one is running while its power stays
|
|
11553
|
+
* above a threshold. No program cap — that surface is the dishwasher's. */
|
|
11554
|
+
DeviceType["Washer"] = "washer";
|
|
11555
|
+
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
11556
|
+
DeviceType["Dryer"] = "dryer";
|
|
11520
11557
|
return DeviceType;
|
|
11521
11558
|
}({});
|
|
11522
11559
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -11774,7 +11811,7 @@ function errMsg(err) {
|
|
|
11774
11811
|
return String(err);
|
|
11775
11812
|
}
|
|
11776
11813
|
//#endregion
|
|
11777
|
-
//#region ../types/dist/composition-
|
|
11814
|
+
//#region ../types/dist/composition-Bb8iwHEJ.mjs
|
|
11778
11815
|
/**
|
|
11779
11816
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11780
11817
|
*
|
|
@@ -12089,6 +12126,13 @@ var table = {
|
|
|
12089
12126
|
apply: () => {
|
|
12090
12127
|
throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
|
|
12091
12128
|
}
|
|
12129
|
+
},
|
|
12130
|
+
schmitt: {
|
|
12131
|
+
minArgs: 3,
|
|
12132
|
+
maxArgs: 5,
|
|
12133
|
+
apply: () => {
|
|
12134
|
+
throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
|
|
12135
|
+
}
|
|
12092
12136
|
}
|
|
12093
12137
|
};
|
|
12094
12138
|
Object.freeze(Object.assign(Object.create(null), table));
|
|
@@ -12529,15 +12573,232 @@ function validateExpressionSource(src) {
|
|
|
12529
12573
|
}
|
|
12530
12574
|
return null;
|
|
12531
12575
|
}
|
|
12532
|
-
/**
|
|
12533
|
-
|
|
12534
|
-
|
|
12535
|
-
|
|
12576
|
+
/**
|
|
12577
|
+
* One power reading becomes a washer, dryer or dishwasher (D944 helper).
|
|
12578
|
+
*
|
|
12579
|
+
* Standby of the control board is under 5 W; a motor or a heater is far
|
|
12580
|
+
* above that, so 5 W is the line. A door lock or a short pump spike is not
|
|
12581
|
+
* a cycle, so running starts only after 30 s above the line.
|
|
12582
|
+
*
|
|
12583
|
+
* A wash, dry or dishwasher pause (soak, heat-wait, heater off between
|
|
12584
|
+
* bursts) lasts several minutes. Ending the cycle on the first dip would
|
|
12585
|
+
* report "finished" in the middle of the program, so the stop dwell is
|
|
12586
|
+
* 10 minutes on every type. The number is stored per type so one machine
|
|
12587
|
+
* can move later without moving the others.
|
|
12588
|
+
*/
|
|
12589
|
+
var APPLIANCE_TEMPLATE_ID = "appliance";
|
|
12590
|
+
var APPLIANCE_KINDS = [
|
|
12591
|
+
"washer",
|
|
12592
|
+
"dryer",
|
|
12593
|
+
"dishwasher"
|
|
12594
|
+
];
|
|
12595
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
12596
|
+
/**
|
|
12597
|
+
* Statistics and integrals over a history read (D944).
|
|
12598
|
+
*
|
|
12599
|
+
* Pure: the same points always give the same answer, so a restart recomputes
|
|
12600
|
+
* rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
|
|
12601
|
+
* never the mean of the samples. A hole with no held value is not integrated
|
|
12602
|
+
* as zero. A truncated read is not published.
|
|
12603
|
+
*/
|
|
12604
|
+
var STATISTIC_STATS = [
|
|
12605
|
+
"min",
|
|
12606
|
+
"max",
|
|
12607
|
+
"mean",
|
|
12608
|
+
"sum",
|
|
12609
|
+
"count",
|
|
12610
|
+
"change"
|
|
12611
|
+
];
|
|
12612
|
+
/**
|
|
12613
|
+
* The range a statistics or integral helper asks history for (D944).
|
|
12614
|
+
*
|
|
12615
|
+
* Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
|
|
12616
|
+
* or 25 hours across a DST change. A tier is eligible only when its retention
|
|
12617
|
+
* is strictly longer than the longest that range can be. Forever qualifies.
|
|
12618
|
+
* Equal to a tier's retention is a refusal, not a short answer.
|
|
12619
|
+
*/
|
|
12620
|
+
var MINUTE_MS = 6e4;
|
|
12621
|
+
var HOUR_MS = 60 * MINUTE_MS;
|
|
12622
|
+
var DAY_MS = 24 * HOUR_MS;
|
|
12623
|
+
var ROLLING_SPANS = [
|
|
12624
|
+
"1h",
|
|
12625
|
+
"24h",
|
|
12626
|
+
"2d",
|
|
12627
|
+
"7d",
|
|
12628
|
+
"30d",
|
|
12629
|
+
"custom"
|
|
12630
|
+
];
|
|
12631
|
+
var CALENDAR_PERIODS = [
|
|
12632
|
+
"today",
|
|
12633
|
+
"yesterday",
|
|
12634
|
+
"this-week",
|
|
12635
|
+
"this-month"
|
|
12636
|
+
];
|
|
12637
|
+
var RESET_PERIODS = [
|
|
12638
|
+
"never",
|
|
12639
|
+
"daily",
|
|
12640
|
+
"weekly",
|
|
12641
|
+
"monthly"
|
|
12642
|
+
];
|
|
12643
|
+
5 * MINUTE_MS;
|
|
12644
|
+
24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
|
|
12645
|
+
7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
|
|
12646
|
+
var rollingWindowObject = object({
|
|
12647
|
+
kind: literal("rolling"),
|
|
12648
|
+
span: _enum(ROLLING_SPANS),
|
|
12649
|
+
customMinutes: number().int().min(5).optional()
|
|
12650
|
+
}).check((payload) => {
|
|
12651
|
+
if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
|
|
12652
|
+
code: "custom",
|
|
12653
|
+
message: "a custom range needs a duration in minutes",
|
|
12654
|
+
path: ["customMinutes"],
|
|
12655
|
+
input: payload.value
|
|
12656
|
+
});
|
|
12657
|
+
if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
|
|
12658
|
+
code: "custom",
|
|
12659
|
+
message: "only a custom range carries a duration",
|
|
12660
|
+
path: ["customMinutes"],
|
|
12661
|
+
input: payload.value
|
|
12662
|
+
});
|
|
12663
|
+
});
|
|
12664
|
+
var RollingWindowSchema = rollingWindowObject;
|
|
12665
|
+
var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
|
|
12666
|
+
kind: literal("calendar"),
|
|
12667
|
+
period: _enum(CALENDAR_PERIODS)
|
|
12668
|
+
})]);
|
|
12669
|
+
/**
|
|
12670
|
+
* A composition source identity: `(addonId, stableId)`, never a numeric device
|
|
12671
|
+
* id. Split out so helper schemas can name it without a cycle through
|
|
12672
|
+
* `composition.ts` (D659).
|
|
12673
|
+
*/
|
|
12536
12674
|
var CompositionSourceRefSchema = object({
|
|
12537
12675
|
/** The owning addon; `stableId` is unique only within it. */
|
|
12538
12676
|
addonId: string().min(1),
|
|
12539
12677
|
stableId: string().min(1)
|
|
12540
12678
|
});
|
|
12679
|
+
var GROUP_TEMPLATE_IDS = [
|
|
12680
|
+
"temperature",
|
|
12681
|
+
"humidity",
|
|
12682
|
+
"pressure",
|
|
12683
|
+
"illuminance",
|
|
12684
|
+
"power",
|
|
12685
|
+
"energy",
|
|
12686
|
+
"battery",
|
|
12687
|
+
"numeric",
|
|
12688
|
+
"motion",
|
|
12689
|
+
"contact",
|
|
12690
|
+
"leak",
|
|
12691
|
+
"smoke",
|
|
12692
|
+
"occupancy",
|
|
12693
|
+
"binary",
|
|
12694
|
+
"switch",
|
|
12695
|
+
"light",
|
|
12696
|
+
"fan",
|
|
12697
|
+
"cover",
|
|
12698
|
+
"valve",
|
|
12699
|
+
"lock",
|
|
12700
|
+
"media-player",
|
|
12701
|
+
"climate"
|
|
12702
|
+
];
|
|
12703
|
+
var NUMERIC_AGGREGATES = [
|
|
12704
|
+
"mean",
|
|
12705
|
+
"median",
|
|
12706
|
+
"min",
|
|
12707
|
+
"max",
|
|
12708
|
+
"sum"
|
|
12709
|
+
];
|
|
12710
|
+
var BINARY_AGGREGATES = ["any", "all"];
|
|
12711
|
+
/** How an actuator group decides it is on. Unknown members never count as off. */
|
|
12712
|
+
var ACTIVE_CRITERIA = ["at-least-one", "all"];
|
|
12713
|
+
var AGGREGATE_REDUCERS = [
|
|
12714
|
+
"mean",
|
|
12715
|
+
"median",
|
|
12716
|
+
"min",
|
|
12717
|
+
"max",
|
|
12718
|
+
"sum",
|
|
12719
|
+
"any",
|
|
12720
|
+
"all"
|
|
12721
|
+
];
|
|
12722
|
+
var GroupTemplateIdSchema = _enum(GROUP_TEMPLATE_IDS);
|
|
12723
|
+
var AggregateReducerSchema = _enum(AGGREGATE_REDUCERS);
|
|
12724
|
+
var ActiveCriterionSchema = _enum(ACTIVE_CRITERIA);
|
|
12725
|
+
/** One member. The field is stored so occupancy can name motion or binary. */
|
|
12726
|
+
var HelperMemberSchema = object({
|
|
12727
|
+
source: CompositionSourceRefSchema,
|
|
12728
|
+
cap: string().min(1),
|
|
12729
|
+
fieldPath: string().min(1)
|
|
12730
|
+
});
|
|
12731
|
+
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
12732
|
+
object({
|
|
12733
|
+
kind: literal("group"),
|
|
12734
|
+
templateId: GroupTemplateIdSchema,
|
|
12735
|
+
aggregate: AggregateReducerSchema,
|
|
12736
|
+
members: array(HelperMemberSchema).min(1).max(64),
|
|
12737
|
+
/** Actuator groups only. Readings leave this unset. */
|
|
12738
|
+
active: ActiveCriterionSchema.optional()
|
|
12739
|
+
}),
|
|
12740
|
+
object({
|
|
12741
|
+
kind: literal("appliance"),
|
|
12742
|
+
templateId: literal(APPLIANCE_TEMPLATE_ID),
|
|
12743
|
+
appliance: _enum(APPLIANCE_KINDS),
|
|
12744
|
+
thresholdWatts: number().positive(),
|
|
12745
|
+
startDwellMs: number().int().nonnegative(),
|
|
12746
|
+
stopDwellMs: number().int().nonnegative(),
|
|
12747
|
+
member: HelperMemberSchema
|
|
12748
|
+
}),
|
|
12749
|
+
object({
|
|
12750
|
+
kind: literal("statistic"),
|
|
12751
|
+
stat: _enum(STATISTIC_STATS),
|
|
12752
|
+
window: StatisticWindowSchema,
|
|
12753
|
+
source: HelperMemberSchema
|
|
12754
|
+
}),
|
|
12755
|
+
object({
|
|
12756
|
+
kind: literal("integral"),
|
|
12757
|
+
reset: _enum(RESET_PERIODS),
|
|
12758
|
+
output: _enum(["energy", "meter"]),
|
|
12759
|
+
source: HelperMemberSchema
|
|
12760
|
+
}),
|
|
12761
|
+
object({
|
|
12762
|
+
kind: literal("threshold"),
|
|
12763
|
+
source: HelperMemberSchema,
|
|
12764
|
+
high: number(),
|
|
12765
|
+
low: number(),
|
|
12766
|
+
onForMs: number().int().nonnegative(),
|
|
12767
|
+
offForMs: number().int().nonnegative()
|
|
12768
|
+
}),
|
|
12769
|
+
object({
|
|
12770
|
+
kind: literal("arithmetic"),
|
|
12771
|
+
op: _enum([
|
|
12772
|
+
"sum",
|
|
12773
|
+
"difference",
|
|
12774
|
+
"ratio"
|
|
12775
|
+
]),
|
|
12776
|
+
inputs: array(HelperMemberSchema).min(2).max(64)
|
|
12777
|
+
}),
|
|
12778
|
+
object({
|
|
12779
|
+
kind: literal("expression"),
|
|
12780
|
+
expr: string().min(1),
|
|
12781
|
+
inputs: array(object({
|
|
12782
|
+
name: string().min(1),
|
|
12783
|
+
member: HelperMemberSchema
|
|
12784
|
+
})).min(1).max(64),
|
|
12785
|
+
output: _enum(["number", "boolean"])
|
|
12786
|
+
}),
|
|
12787
|
+
object({
|
|
12788
|
+
kind: literal("derivative"),
|
|
12789
|
+
window: RollingWindowSchema,
|
|
12790
|
+
per: _enum([
|
|
12791
|
+
"second",
|
|
12792
|
+
"minute",
|
|
12793
|
+
"hour"
|
|
12794
|
+
]),
|
|
12795
|
+
source: HelperMemberSchema
|
|
12796
|
+
})
|
|
12797
|
+
]);
|
|
12798
|
+
/** A composed device's stableId is this prefix plus the block id. */
|
|
12799
|
+
var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
|
|
12800
|
+
/** Longest snippet a `code` source may carry (slice 4). */
|
|
12801
|
+
var MAX_COMPOSITION_CODE_LENGTH = 2e4;
|
|
12541
12802
|
/** One field of one capability of one source device. */
|
|
12542
12803
|
var CompositionFieldReadSchema = object({
|
|
12543
12804
|
source: CompositionSourceRefSchema,
|
|
@@ -12581,7 +12842,59 @@ var CompositionCodeSourceSchema = object({
|
|
|
12581
12842
|
var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
12582
12843
|
CompositionFromSourceSchema,
|
|
12583
12844
|
CompositionExpressionSourceSchema,
|
|
12584
|
-
CompositionCodeSourceSchema
|
|
12845
|
+
CompositionCodeSourceSchema,
|
|
12846
|
+
object({
|
|
12847
|
+
kind: literal("aggregate"),
|
|
12848
|
+
reducer: _enum([
|
|
12849
|
+
"mean",
|
|
12850
|
+
"median",
|
|
12851
|
+
"min",
|
|
12852
|
+
"max",
|
|
12853
|
+
"sum",
|
|
12854
|
+
"any",
|
|
12855
|
+
"all"
|
|
12856
|
+
]),
|
|
12857
|
+
reads: array(CompositionFieldReadSchema).min(1).max(64),
|
|
12858
|
+
/**
|
|
12859
|
+
* Set when the reads are an enum state, not a yes/no: the listed states
|
|
12860
|
+
* count as active (a cover is `open`). A state outside the list is inactive;
|
|
12861
|
+
* a read that is not a state at all is left out. Only `any` / `all` use it.
|
|
12862
|
+
*/
|
|
12863
|
+
activeWhen: array(string().min(1)).min(1).optional(),
|
|
12864
|
+
/** `value` is the reading; the others are the member counts. */
|
|
12865
|
+
publish: _enum([
|
|
12866
|
+
"value",
|
|
12867
|
+
"included",
|
|
12868
|
+
"excluded",
|
|
12869
|
+
"total"
|
|
12870
|
+
]),
|
|
12871
|
+
/** What a `value` publish does when nobody is included. Counts ignore this. */
|
|
12872
|
+
empty: _enum([
|
|
12873
|
+
"null",
|
|
12874
|
+
"omit",
|
|
12875
|
+
"offline"
|
|
12876
|
+
])
|
|
12877
|
+
}),
|
|
12878
|
+
object({
|
|
12879
|
+
kind: literal("series"),
|
|
12880
|
+
read: CompositionFieldReadSchema,
|
|
12881
|
+
/** `value` is the statistic or the integral. The others are the gap counts. */
|
|
12882
|
+
publish: _enum([
|
|
12883
|
+
"value",
|
|
12884
|
+
"included",
|
|
12885
|
+
"excluded",
|
|
12886
|
+
"total"
|
|
12887
|
+
]),
|
|
12888
|
+
/**
|
|
12889
|
+
* What an unanswered `value` does. Counts ignore this. `offline` leaves the
|
|
12890
|
+
* device unseeded until history answers, so a restart cannot publish 0.
|
|
12891
|
+
*/
|
|
12892
|
+
empty: _enum([
|
|
12893
|
+
"null",
|
|
12894
|
+
"omit",
|
|
12895
|
+
"offline"
|
|
12896
|
+
])
|
|
12897
|
+
})
|
|
12585
12898
|
]);
|
|
12586
12899
|
var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
|
|
12587
12900
|
kind: literal("forward"),
|
|
@@ -12627,7 +12940,13 @@ var CompositionSchema = object({
|
|
|
12627
12940
|
kind: literal("existing"),
|
|
12628
12941
|
device: CompositionSourceRefSchema
|
|
12629
12942
|
})]),
|
|
12630
|
-
features: array(CompositionFeatureSchema).min(1).max(16)
|
|
12943
|
+
features: array(CompositionFeatureSchema).min(1).max(16),
|
|
12944
|
+
/**
|
|
12945
|
+
* Present when the block is a helper template. Absent on a free
|
|
12946
|
+
* composition (the room example, device 4603). The editor reads this,
|
|
12947
|
+
* never the compiled features.
|
|
12948
|
+
*/
|
|
12949
|
+
helper: HelperSpecSchema.optional()
|
|
12631
12950
|
}).superRefine((composition, ctx) => {
|
|
12632
12951
|
const seen = /* @__PURE__ */ new Set();
|
|
12633
12952
|
composition.features.forEach((feature, index) => {
|
|
@@ -17734,7 +18053,22 @@ var BrokerProviderInfoSchema = object({
|
|
|
17734
18053
|
*/
|
|
17735
18054
|
interactiveConnectPath: string().min(1).optional(),
|
|
17736
18055
|
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
17737
|
-
interactiveDisconnectPath: string().min(1).optional()
|
|
18056
|
+
interactiveDisconnectPath: string().min(1).optional(),
|
|
18057
|
+
/**
|
|
18058
|
+
* The kind signs in through a server-held interactive session instead of
|
|
18059
|
+
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
18060
|
+
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
18061
|
+
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
18062
|
+
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
18063
|
+
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
18064
|
+
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
18065
|
+
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
18066
|
+
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
18067
|
+
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
18068
|
+
* offers it as the default path; the fields the session fills are
|
|
18069
|
+
* marked `filledBySignIn` in the kind's settings schema.
|
|
18070
|
+
*/
|
|
18071
|
+
signIn: object({ basePath: string().min(1) }).optional()
|
|
17738
18072
|
}))
|
|
17739
18073
|
});
|
|
17740
18074
|
var ListInputSchema = object({
|
|
@@ -18835,7 +19169,12 @@ var CompositionProblemSchema = object({
|
|
|
18835
19169
|
"item-field-reserved",
|
|
18836
19170
|
"target-missing",
|
|
18837
19171
|
"target-unreadable",
|
|
18838
|
-
"not-replaceable"
|
|
19172
|
+
"not-replaceable",
|
|
19173
|
+
"native-capability-replace",
|
|
19174
|
+
"mixed-quantity",
|
|
19175
|
+
"helper-mismatch",
|
|
19176
|
+
"member-constraint",
|
|
19177
|
+
"retention"
|
|
18839
19178
|
]),
|
|
18840
19179
|
path: string(),
|
|
18841
19180
|
message: string()
|
|
@@ -18856,7 +19195,8 @@ var CompositionUnavailableModeSchema = _enum([
|
|
|
18856
19195
|
"null",
|
|
18857
19196
|
"offline",
|
|
18858
19197
|
"unknown",
|
|
18859
|
-
"release"
|
|
19198
|
+
"release",
|
|
19199
|
+
"omit"
|
|
18860
19200
|
]);
|
|
18861
19201
|
/**
|
|
18862
19202
|
* `derived` reads a source · `clock` reads only `now`, recomputed when a sibling
|
|
@@ -18893,9 +19233,11 @@ var CompositionFieldPlanSchema = object({
|
|
|
18893
19233
|
reads: array(CompositionFieldReadSchema)
|
|
18894
19234
|
});
|
|
18895
19235
|
/**
|
|
18896
|
-
* What a feature does to its target (D663): `new` builds a capability on a
|
|
18897
|
-
* device; on an existing device it `add`s a capability the device lacks
|
|
18898
|
-
*
|
|
19236
|
+
* What a feature does to its target (D663, D944): `new` builds a capability on a
|
|
19237
|
+
* new device; on an existing device it `add`s a capability the device lacks.
|
|
19238
|
+
* Replacing a capability the device already serves is refused
|
|
19239
|
+
* (`native-capability-replace`), not planned. `replace` remains on the claim
|
|
19240
|
+
* wire for a claim the hub already holds.
|
|
18899
19241
|
*/
|
|
18900
19242
|
var CompositionFeatureModeSchema = _enum([
|
|
18901
19243
|
"new",
|
|
@@ -19066,8 +19408,392 @@ Object.freeze({
|
|
|
19066
19408
|
latchedAt: {
|
|
19067
19409
|
kind: "fixed",
|
|
19068
19410
|
result: ["number", "null"]
|
|
19411
|
+
},
|
|
19412
|
+
schmitt: {
|
|
19413
|
+
kind: "fixed",
|
|
19414
|
+
result: ["boolean", "null"]
|
|
19069
19415
|
}
|
|
19070
19416
|
});
|
|
19417
|
+
/**
|
|
19418
|
+
* One catalog row per base reading a group helper can build (D944 §3).
|
|
19419
|
+
* Data, not a form default written into the block.
|
|
19420
|
+
*/
|
|
19421
|
+
var NOW$2 = {
|
|
19422
|
+
fieldPath: "lastFetchedAt",
|
|
19423
|
+
expr: "now"
|
|
19424
|
+
};
|
|
19425
|
+
var CHANGED = {
|
|
19426
|
+
fieldPath: "lastChangedAt",
|
|
19427
|
+
expr: "now"
|
|
19428
|
+
};
|
|
19429
|
+
function numeric(input) {
|
|
19430
|
+
return {
|
|
19431
|
+
...input,
|
|
19432
|
+
family: "numeric",
|
|
19433
|
+
deviceType: DeviceType.Sensor,
|
|
19434
|
+
memberDeviceType: DeviceType.Sensor,
|
|
19435
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
19436
|
+
activeWhen: void 0,
|
|
19437
|
+
commandCaps: [],
|
|
19438
|
+
baseUnit: baseUnitOf(input.quantity),
|
|
19439
|
+
aggregates: NUMERIC_AGGREGATES,
|
|
19440
|
+
accept: [{
|
|
19441
|
+
cap: input.outputCap,
|
|
19442
|
+
fieldPath: input.outputField
|
|
19443
|
+
}]
|
|
19444
|
+
};
|
|
19445
|
+
}
|
|
19446
|
+
function binary(input) {
|
|
19447
|
+
return {
|
|
19448
|
+
...input,
|
|
19449
|
+
family: "binary",
|
|
19450
|
+
deviceType: DeviceType.Sensor,
|
|
19451
|
+
memberDeviceType: DeviceType.Sensor,
|
|
19452
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
19453
|
+
activeWhen: void 0,
|
|
19454
|
+
commandCaps: [],
|
|
19455
|
+
quantity: "none",
|
|
19456
|
+
baseUnit: void 0,
|
|
19457
|
+
aggregates: BINARY_AGGREGATES,
|
|
19458
|
+
empty: "offline"
|
|
19459
|
+
};
|
|
19460
|
+
}
|
|
19461
|
+
var GROUP_TEMPLATES = [
|
|
19462
|
+
numeric({
|
|
19463
|
+
id: "temperature",
|
|
19464
|
+
summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
|
|
19465
|
+
role: DeviceRole.TemperatureSensor,
|
|
19466
|
+
outputCap: "temperature-sensor",
|
|
19467
|
+
outputField: "celsius",
|
|
19468
|
+
quantity: "temperature",
|
|
19469
|
+
defaultAggregate: "mean",
|
|
19470
|
+
empty: "null",
|
|
19471
|
+
companions: [NOW$2]
|
|
19472
|
+
}),
|
|
19473
|
+
numeric({
|
|
19474
|
+
id: "humidity",
|
|
19475
|
+
summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
19476
|
+
role: DeviceRole.HumiditySensor,
|
|
19477
|
+
outputCap: "humidity-sensor",
|
|
19478
|
+
outputField: "percent",
|
|
19479
|
+
quantity: "humidity",
|
|
19480
|
+
defaultAggregate: "mean",
|
|
19481
|
+
empty: "null",
|
|
19482
|
+
companions: [NOW$2]
|
|
19483
|
+
}),
|
|
19484
|
+
numeric({
|
|
19485
|
+
id: "pressure",
|
|
19486
|
+
summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
|
|
19487
|
+
role: DeviceRole.PressureSensor,
|
|
19488
|
+
outputCap: "pressure-sensor",
|
|
19489
|
+
outputField: "hpa",
|
|
19490
|
+
quantity: "pressure",
|
|
19491
|
+
defaultAggregate: "mean",
|
|
19492
|
+
empty: "null",
|
|
19493
|
+
companions: [NOW$2]
|
|
19494
|
+
}),
|
|
19495
|
+
numeric({
|
|
19496
|
+
id: "illuminance",
|
|
19497
|
+
summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
|
|
19498
|
+
role: DeviceRole.AmbientLightSensor,
|
|
19499
|
+
outputCap: "ambient-light-sensor",
|
|
19500
|
+
outputField: "lux",
|
|
19501
|
+
quantity: "illuminance",
|
|
19502
|
+
defaultAggregate: "mean",
|
|
19503
|
+
empty: "null",
|
|
19504
|
+
companions: [NOW$2]
|
|
19505
|
+
}),
|
|
19506
|
+
numeric({
|
|
19507
|
+
id: "power",
|
|
19508
|
+
summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
|
|
19509
|
+
role: DeviceRole.PowerSensor,
|
|
19510
|
+
outputCap: "power-meter",
|
|
19511
|
+
outputField: "watts",
|
|
19512
|
+
quantity: "power",
|
|
19513
|
+
defaultAggregate: "sum",
|
|
19514
|
+
empty: "omit",
|
|
19515
|
+
companions: [NOW$2]
|
|
19516
|
+
}),
|
|
19517
|
+
numeric({
|
|
19518
|
+
id: "energy",
|
|
19519
|
+
summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
|
|
19520
|
+
role: DeviceRole.EnergySensor,
|
|
19521
|
+
outputCap: "power-meter",
|
|
19522
|
+
outputField: "kwhTotal",
|
|
19523
|
+
quantity: "energy",
|
|
19524
|
+
defaultAggregate: "sum",
|
|
19525
|
+
empty: "omit",
|
|
19526
|
+
companions: [NOW$2]
|
|
19527
|
+
}),
|
|
19528
|
+
numeric({
|
|
19529
|
+
id: "battery",
|
|
19530
|
+
summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
19531
|
+
role: DeviceRole.BatterySensor,
|
|
19532
|
+
outputCap: "battery",
|
|
19533
|
+
outputField: "percentage",
|
|
19534
|
+
quantity: "percentage",
|
|
19535
|
+
defaultAggregate: "mean",
|
|
19536
|
+
empty: "null",
|
|
19537
|
+
companions: [
|
|
19538
|
+
{
|
|
19539
|
+
fieldPath: "charging",
|
|
19540
|
+
expr: "\"unknown\""
|
|
19541
|
+
},
|
|
19542
|
+
{
|
|
19543
|
+
fieldPath: "sleeping",
|
|
19544
|
+
expr: "false"
|
|
19545
|
+
},
|
|
19546
|
+
{
|
|
19547
|
+
fieldPath: "lastUpdated",
|
|
19548
|
+
expr: "now"
|
|
19549
|
+
}
|
|
19550
|
+
]
|
|
19551
|
+
}),
|
|
19552
|
+
numeric({
|
|
19553
|
+
id: "numeric",
|
|
19554
|
+
summary: "A numeric reading in the members’ shared quantity. Mixed quantities are refused. Nobody reporting publishes null, not 0.",
|
|
19555
|
+
role: DeviceRole.NumericSensor,
|
|
19556
|
+
outputCap: "numeric-sensor",
|
|
19557
|
+
outputField: "value",
|
|
19558
|
+
quantity: "device",
|
|
19559
|
+
defaultAggregate: "mean",
|
|
19560
|
+
empty: "null",
|
|
19561
|
+
companions: [NOW$2]
|
|
19562
|
+
}),
|
|
19563
|
+
binary({
|
|
19564
|
+
id: "motion",
|
|
19565
|
+
summary: "Any or all motion. Offline and unknown members are left out, never read as off. Nobody reporting takes the device offline.",
|
|
19566
|
+
role: DeviceRole.MotionSensor,
|
|
19567
|
+
outputCap: "motion",
|
|
19568
|
+
outputField: "detected",
|
|
19569
|
+
defaultAggregate: "any",
|
|
19570
|
+
accept: [{
|
|
19571
|
+
cap: "motion",
|
|
19572
|
+
fieldPath: "detected"
|
|
19573
|
+
}],
|
|
19574
|
+
companions: [{
|
|
19575
|
+
fieldPath: "lastDetectedAt",
|
|
19576
|
+
expr: "null"
|
|
19577
|
+
}, {
|
|
19578
|
+
fieldPath: "autoClearAfterMs",
|
|
19579
|
+
expr: "null"
|
|
19580
|
+
}]
|
|
19581
|
+
}),
|
|
19582
|
+
binary({
|
|
19583
|
+
id: "contact",
|
|
19584
|
+
summary: "Any or all open contacts. Offline and unknown members are left out, never read as closed.",
|
|
19585
|
+
role: DeviceRole.ContactSensor,
|
|
19586
|
+
outputCap: "contact",
|
|
19587
|
+
outputField: "entryOpen",
|
|
19588
|
+
defaultAggregate: "any",
|
|
19589
|
+
accept: [{
|
|
19590
|
+
cap: "contact",
|
|
19591
|
+
fieldPath: "entryOpen"
|
|
19592
|
+
}],
|
|
19593
|
+
companions: [CHANGED]
|
|
19594
|
+
}),
|
|
19595
|
+
binary({
|
|
19596
|
+
id: "leak",
|
|
19597
|
+
summary: "Any or all leak detections. Offline and unknown members are left out, never read as dry.",
|
|
19598
|
+
role: DeviceRole.LeakSensor,
|
|
19599
|
+
outputCap: "flood",
|
|
19600
|
+
outputField: "flooded",
|
|
19601
|
+
defaultAggregate: "any",
|
|
19602
|
+
accept: [{
|
|
19603
|
+
cap: "flood",
|
|
19604
|
+
fieldPath: "flooded"
|
|
19605
|
+
}],
|
|
19606
|
+
companions: [CHANGED]
|
|
19607
|
+
}),
|
|
19608
|
+
binary({
|
|
19609
|
+
id: "smoke",
|
|
19610
|
+
summary: "Any or all smoke detections. Offline and unknown members are left out, never read as clear.",
|
|
19611
|
+
role: DeviceRole.SmokeSensor,
|
|
19612
|
+
outputCap: "smoke",
|
|
19613
|
+
outputField: "detected",
|
|
19614
|
+
defaultAggregate: "any",
|
|
19615
|
+
accept: [{
|
|
19616
|
+
cap: "smoke",
|
|
19617
|
+
fieldPath: "detected"
|
|
19618
|
+
}],
|
|
19619
|
+
companions: [CHANGED]
|
|
19620
|
+
}),
|
|
19621
|
+
binary({
|
|
19622
|
+
id: "occupancy",
|
|
19623
|
+
summary: "Any or all of motion or binary sensors, published as a binary. Presence zones are not aggregated. Nobody reporting is offline, not off.",
|
|
19624
|
+
role: DeviceRole.BinarySensor,
|
|
19625
|
+
outputCap: "binary",
|
|
19626
|
+
outputField: "on",
|
|
19627
|
+
defaultAggregate: "any",
|
|
19628
|
+
accept: [{
|
|
19629
|
+
cap: "motion",
|
|
19630
|
+
fieldPath: "detected"
|
|
19631
|
+
}, {
|
|
19632
|
+
cap: "binary",
|
|
19633
|
+
fieldPath: "on"
|
|
19634
|
+
}],
|
|
19635
|
+
companions: [CHANGED]
|
|
19636
|
+
}),
|
|
19637
|
+
binary({
|
|
19638
|
+
id: "binary",
|
|
19639
|
+
summary: "Any or all generic binary sensors. Offline and unknown members are left out, never read as off.",
|
|
19640
|
+
role: DeviceRole.BinarySensor,
|
|
19641
|
+
outputCap: "binary",
|
|
19642
|
+
outputField: "on",
|
|
19643
|
+
defaultAggregate: "any",
|
|
19644
|
+
accept: [{
|
|
19645
|
+
cap: "binary",
|
|
19646
|
+
fieldPath: "on"
|
|
19647
|
+
}],
|
|
19648
|
+
companions: [CHANGED]
|
|
19649
|
+
}),
|
|
19650
|
+
actuator({
|
|
19651
|
+
id: "switch",
|
|
19652
|
+
summary: "On when at least one switch is on, or when all of them are. Offline and unknown members are left out of that decision, never read as off, and still receive the command.",
|
|
19653
|
+
deviceType: DeviceType.Switch,
|
|
19654
|
+
outputCap: "switch",
|
|
19655
|
+
outputField: "on",
|
|
19656
|
+
member: {
|
|
19657
|
+
cap: "switch",
|
|
19658
|
+
fieldPath: "on"
|
|
19659
|
+
},
|
|
19660
|
+
commandCaps: ["switch"]
|
|
19661
|
+
}),
|
|
19662
|
+
actuator({
|
|
19663
|
+
id: "light",
|
|
19664
|
+
summary: "On when at least one light is on, or when all of them are. Brightness and colour are offered only when every member has them. A command is not sent to a member that lacks it.",
|
|
19665
|
+
deviceType: DeviceType.Light,
|
|
19666
|
+
outputCap: "switch",
|
|
19667
|
+
outputField: "on",
|
|
19668
|
+
member: {
|
|
19669
|
+
cap: "switch",
|
|
19670
|
+
fieldPath: "on"
|
|
19671
|
+
},
|
|
19672
|
+
commandCaps: [
|
|
19673
|
+
"switch",
|
|
19674
|
+
"brightness",
|
|
19675
|
+
"color"
|
|
19676
|
+
]
|
|
19677
|
+
}),
|
|
19678
|
+
actuator({
|
|
19679
|
+
id: "fan",
|
|
19680
|
+
summary: "On when at least one fan is on, or when all of them are. Speed, preset, direction and oscillation are offered only when every fan has them. A command is not sent to a fan that lacks it.",
|
|
19681
|
+
deviceType: DeviceType.Fan,
|
|
19682
|
+
outputCap: "switch",
|
|
19683
|
+
outputField: "on",
|
|
19684
|
+
member: {
|
|
19685
|
+
cap: "switch",
|
|
19686
|
+
fieldPath: "on"
|
|
19687
|
+
},
|
|
19688
|
+
commandCaps: ["switch", "fan-control"]
|
|
19689
|
+
}),
|
|
19690
|
+
actuator({
|
|
19691
|
+
id: "cover",
|
|
19692
|
+
summary: "Open when at least one cover is open or part-open, or only when all of them are. Position and tilt are offered only when every cover has them. A cover that reports nothing is left out, never read as closed.",
|
|
19693
|
+
deviceType: DeviceType.Cover,
|
|
19694
|
+
outputCap: "group-active",
|
|
19695
|
+
outputField: "active",
|
|
19696
|
+
member: {
|
|
19697
|
+
cap: "cover",
|
|
19698
|
+
fieldPath: "state"
|
|
19699
|
+
},
|
|
19700
|
+
activeWhen: [
|
|
19701
|
+
"open",
|
|
19702
|
+
"opening",
|
|
19703
|
+
"stopped"
|
|
19704
|
+
],
|
|
19705
|
+
commandCaps: ["cover"]
|
|
19706
|
+
}),
|
|
19707
|
+
actuator({
|
|
19708
|
+
id: "valve",
|
|
19709
|
+
summary: "Open when at least one valve is open or part-open, or only when all of them are. Position is offered only when every valve has it. A valve that reports nothing is left out, never read as closed.",
|
|
19710
|
+
deviceType: DeviceType.Valve,
|
|
19711
|
+
outputCap: "group-active",
|
|
19712
|
+
outputField: "active",
|
|
19713
|
+
member: {
|
|
19714
|
+
cap: "valve",
|
|
19715
|
+
fieldPath: "state"
|
|
19716
|
+
},
|
|
19717
|
+
activeWhen: [
|
|
19718
|
+
"open",
|
|
19719
|
+
"opening",
|
|
19720
|
+
"stopped"
|
|
19721
|
+
],
|
|
19722
|
+
commandCaps: ["valve"]
|
|
19723
|
+
}),
|
|
19724
|
+
actuator({
|
|
19725
|
+
id: "lock",
|
|
19726
|
+
summary: "Locked when at least one lock is locked, or only when all of them are. A jammed or moving lock is not locked. Locking is one tap; unlocking the group needs a confirmation every time, and nothing is sent without it.",
|
|
19727
|
+
deviceType: DeviceType.Lock,
|
|
19728
|
+
outputCap: "group-active",
|
|
19729
|
+
outputField: "active",
|
|
19730
|
+
member: {
|
|
19731
|
+
cap: "lock-control",
|
|
19732
|
+
fieldPath: "state"
|
|
19733
|
+
},
|
|
19734
|
+
activeWhen: ["locked"],
|
|
19735
|
+
commandCaps: ["lock-control"]
|
|
19736
|
+
}),
|
|
19737
|
+
actuator({
|
|
19738
|
+
id: "media-player",
|
|
19739
|
+
summary: "Playing when at least one player is playing, or only when all of them are. Volume is offered only when every player has it. A player that reports nothing is left out, never read as stopped.",
|
|
19740
|
+
deviceType: DeviceType.MediaPlayer,
|
|
19741
|
+
outputCap: "group-active",
|
|
19742
|
+
outputField: "active",
|
|
19743
|
+
member: {
|
|
19744
|
+
cap: "media-player",
|
|
19745
|
+
fieldPath: "state"
|
|
19746
|
+
},
|
|
19747
|
+
activeWhen: ["playing", "buffering"],
|
|
19748
|
+
commandCaps: ["media-player"]
|
|
19749
|
+
}),
|
|
19750
|
+
actuator({
|
|
19751
|
+
id: "climate",
|
|
19752
|
+
summary: "Running when at least one thermostat or air conditioner is not off, or only when all of them are. Target temperature and fan are offered only when every device has them. A device that reports nothing is left out, never read as off.",
|
|
19753
|
+
deviceType: DeviceType.Thermostat,
|
|
19754
|
+
memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
|
|
19755
|
+
outputCap: "group-active",
|
|
19756
|
+
outputField: "active",
|
|
19757
|
+
member: {
|
|
19758
|
+
cap: "climate-control",
|
|
19759
|
+
fieldPath: "mode"
|
|
19760
|
+
},
|
|
19761
|
+
activeWhen: [
|
|
19762
|
+
"heat",
|
|
19763
|
+
"cool",
|
|
19764
|
+
"auto",
|
|
19765
|
+
"heat_cool",
|
|
19766
|
+
"fan_only",
|
|
19767
|
+
"dry"
|
|
19768
|
+
],
|
|
19769
|
+
commandCaps: ["climate-control"]
|
|
19770
|
+
})
|
|
19771
|
+
];
|
|
19772
|
+
function actuator(input) {
|
|
19773
|
+
return {
|
|
19774
|
+
id: input.id,
|
|
19775
|
+
summary: input.summary,
|
|
19776
|
+
deviceType: input.deviceType,
|
|
19777
|
+
outputCap: input.outputCap,
|
|
19778
|
+
outputField: input.outputField,
|
|
19779
|
+
commandCaps: input.commandCaps,
|
|
19780
|
+
family: "actuator",
|
|
19781
|
+
role: void 0,
|
|
19782
|
+
memberDeviceType: input.deviceType,
|
|
19783
|
+
memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
|
|
19784
|
+
activeWhen: input.activeWhen,
|
|
19785
|
+
quantity: "none",
|
|
19786
|
+
baseUnit: void 0,
|
|
19787
|
+
aggregates: BINARY_AGGREGATES,
|
|
19788
|
+
defaultAggregate: "any",
|
|
19789
|
+
empty: "offline",
|
|
19790
|
+
accept: [input.member],
|
|
19791
|
+
companions: [CHANGED]
|
|
19792
|
+
};
|
|
19793
|
+
}
|
|
19794
|
+
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
19795
|
+
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
19796
|
+
DeviceType.Camera;
|
|
19071
19797
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
19072
19798
|
/**
|
|
19073
19799
|
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
@@ -20894,12 +21620,12 @@ function buildDeviceAdminUrl(parts) {
|
|
|
20894
21620
|
* providers detect renames by matching `uniqueId` and patch `id`
|
|
20895
21621
|
* in-place so dispatch keeps working.
|
|
20896
21622
|
*
|
|
20897
|
-
* **
|
|
20898
|
-
*
|
|
20899
|
-
*
|
|
20900
|
-
*
|
|
20901
|
-
* (`ha-domain-mapping`)
|
|
20902
|
-
* stored after that point.
|
|
21623
|
+
* **Display unit and precision are NOT stored here.** They are a read-time
|
|
21624
|
+
* preference (D944). A polymorphic reading stores its `quantity` here once,
|
|
21625
|
+
* at adopt, and a date/time value stores `dateTimeShape` the same way —
|
|
21626
|
+
* neither is repeated on each sample. The `device_class` tag is an
|
|
21627
|
+
* HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
|
|
21628
|
+
* to derive the CamStack `DeviceRole`; it is never stored after that point.
|
|
20903
21629
|
*
|
|
20904
21630
|
* The `raw` escape hatch carries provider-specific attributes that don't
|
|
20905
21631
|
* fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
|
|
@@ -20913,6 +21639,14 @@ var SourceInfoSchema = object({
|
|
|
20913
21639
|
/** Immutable upstream identifier when available (HA `unique_id`, … ).
|
|
20914
21640
|
* Used to detect rename when `id` changes. */
|
|
20915
21641
|
uniqueId: string().optional(),
|
|
21642
|
+
/** Quantity of a polymorphic reading, written once at adopt (D944). */
|
|
21643
|
+
quantity: ReadingQuantitySchema.optional(),
|
|
21644
|
+
/** Date / time / datetime shape of a string reading, written once at adopt. */
|
|
21645
|
+
dateTimeShape: _enum([
|
|
21646
|
+
"date",
|
|
21647
|
+
"time",
|
|
21648
|
+
"datetime"
|
|
21649
|
+
]).optional(),
|
|
20916
21650
|
/** Provider-specific extras that don't fit the structured slots.
|
|
20917
21651
|
* Whitelist what goes here per provider — do NOT dump entire HA
|
|
20918
21652
|
* attribute blobs (would explode the meta JSON column). */
|
|
@@ -42017,7 +42751,8 @@ method(object({
|
|
|
42017
42751
|
"enum"
|
|
42018
42752
|
]),
|
|
42019
42753
|
enumValues: array(string()).optional(),
|
|
42020
|
-
item: boolean().optional()
|
|
42754
|
+
item: boolean().optional(),
|
|
42755
|
+
quantity: ReadingQuantitySchema.optional()
|
|
42021
42756
|
})).readonly(),
|
|
42022
42757
|
itemArray: object({
|
|
42023
42758
|
path: string(),
|
|
@@ -44378,7 +45113,7 @@ var NetworkAccessStatusSchema = object({
|
|
|
44378
45113
|
});
|
|
44379
45114
|
/**
|
|
44380
45115
|
* Optional, richer endpoint shape returned by providers that expose
|
|
44381
|
-
* MORE than one ingress concurrently (Tailscale
|
|
45116
|
+
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
44382
45117
|
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
44383
45118
|
* the originating provider config (mode + sourcePort) so the
|
|
44384
45119
|
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
@@ -45861,8 +46596,8 @@ var MaskPolygonShapeSchema = object({
|
|
|
45861
46596
|
/** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
|
|
45862
46597
|
var MaskGridShapeSchema = object({
|
|
45863
46598
|
kind: literal("grid"),
|
|
45864
|
-
gridWidth: number(),
|
|
45865
|
-
gridHeight: number(),
|
|
46599
|
+
gridWidth: number().meta(readingMeta("none")),
|
|
46600
|
+
gridHeight: number().meta(readingMeta("none")),
|
|
45866
46601
|
cells: array(boolean())
|
|
45867
46602
|
});
|
|
45868
46603
|
discriminatedUnion("kind", [
|
|
@@ -45974,6 +46709,8 @@ var NcSystemEventKindSchema = _enum([
|
|
|
45974
46709
|
"detection-blind",
|
|
45975
46710
|
"motion-noisy",
|
|
45976
46711
|
"motion-calm",
|
|
46712
|
+
"package-llm-silent",
|
|
46713
|
+
"package-llm-answered",
|
|
45977
46714
|
"recording-stalled",
|
|
45978
46715
|
"recording-healthy",
|
|
45979
46716
|
"recording-degraded",
|
|
@@ -47063,7 +47800,20 @@ var NcRuleInputSchema = object({
|
|
|
47063
47800
|
* save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
|
|
47064
47801
|
* path does not run Zod defaults.
|
|
47065
47802
|
*/
|
|
47066
|
-
groupingSec: number().int().min(5).max(60).optional()
|
|
47803
|
+
groupingSec: number().int().min(5).max(60).optional(),
|
|
47804
|
+
/**
|
|
47805
|
+
* ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
|
|
47806
|
+
* carries "Correct" / "Wrong" buttons (and, where the channel has room, a
|
|
47807
|
+
* "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
|
|
47808
|
+
* same mark the Library writes, and records whose notification it was and
|
|
47809
|
+
* when. A channel with no buttons gets the same two choices as links.
|
|
47810
|
+
*
|
|
47811
|
+
* `.optional()`, never `.default()` (a Zod default does not run on the addon
|
|
47812
|
+
* cap path): absent and `false` both mean off, which is what every rule
|
|
47813
|
+
* authored before this field meant. An editor turns it off by writing
|
|
47814
|
+
* `false`, so it needs no `clear` key.
|
|
47815
|
+
*/
|
|
47816
|
+
askVerdict: boolean().optional()
|
|
47067
47817
|
});
|
|
47068
47818
|
/**
|
|
47069
47819
|
* Partial patch for `updateRule` — any subset of the input fields, plus the
|
|
@@ -57065,6 +57815,28 @@ var PackageVersionInfoSchema = object({
|
|
|
57065
57815
|
deprecated: string().optional(),
|
|
57066
57816
|
distTags: array(string()).readonly()
|
|
57067
57817
|
});
|
|
57818
|
+
/**
|
|
57819
|
+
* One published document. `ok` is markdown from the registry. `missing` means
|
|
57820
|
+
* the registry answered and the file was not there. `error` is a registry or
|
|
57821
|
+
* tarball failure — never an empty string standing in for either.
|
|
57822
|
+
*/
|
|
57823
|
+
var PackageDocBodySchema = discriminatedUnion("status", [
|
|
57824
|
+
object({
|
|
57825
|
+
status: literal("ok"),
|
|
57826
|
+
markdown: string()
|
|
57827
|
+
}),
|
|
57828
|
+
object({ status: literal("missing") }),
|
|
57829
|
+
object({
|
|
57830
|
+
status: literal("error"),
|
|
57831
|
+
message: string()
|
|
57832
|
+
})
|
|
57833
|
+
]);
|
|
57834
|
+
var PackageDocsSchema = object({
|
|
57835
|
+
name: string(),
|
|
57836
|
+
version: string(),
|
|
57837
|
+
readme: PackageDocBodySchema,
|
|
57838
|
+
changelog: PackageDocBodySchema
|
|
57839
|
+
});
|
|
57068
57840
|
var SearchResultSchema = object({
|
|
57069
57841
|
name: string(),
|
|
57070
57842
|
version: string(),
|
|
@@ -57283,7 +58055,10 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
57283
58055
|
}), object({ success: literal(true) }), {
|
|
57284
58056
|
kind: "mutation",
|
|
57285
58057
|
auth: "admin"
|
|
57286
|
-
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
|
|
58058
|
+
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
|
|
58059
|
+
name: string().min(1),
|
|
58060
|
+
version: string().min(1).optional()
|
|
58061
|
+
}), PackageDocsSchema, { auth: "admin" }), method(object({ addonId: string() }), RestartAddonResultSchema, {
|
|
57287
58062
|
kind: "mutation",
|
|
57288
58063
|
auth: "admin"
|
|
57289
58064
|
}), method(object({ packageName: string() }), object({ success: literal(true) }), {
|
|
@@ -57342,27 +58117,17 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
57342
58117
|
*/
|
|
57343
58118
|
var AirQualitySensorStatusSchema = object({
|
|
57344
58119
|
/** Carbon dioxide concentration in ppm. */
|
|
57345
|
-
co2Ppm: number().min(0).optional(),
|
|
58120
|
+
co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
57346
58121
|
/** Total volatile organic compounds in ppb. */
|
|
57347
|
-
vocPpb: number().min(0).optional(),
|
|
58122
|
+
vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
57348
58123
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
57349
|
-
pm25: number().min(0).optional(),
|
|
58124
|
+
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
57350
58125
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
57351
|
-
pm10: number().min(0).optional(),
|
|
58126
|
+
pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
57352
58127
|
/** Composite AQI value (typically 0..500). */
|
|
57353
|
-
aqi: number().optional(),
|
|
58128
|
+
aqi: number().optional().meta(readingMeta("index")),
|
|
57354
58129
|
/** Ms epoch when the slice was last updated. */
|
|
57355
|
-
lastFetchedAt: number()
|
|
57356
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
57357
|
-
* `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
|
|
57358
|
-
* upstream `sensor.*` entity surfaces ONE device_class, so one unit
|
|
57359
|
-
* per slice is unambiguous. */
|
|
57360
|
-
unit: string().optional(),
|
|
57361
|
-
/** Suggested decimal places for numeric display.
|
|
57362
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
57363
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
57364
|
-
* auto-formatting when absent. */
|
|
57365
|
-
precision: number().int().min(0).max(10).optional()
|
|
58130
|
+
lastFetchedAt: number()
|
|
57366
58131
|
});
|
|
57367
58132
|
var airQualitySensorCapability = {
|
|
57368
58133
|
name: "air-quality-sensor",
|
|
@@ -57399,12 +58164,12 @@ var airQualitySensorCapability = {
|
|
|
57399
58164
|
},
|
|
57400
58165
|
pm25: {
|
|
57401
58166
|
kind: "gauge",
|
|
57402
|
-
unit: "
|
|
58167
|
+
unit: "µg/m³",
|
|
57403
58168
|
deadband: 1
|
|
57404
58169
|
},
|
|
57405
58170
|
pm10: {
|
|
57406
58171
|
kind: "gauge",
|
|
57407
|
-
unit: "
|
|
58172
|
+
unit: "µg/m³",
|
|
57408
58173
|
deadband: 1
|
|
57409
58174
|
},
|
|
57410
58175
|
aqi: {
|
|
@@ -57422,22 +58187,13 @@ var airQualitySensorCapability = {
|
|
|
57422
58187
|
* entries with `device_class: illuminance`.
|
|
57423
58188
|
*/
|
|
57424
58189
|
var AmbientLightSensorStatusSchema = object({
|
|
57425
|
-
/** Current illuminance in lux (lx). */
|
|
57426
|
-
lux: number().min(0).meta({
|
|
57427
|
-
|
|
58190
|
+
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
58191
|
+
lux: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
58192
|
+
precision: 0,
|
|
57428
58193
|
label: "Illuminance"
|
|
57429
|
-
}),
|
|
58194
|
+
})),
|
|
57430
58195
|
/** Ms epoch when the slice was last updated. */
|
|
57431
|
-
lastFetchedAt: number()
|
|
57432
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
57433
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
57434
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
57435
|
-
unit: string().optional(),
|
|
57436
|
-
/** Suggested decimal places for numeric display.
|
|
57437
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
57438
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
57439
|
-
* auto-formatting when absent. */
|
|
57440
|
-
precision: number().int().min(0).max(10).optional()
|
|
58196
|
+
lastFetchedAt: number()
|
|
57441
58197
|
});
|
|
57442
58198
|
var ambientLightSensorCapability = {
|
|
57443
58199
|
name: "ambient-light-sensor",
|
|
@@ -57469,12 +58225,7 @@ var ambientLightSensorCapability = {
|
|
|
57469
58225
|
} } },
|
|
57470
58226
|
/** Clock fields: written, but excluded from the compare that decides
|
|
57471
58227
|
* whether persisting is worth a SQLite commit. */
|
|
57472
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
57473
|
-
customize: {
|
|
57474
|
-
version: 1,
|
|
57475
|
-
advanced: "derive",
|
|
57476
|
-
targets: []
|
|
57477
|
-
}
|
|
58228
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
57478
58229
|
};
|
|
57479
58230
|
/**
|
|
57480
58231
|
* Per-class audio metrics aggregated over a sliding window.
|
|
@@ -57701,10 +58452,7 @@ var BatteryStatusSchema = object({
|
|
|
57701
58452
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
57702
58453
|
* declines to band a non-finite reading, which is the correct shape.
|
|
57703
58454
|
*/
|
|
57704
|
-
percentage: number().min(0).max(100).nullable().meta({
|
|
57705
|
-
unit: "%",
|
|
57706
|
-
label: "Charge"
|
|
57707
|
-
}),
|
|
58455
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
57708
58456
|
/**
|
|
57709
58457
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
57710
58458
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -57731,7 +58479,14 @@ var BatteryStatusSchema = object({
|
|
|
57731
58479
|
*/
|
|
57732
58480
|
sleeping: boolean().meta({ label: "Sleeping" }),
|
|
57733
58481
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
57734
|
-
lastUpdated: number(),
|
|
58482
|
+
lastUpdated: number().meta(readingMeta("none")),
|
|
58483
|
+
/**
|
|
58484
|
+
* Battery voltage in volts, for a cell the firmware reports as voltage
|
|
58485
|
+
* rather than a percentage (Ecowitt capacitor packs). Absent on cameras
|
|
58486
|
+
* that only report charge. A second reading on this cap, not a second
|
|
58487
|
+
* `numeric-sensor` on the same device.
|
|
58488
|
+
*/
|
|
58489
|
+
volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
57735
58490
|
/**
|
|
57736
58491
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
57737
58492
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -57778,12 +58533,12 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
57778
58533
|
DeviceType.Switch,
|
|
57779
58534
|
DeviceType.PetFeeder,
|
|
57780
58535
|
DeviceType.Container,
|
|
57781
|
-
DeviceType.Thermostat
|
|
58536
|
+
DeviceType.Thermostat,
|
|
58537
|
+
DeviceType.EventEmitter
|
|
57782
58538
|
];
|
|
57783
58539
|
/** Generic Customize target for the percentage reading (D936). */
|
|
57784
58540
|
var batteryCustomize = {
|
|
57785
58541
|
version: 1,
|
|
57786
|
-
advanced: "derive",
|
|
57787
58542
|
targets: [{
|
|
57788
58543
|
id: "percentage",
|
|
57789
58544
|
cardinality: "scalar",
|
|
@@ -57792,7 +58547,7 @@ var batteryCustomize = {
|
|
|
57792
58547
|
stateField: "percentage"
|
|
57793
58548
|
},
|
|
57794
58549
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
57795
|
-
apply: "add
|
|
58550
|
+
apply: "add",
|
|
57796
58551
|
path: "percentage",
|
|
57797
58552
|
sources: [{
|
|
57798
58553
|
kind: "field",
|
|
@@ -57913,10 +58668,16 @@ onStatusChanged: { data: object({
|
|
|
57913
58668
|
/**
|
|
57914
58669
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
57915
58670
|
*/
|
|
57916
|
-
history: { fields: {
|
|
57917
|
-
|
|
57918
|
-
|
|
57919
|
-
|
|
58671
|
+
history: { fields: {
|
|
58672
|
+
percentage: {
|
|
58673
|
+
kind: "gauge",
|
|
58674
|
+
unit: "%"
|
|
58675
|
+
},
|
|
58676
|
+
volts: {
|
|
58677
|
+
kind: "gauge",
|
|
58678
|
+
unit: "V"
|
|
58679
|
+
}
|
|
58680
|
+
} },
|
|
57920
58681
|
customize: batteryCustomize,
|
|
57921
58682
|
/** Clock fields: written, but excluded from the compare that decides
|
|
57922
58683
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -57963,12 +58724,7 @@ var binaryCapability = {
|
|
|
57963
58724
|
history: { fields: { on: { kind: "boolean" } } },
|
|
57964
58725
|
/** Clock fields: written, but excluded from the compare that decides
|
|
57965
58726
|
* whether persisting is worth a SQLite commit. */
|
|
57966
|
-
volatileStateFields: ["lastChangedAt"]
|
|
57967
|
-
customize: {
|
|
57968
|
-
version: 1,
|
|
57969
|
-
advanced: "derive",
|
|
57970
|
-
targets: []
|
|
57971
|
-
}
|
|
58727
|
+
volatileStateFields: ["lastChangedAt"]
|
|
57972
58728
|
};
|
|
57973
58729
|
/**
|
|
57974
58730
|
* Dimmable-light brightness control. Co-exists with `switch` on the
|
|
@@ -57984,7 +58740,7 @@ var binaryCapability = {
|
|
|
57984
58740
|
*/
|
|
57985
58741
|
var BrightnessStatusSchema = object({
|
|
57986
58742
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
57987
|
-
percentage: number().min(0).max(100),
|
|
58743
|
+
percentage: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
57988
58744
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
57989
58745
|
lastChangedAt: number()
|
|
57990
58746
|
});
|
|
@@ -58438,21 +59194,21 @@ var ClimateControlStatusSchema = object({
|
|
|
58438
59194
|
/** Single setpoint in Celsius. Used by single-target modes
|
|
58439
59195
|
* (heat / cool / dry). Null when the device is in a range mode
|
|
58440
59196
|
* (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
|
|
58441
|
-
target: number().nullable(),
|
|
59197
|
+
target: number().nullable().meta(readingMeta("temperature")),
|
|
58442
59198
|
/** Upper bound of the dual setpoint in Celsius. Populated only
|
|
58443
59199
|
* in `heat_cool` mode. */
|
|
58444
|
-
targetHigh: number().nullable(),
|
|
59200
|
+
targetHigh: number().nullable().meta(readingMeta("temperature")),
|
|
58445
59201
|
/** Lower bound of the dual setpoint in Celsius. Populated only
|
|
58446
59202
|
* in `heat_cool` mode. */
|
|
58447
|
-
targetLow: number().nullable(),
|
|
59203
|
+
targetLow: number().nullable().meta(readingMeta("temperature")),
|
|
58448
59204
|
/** Target relative humidity (0..100). Null when the device has no
|
|
58449
59205
|
* humidity control. */
|
|
58450
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
59206
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
58451
59207
|
/** Current measured temperature in Celsius. Read-only — pushed by
|
|
58452
59208
|
* the device's internal sensor. */
|
|
58453
|
-
currentTemp: number().nullable(),
|
|
59209
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
58454
59210
|
/** Current measured relative humidity (0..100). Read-only. */
|
|
58455
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
59211
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
58456
59212
|
/** Vertical louver swing on/off. Null when the device has no vertical
|
|
58457
59213
|
* swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
|
|
58458
59214
|
swingVertical: boolean().nullable(),
|
|
@@ -58460,11 +59216,11 @@ var ClimateControlStatusSchema = object({
|
|
|
58460
59216
|
* horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
|
|
58461
59217
|
swingHorizontal: boolean().nullable(),
|
|
58462
59218
|
/** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
58463
|
-
minTemp: number().nullable().optional(),
|
|
59219
|
+
minTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
58464
59220
|
/** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
58465
|
-
maxTemp: number().nullable().optional(),
|
|
59221
|
+
maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
58466
59222
|
/** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
|
|
58467
|
-
tempStep: number().positive().nullable().optional(),
|
|
59223
|
+
tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
|
|
58468
59224
|
/** Ms epoch when the slice was last updated (push or command). */
|
|
58469
59225
|
lastFetchedAt: number()
|
|
58470
59226
|
});
|
|
@@ -58606,17 +59362,17 @@ var climateControlCapability = {
|
|
|
58606
59362
|
* modes to expose in the picker without a slice fetch.
|
|
58607
59363
|
*/
|
|
58608
59364
|
var RgbTripletSchema = object({
|
|
58609
|
-
r: number().int().min(0).max(255),
|
|
58610
|
-
g: number().int().min(0).max(255),
|
|
58611
|
-
b: number().int().min(0).max(255)
|
|
59365
|
+
r: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
59366
|
+
g: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
59367
|
+
b: number().int().min(0).max(255).meta(readingMeta("none"))
|
|
58612
59368
|
});
|
|
58613
59369
|
var HsvTripletSchema = object({
|
|
58614
59370
|
/** Hue in degrees, 0..360. */
|
|
58615
|
-
h: number().min(0).max(360),
|
|
59371
|
+
h: number().min(0).max(360).meta(readingMeta("none")),
|
|
58616
59372
|
/** Saturation as 0..100 inclusive. */
|
|
58617
|
-
s: number().min(0).max(100),
|
|
59373
|
+
s: number().min(0).max(100).meta(readingMeta("none")),
|
|
58618
59374
|
/** Value/brightness as 0..100 inclusive. */
|
|
58619
|
-
v: number().min(0).max(100)
|
|
59375
|
+
v: number().min(0).max(100).meta(readingMeta("none"))
|
|
58620
59376
|
});
|
|
58621
59377
|
var ColorInputSchema = discriminatedUnion("mode", [
|
|
58622
59378
|
object({
|
|
@@ -58645,7 +59401,7 @@ var ColorStatusSchema = object({
|
|
|
58645
59401
|
/** Populated when `mode === 'hsv'`. */
|
|
58646
59402
|
hsv: HsvTripletSchema.optional(),
|
|
58647
59403
|
/** Populated when `mode === 'mired'`. */
|
|
58648
|
-
mireds: number().int().optional(),
|
|
59404
|
+
mireds: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
58649
59405
|
/** Ms epoch of the last operator-driven change. */
|
|
58650
59406
|
lastChangedAt: number()
|
|
58651
59407
|
});
|
|
@@ -58840,18 +59596,12 @@ _enum([
|
|
|
58840
59596
|
"empty",
|
|
58841
59597
|
"full"
|
|
58842
59598
|
]);
|
|
58843
|
-
/**
|
|
58844
|
-
|
|
58845
|
-
|
|
58846
|
-
"days",
|
|
58847
|
-
"hours",
|
|
58848
|
-
"cycles"
|
|
58849
|
-
]);
|
|
58850
|
-
/** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
|
|
58851
|
-
* days). `value` null = unknown (D393). */
|
|
59599
|
+
/** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
|
|
59600
|
+
var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
|
|
59601
|
+
/** Remaining life. `value` null = unknown (D393). Duration is seconds. */
|
|
58852
59602
|
var ConsumableRemainingSchema = object({
|
|
58853
|
-
value: number().min(0).nullable(),
|
|
58854
|
-
|
|
59603
|
+
value: number().min(0).nullable().meta(readingMeta("device")),
|
|
59604
|
+
kind: ConsumableRemainingKindSchema
|
|
58855
59605
|
});
|
|
58856
59606
|
/** A single consumable item. Either a continuous `level` (remaining
|
|
58857
59607
|
* life %, or fill % for a `supply` / `waste` item) or a discrete `status`
|
|
@@ -58864,7 +59614,7 @@ var ConsumableItemSchema = object({
|
|
|
58864
59614
|
/** Display name. */
|
|
58865
59615
|
label: string().min(1),
|
|
58866
59616
|
/** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
|
|
58867
|
-
level: number().min(0).max(100).nullable(),
|
|
59617
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58868
59618
|
/** The provider's discrete verdict when known (binary mode). */
|
|
58869
59619
|
status: ConsumableStatusSchema.nullable(),
|
|
58870
59620
|
/** What the item is — decides whether low or full is bad. Absent = `wear`. */
|
|
@@ -58946,8 +59696,8 @@ var consumablesCustomize = {
|
|
|
58946
59696
|
transforms: [{ kind: "identity" }]
|
|
58947
59697
|
}],
|
|
58948
59698
|
literals: [{
|
|
58949
|
-
path: "remaining.
|
|
58950
|
-
values: [...
|
|
59699
|
+
path: "remaining.kind",
|
|
59700
|
+
values: [...ConsumableRemainingKindSchema.options]
|
|
58951
59701
|
}]
|
|
58952
59702
|
},
|
|
58953
59703
|
{
|
|
@@ -59076,7 +59826,6 @@ reset: method(object({
|
|
|
59076
59826
|
},
|
|
59077
59827
|
"remaining.value": {
|
|
59078
59828
|
kind: "gauge",
|
|
59079
|
-
unitFrom: "remaining.unit",
|
|
59080
59829
|
deadband: 1,
|
|
59081
59830
|
displayName: "Remaining"
|
|
59082
59831
|
}
|
|
@@ -59146,10 +59895,9 @@ var contactCapability = {
|
|
|
59146
59895
|
* (empty array for the other kinds). It lives in the slice because
|
|
59147
59896
|
* HA `select.*` entities may update their options at runtime.
|
|
59148
59897
|
*
|
|
59149
|
-
*
|
|
59150
|
-
*
|
|
59151
|
-
*
|
|
59152
|
-
* push — consistent with `options` and the sensor-unit approach.
|
|
59898
|
+
* Numeric `min` / `max` / `step` live in the slice and share the device
|
|
59899
|
+
* quantity (`SourceInfo.quantity`, D944). Display precision is a read-time
|
|
59900
|
+
* preference. A datetime control's shape is `SourceInfo.dateTimeShape`.
|
|
59153
59901
|
*/
|
|
59154
59902
|
var ControlStatusSchema = object({
|
|
59155
59903
|
kind: _enum([
|
|
@@ -59161,39 +59909,17 @@ var ControlStatusSchema = object({
|
|
|
59161
59909
|
/** Current value. Numeric kind → number; select/text/datetime → string.
|
|
59162
59910
|
* Datetime values are ISO 8601 strings (full timestamp, date-only,
|
|
59163
59911
|
* or time-only — HA accepts all three forms). */
|
|
59164
|
-
value: union([number(), string()]),
|
|
59912
|
+
value: union([number().meta(readingMeta("device")), string()]),
|
|
59165
59913
|
/** Acceptable values when `kind === 'select'`. Empty for other kinds. */
|
|
59166
59914
|
options: array(string()),
|
|
59167
59915
|
/** Ms epoch when the slice was last updated. */
|
|
59168
59916
|
lastChangedAt: number(),
|
|
59169
|
-
/**
|
|
59170
|
-
|
|
59171
|
-
|
|
59172
|
-
|
|
59173
|
-
/**
|
|
59174
|
-
|
|
59175
|
-
min: number().optional(),
|
|
59176
|
-
/** Maximum allowed value. Populated live from HA `attributes.max`.
|
|
59177
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
59178
|
-
max: number().optional(),
|
|
59179
|
-
/** Step increment. Populated live from HA `attributes.step`.
|
|
59180
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
59181
|
-
step: number().optional(),
|
|
59182
|
-
/** Suggested decimal places for numeric display. Populated live from HA
|
|
59183
|
-
* `attributes.suggested_display_precision`, or derived from `step` when the
|
|
59184
|
-
* HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
|
|
59185
|
-
* other kinds. Falls back to auto-formatting when absent. */
|
|
59186
|
-
precision: number().int().min(0).max(10).optional(),
|
|
59187
|
-
/** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
|
|
59188
|
-
* 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
|
|
59189
|
-
* (UI defaults to 'datetime'). Derived live from the device's HA domain
|
|
59190
|
-
* (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
|
|
59191
|
-
* `has_date`/`has_time` attributes on each push. */
|
|
59192
|
-
format: _enum([
|
|
59193
|
-
"date",
|
|
59194
|
-
"time",
|
|
59195
|
-
"datetime"
|
|
59196
|
-
]).optional()
|
|
59917
|
+
/** Minimum allowed value, in the device quantity's base unit. */
|
|
59918
|
+
min: number().optional().meta(readingMeta("device")),
|
|
59919
|
+
/** Maximum allowed value, in the device quantity's base unit. */
|
|
59920
|
+
max: number().optional().meta(readingMeta("device")),
|
|
59921
|
+
/** Step increment, in the device quantity's base unit. */
|
|
59922
|
+
step: number().optional().meta(readingMeta("device"))
|
|
59197
59923
|
});
|
|
59198
59924
|
var ControlSetValueInputSchema = discriminatedUnion("kind", [
|
|
59199
59925
|
object({
|
|
@@ -59259,9 +59985,9 @@ var CoverStatusSchema = object({
|
|
|
59259
59985
|
]),
|
|
59260
59986
|
/** 0 = fully closed, 100 = fully open. Null when the device has
|
|
59261
59987
|
* no intermediate position surface. */
|
|
59262
|
-
position: number().min(0).max(100).nullable(),
|
|
59988
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59263
59989
|
/** 0 = closed slats, 100 = open slats. Null when no tilt support. */
|
|
59264
|
-
tiltPosition: number().min(0).max(100).nullable(),
|
|
59990
|
+
tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59265
59991
|
/** Ms epoch when the slice was last updated. */
|
|
59266
59992
|
lastChangedAt: number()
|
|
59267
59993
|
});
|
|
@@ -59364,9 +60090,9 @@ var NormalizedRangeSchema$1 = object({
|
|
|
59364
60090
|
var DayNightStatusSchema = object({
|
|
59365
60091
|
mode: DayNightModeSchema,
|
|
59366
60092
|
/** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
|
|
59367
|
-
sensitivity: number().optional(),
|
|
60093
|
+
sensitivity: number().optional().meta(readingMeta("none")),
|
|
59368
60094
|
/** Delay before the IR-cut filter flips, in seconds. */
|
|
59369
|
-
switchDelaySec: number().optional(),
|
|
60095
|
+
switchDelaySec: number().optional().meta(readingMeta("duration")),
|
|
59370
60096
|
lastFetchedAt: number()
|
|
59371
60097
|
});
|
|
59372
60098
|
/**
|
|
@@ -59584,11 +60310,11 @@ var DishwasherStatusSchema = object({
|
|
|
59584
60310
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
59585
60311
|
availablePrograms: array(string()),
|
|
59586
60312
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
59587
|
-
remainingSeconds: number().int().nonnegative().nullable(),
|
|
60313
|
+
remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
59588
60314
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
59589
|
-
delayedStartSeconds: number().int().nonnegative().nullable(),
|
|
60315
|
+
delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
59590
60316
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
59591
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
60317
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59592
60318
|
/** True when the door is reported open; null when the door state is not known. */
|
|
59593
60319
|
doorOpen: boolean().nullable(),
|
|
59594
60320
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -59612,7 +60338,7 @@ var DishwasherStatusSchema = object({
|
|
|
59612
60338
|
/** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
|
|
59613
60339
|
machineCareDue: boolean().nullable(),
|
|
59614
60340
|
/** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
|
|
59615
|
-
machineCareRemainingRuns: number().int().nonnegative().nullable(),
|
|
60341
|
+
machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
|
|
59616
60342
|
/** Option states: null when the appliance does not expose the option or has not said. */
|
|
59617
60343
|
halfLoad: boolean().nullable(),
|
|
59618
60344
|
hygienePlus: boolean().nullable(),
|
|
@@ -59627,7 +60353,7 @@ var DishwasherStatusSchema = object({
|
|
|
59627
60353
|
/** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
|
|
59628
60354
|
activeEvents: array(string()),
|
|
59629
60355
|
/** Numeric error code when the appliance reports one; null otherwise. */
|
|
59630
|
-
errorCode: number().nullable(),
|
|
60356
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
59631
60357
|
/** Name of the raised alert-level event while the appliance is in error; null otherwise. */
|
|
59632
60358
|
errorLabel: string().nullable(),
|
|
59633
60359
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
@@ -59858,7 +60584,7 @@ var DoorbellStatusSchema = object({
|
|
|
59858
60584
|
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
59859
60585
|
lastPressedAt: number().nullable(),
|
|
59860
60586
|
/** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
|
|
59861
|
-
pressCountSinceStart: number()
|
|
60587
|
+
pressCountSinceStart: number().meta(readingMeta("count"))
|
|
59862
60588
|
});
|
|
59863
60589
|
var DoorbellPressEventSchema = object({
|
|
59864
60590
|
deviceId: number(),
|
|
@@ -59923,17 +60649,7 @@ onPressed: {
|
|
|
59923
60649
|
history: "none",
|
|
59924
60650
|
customize: doorbellCustomize
|
|
59925
60651
|
};
|
|
59926
|
-
|
|
59927
|
-
* Enum-state sensor — a string value picked from a finite option set.
|
|
59928
|
-
* Drives HA `sensor` entries with `state_class: enum` (HVAC action
|
|
59929
|
-
* states, weather conditions, contact-source channels, mode strings).
|
|
59930
|
-
* The option set is stable across observations; it is persisted in the
|
|
59931
|
-
* device config blob (via the control slice at adoption) and is NOT
|
|
59932
|
-
* stored in `sourceInfo`.
|
|
59933
|
-
*/
|
|
59934
|
-
/** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
|
|
59935
|
-
* enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
|
|
59936
|
-
var EnumSensorDateTimeFormatSchema = _enum([
|
|
60652
|
+
_enum([
|
|
59937
60653
|
"date",
|
|
59938
60654
|
"time",
|
|
59939
60655
|
"datetime"
|
|
@@ -59941,12 +60657,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
|
|
|
59941
60657
|
var EnumSensorStatusSchema = object({
|
|
59942
60658
|
value: string(),
|
|
59943
60659
|
/**
|
|
59944
|
-
* Set
|
|
59945
|
-
* as
|
|
59946
|
-
*
|
|
59947
|
-
* a time-only sensor → `'time'`.
|
|
60660
|
+
* Set when `value` is a timestamp rather than an enum state, so history
|
|
60661
|
+
* does not learn ISO strings as states. The display shape (date / time /
|
|
60662
|
+
* datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
|
|
59948
60663
|
*/
|
|
59949
|
-
|
|
60664
|
+
timestamp: boolean().optional(),
|
|
59950
60665
|
/** Ms epoch when the slice was last updated. */
|
|
59951
60666
|
lastFetchedAt: number()
|
|
59952
60667
|
});
|
|
@@ -59973,11 +60688,11 @@ var enumSensorCapability = {
|
|
|
59973
60688
|
* History (D850): the value as an `enum` series (an appliance's program or
|
|
59974
60689
|
* operation state, an HVAC action). The option set is per device, so the
|
|
59975
60690
|
* value list is LEARNED as values are first seen (bounded). A sensor with a
|
|
59976
|
-
* `
|
|
60691
|
+
* `timestamp` carries a clock, not a state, and is not recorded.
|
|
59977
60692
|
*/
|
|
59978
60693
|
history: { fields: { value: {
|
|
59979
60694
|
kind: "enum",
|
|
59980
|
-
skipWhenSet: "
|
|
60695
|
+
skipWhenSet: "timestamp"
|
|
59981
60696
|
} } },
|
|
59982
60697
|
/** Clock fields: written, but excluded from the compare that decides
|
|
59983
60698
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -59997,13 +60712,13 @@ var EventFireSchema = object({
|
|
|
59997
60712
|
deviceId: number(),
|
|
59998
60713
|
eventType: string(),
|
|
59999
60714
|
data: record(string(), unknown()).nullable(),
|
|
60000
|
-
timestamp: number(),
|
|
60001
|
-
seq: number()
|
|
60715
|
+
timestamp: number().meta(readingMeta("none")),
|
|
60716
|
+
seq: number().meta(readingMeta("count"))
|
|
60002
60717
|
});
|
|
60003
60718
|
var EventEmitterStatusSchema = object({
|
|
60004
60719
|
eventTypes: array(string()),
|
|
60005
60720
|
lastEvent: EventFireSchema.nullable(),
|
|
60006
|
-
eventCountSinceStart: number()
|
|
60721
|
+
eventCountSinceStart: number().meta(readingMeta("count"))
|
|
60007
60722
|
});
|
|
60008
60723
|
var eventEmitterCapability = {
|
|
60009
60724
|
name: "event-emitter",
|
|
@@ -60416,10 +61131,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
60416
61131
|
var FanControlStatusSchema = object({
|
|
60417
61132
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
60418
61133
|
* speed surface (single-speed fan). */
|
|
60419
|
-
percentage: number().min(0).max(100).nullable(),
|
|
61134
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60420
61135
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
60421
61136
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
60422
|
-
percentageStep: number().positive().optional(),
|
|
61137
|
+
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
60423
61138
|
/** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
|
|
60424
61139
|
* — empty string when no preset is active or supported. */
|
|
60425
61140
|
preset: string(),
|
|
@@ -60540,7 +61255,7 @@ var FeatureProbeStatusSchema = object({
|
|
|
60540
61255
|
/** Camera/firmware model string. `null` when the firmware doesn't expose it. */
|
|
60541
61256
|
model: string().nullable(),
|
|
60542
61257
|
/** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
|
|
60543
|
-
channelCount: number().nullable(),
|
|
61258
|
+
channelCount: number().nullable().meta(readingMeta("count")),
|
|
60544
61259
|
/**
|
|
60545
61260
|
* Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
|
|
60546
61261
|
* completes — drivers' `getAccessoryChildren()` should treat zero as
|
|
@@ -60663,9 +61378,151 @@ var gasCapability = {
|
|
|
60663
61378
|
* whether persisting is worth a SQLite commit. */
|
|
60664
61379
|
volatileStateFields: ["lastChangedAt"]
|
|
60665
61380
|
};
|
|
61381
|
+
var GroupCommandsSchema = object({
|
|
61382
|
+
/** One row per member that has been commanded. A later command replaces that member's row. */
|
|
61383
|
+
results: array(object({
|
|
61384
|
+
deviceId: number().int().nonnegative(),
|
|
61385
|
+
cap: string().min(1),
|
|
61386
|
+
method: string().min(1),
|
|
61387
|
+
ok: boolean(),
|
|
61388
|
+
error: string().optional(),
|
|
61389
|
+
/** Ms epoch of the attempt. */
|
|
61390
|
+
at: number()
|
|
61391
|
+
})) });
|
|
61392
|
+
var groupCommandsCapability = {
|
|
61393
|
+
name: "group-commands",
|
|
61394
|
+
scope: "device",
|
|
61395
|
+
deviceNative: true,
|
|
61396
|
+
mode: "singleton",
|
|
61397
|
+
deviceTypes: [
|
|
61398
|
+
DeviceType.Switch,
|
|
61399
|
+
DeviceType.Light,
|
|
61400
|
+
DeviceType.Fan,
|
|
61401
|
+
DeviceType.Cover,
|
|
61402
|
+
DeviceType.Valve,
|
|
61403
|
+
DeviceType.Lock,
|
|
61404
|
+
DeviceType.MediaPlayer,
|
|
61405
|
+
DeviceType.Thermostat,
|
|
61406
|
+
DeviceType.Climate
|
|
61407
|
+
],
|
|
61408
|
+
methods: {},
|
|
61409
|
+
status: {
|
|
61410
|
+
schema: GroupCommandsSchema,
|
|
61411
|
+
kind: "push"
|
|
61412
|
+
},
|
|
61413
|
+
runtimeState: GroupCommandsSchema,
|
|
61414
|
+
/**
|
|
61415
|
+
* Runtime-state durability: **session** — the last attempt. A restored
|
|
61416
|
+
* failure would outlive the command that caused it.
|
|
61417
|
+
*
|
|
61418
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
61419
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
61420
|
+
*/
|
|
61421
|
+
durability: "session",
|
|
61422
|
+
history: "none"
|
|
61423
|
+
};
|
|
61424
|
+
/**
|
|
61425
|
+
* Whether a helper actuator group counts as active (D944): at least one
|
|
61426
|
+
* included member is, or every included member is. What "active" means is the
|
|
61427
|
+
* type's: a cover is open, a lock is locked, a media player is playing, a
|
|
61428
|
+
* climate device is not off. A group of lights, switches and fans reports
|
|
61429
|
+
* `switch.on` instead; this cap is for the types whose own state is not a
|
|
61430
|
+
* yes/no. Unknown members are left out (`group-status` counts them) and an
|
|
61431
|
+
* empty included set takes the device offline, never `active: false`.
|
|
61432
|
+
*/
|
|
61433
|
+
var GroupActiveSchema = object({
|
|
61434
|
+
active: boolean().meta({ label: "Active" }),
|
|
61435
|
+
/** Ms epoch when the slice was last updated. */
|
|
61436
|
+
lastChangedAt: number()
|
|
61437
|
+
});
|
|
61438
|
+
var groupActiveCapability = {
|
|
61439
|
+
name: "group-active",
|
|
61440
|
+
scope: "device",
|
|
61441
|
+
deviceNative: true,
|
|
61442
|
+
mode: "singleton",
|
|
61443
|
+
deviceTypes: [
|
|
61444
|
+
DeviceType.Cover,
|
|
61445
|
+
DeviceType.Valve,
|
|
61446
|
+
DeviceType.Lock,
|
|
61447
|
+
DeviceType.MediaPlayer,
|
|
61448
|
+
DeviceType.Climate,
|
|
61449
|
+
DeviceType.Thermostat
|
|
61450
|
+
],
|
|
61451
|
+
methods: {},
|
|
61452
|
+
status: {
|
|
61453
|
+
schema: GroupActiveSchema,
|
|
61454
|
+
kind: "push"
|
|
61455
|
+
},
|
|
61456
|
+
runtimeState: GroupActiveSchema,
|
|
61457
|
+
/**
|
|
61458
|
+
* Runtime-state durability: **session** — recomputed from the members on the
|
|
61459
|
+
* next source event; a restored value would outlive the members it named.
|
|
61460
|
+
*
|
|
61461
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
61462
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
61463
|
+
*/
|
|
61464
|
+
durability: "session",
|
|
61465
|
+
history: "none"
|
|
61466
|
+
};
|
|
61467
|
+
/**
|
|
61468
|
+
* How many members of a helper group answered (D944). Counts are integers,
|
|
61469
|
+
* not measurements: zero included is a real count, never a stand-in reading.
|
|
61470
|
+
* The reading itself stays on the output capability. History is none — the
|
|
61471
|
+
* series that later statistics read is the output cap's own history block.
|
|
61472
|
+
*/
|
|
61473
|
+
var GroupStatusSchema = object({
|
|
61474
|
+
/** Members whose current value entered the aggregate. */
|
|
61475
|
+
included: number().int().nonnegative().meta(readingMeta("count", {
|
|
61476
|
+
precision: 0,
|
|
61477
|
+
label: "Reporting"
|
|
61478
|
+
})),
|
|
61479
|
+
/** Members left out: offline, unknown, or not a finite reading (D393). */
|
|
61480
|
+
excluded: number().int().nonnegative().meta(readingMeta("count", {
|
|
61481
|
+
precision: 0,
|
|
61482
|
+
label: "Excluded"
|
|
61483
|
+
})),
|
|
61484
|
+
/** `included` + `excluded`. */
|
|
61485
|
+
total: number().int().nonnegative().meta(readingMeta("count", {
|
|
61486
|
+
precision: 0,
|
|
61487
|
+
label: "Members"
|
|
61488
|
+
}))
|
|
61489
|
+
});
|
|
61490
|
+
var groupStatusCapability = {
|
|
61491
|
+
name: "group-status",
|
|
61492
|
+
scope: "device",
|
|
61493
|
+
deviceNative: true,
|
|
61494
|
+
mode: "singleton",
|
|
61495
|
+
deviceTypes: [
|
|
61496
|
+
DeviceType.Sensor,
|
|
61497
|
+
DeviceType.Switch,
|
|
61498
|
+
DeviceType.Light,
|
|
61499
|
+
DeviceType.Fan,
|
|
61500
|
+
DeviceType.Cover,
|
|
61501
|
+
DeviceType.Valve,
|
|
61502
|
+
DeviceType.Lock,
|
|
61503
|
+
DeviceType.MediaPlayer,
|
|
61504
|
+
DeviceType.Thermostat,
|
|
61505
|
+
DeviceType.Climate
|
|
61506
|
+
],
|
|
61507
|
+
methods: {},
|
|
61508
|
+
status: {
|
|
61509
|
+
schema: GroupStatusSchema,
|
|
61510
|
+
kind: "push"
|
|
61511
|
+
},
|
|
61512
|
+
runtimeState: GroupStatusSchema,
|
|
61513
|
+
/**
|
|
61514
|
+
* Runtime-state durability: **session** — recomputed from members on the
|
|
61515
|
+
* next source event; a restored count would outlive the readings it named.
|
|
61516
|
+
*
|
|
61517
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
61518
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
61519
|
+
*/
|
|
61520
|
+
durability: "session",
|
|
61521
|
+
history: "none"
|
|
61522
|
+
};
|
|
60666
61523
|
var HeatingValveStatusSchema = object({
|
|
60667
61524
|
/** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
|
|
60668
|
-
position: number().min(0).max(100).nullable(),
|
|
61525
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60669
61526
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
60670
61527
|
lastFetchedAt: number()
|
|
60671
61528
|
});
|
|
@@ -60708,9 +61565,9 @@ var HumidifierStatusSchema = object({
|
|
|
60708
61565
|
/** Whether the humidifier is currently on. */
|
|
60709
61566
|
on: boolean(),
|
|
60710
61567
|
/** Current measured relative humidity (0..100). Null when not reported. */
|
|
60711
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
61568
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
60712
61569
|
/** Target relative humidity (0..100). Null when no setpoint surface. */
|
|
60713
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
61570
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
60714
61571
|
/** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
|
|
60715
61572
|
* device has no mode surface. */
|
|
60716
61573
|
mode: string().nullable(),
|
|
@@ -60720,9 +61577,9 @@ var HumidifierStatusSchema = object({
|
|
|
60720
61577
|
* `idle` / `off`). Null when the device doesn't report it. */
|
|
60721
61578
|
action: string().nullable(),
|
|
60722
61579
|
/** HA `min_humidity` attribute. Null → UI uses 0. */
|
|
60723
|
-
minHumidity: number().nullable(),
|
|
61580
|
+
minHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
60724
61581
|
/** HA `max_humidity` attribute. Null → UI uses 100. */
|
|
60725
|
-
maxHumidity: number().nullable(),
|
|
61582
|
+
maxHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
60726
61583
|
/** Ms epoch when the slice was last updated. */
|
|
60727
61584
|
lastChangedAt: number()
|
|
60728
61585
|
});
|
|
@@ -60792,22 +61649,13 @@ var humidifierCapability = {
|
|
|
60792
61649
|
* `sourceInfo`.
|
|
60793
61650
|
*/
|
|
60794
61651
|
var HumiditySensorStatusSchema = object({
|
|
60795
|
-
/** Current relative humidity, 0..100. */
|
|
60796
|
-
percent: number().min(0).max(100).meta({
|
|
60797
|
-
|
|
61652
|
+
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
61653
|
+
percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
61654
|
+
precision: 0,
|
|
60798
61655
|
label: "Humidity"
|
|
60799
|
-
}),
|
|
61656
|
+
})),
|
|
60800
61657
|
/** Ms epoch when the slice was last updated. */
|
|
60801
|
-
lastFetchedAt: number()
|
|
60802
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
60803
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
60804
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
60805
|
-
unit: string().optional(),
|
|
60806
|
-
/** Suggested decimal places for numeric display.
|
|
60807
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
60808
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
60809
|
-
* auto-formatting when absent. */
|
|
60810
|
-
precision: number().int().min(0).max(10).optional()
|
|
61658
|
+
lastFetchedAt: number()
|
|
60811
61659
|
});
|
|
60812
61660
|
var humiditySensorCapability = {
|
|
60813
61661
|
name: "humidity-sensor",
|
|
@@ -60838,12 +61686,7 @@ var humiditySensorCapability = {
|
|
|
60838
61686
|
} } },
|
|
60839
61687
|
/** Clock fields: written, but excluded from the compare that decides
|
|
60840
61688
|
* whether persisting is worth a SQLite commit. */
|
|
60841
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
60842
|
-
customize: {
|
|
60843
|
-
version: 1,
|
|
60844
|
-
advanced: "derive",
|
|
60845
|
-
targets: []
|
|
60846
|
-
}
|
|
61689
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
60847
61690
|
};
|
|
60848
61691
|
/**
|
|
60849
61692
|
* Image display cap. Models a single still image exposed by an integration —
|
|
@@ -60864,7 +61707,7 @@ var ImageStatusSchema = object({
|
|
|
60864
61707
|
* entity exposes no `entity_picture` (yet). */
|
|
60865
61708
|
url: string().nullable(),
|
|
60866
61709
|
/** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
|
|
60867
|
-
lastUpdated: number().nullable()
|
|
61710
|
+
lastUpdated: number().nullable().meta(readingMeta("none"))
|
|
60868
61711
|
});
|
|
60869
61712
|
var imageCapability = {
|
|
60870
61713
|
name: "image",
|
|
@@ -60952,19 +61795,19 @@ var NormalizedRangeSchema = object({
|
|
|
60952
61795
|
*/
|
|
60953
61796
|
var ImageSettingsStatusSchema = object({
|
|
60954
61797
|
/** Normalized 0–100. */
|
|
60955
|
-
brightness: number().optional(),
|
|
61798
|
+
brightness: number().optional().meta(readingMeta("none")),
|
|
60956
61799
|
/** Normalized 0–100. */
|
|
60957
|
-
contrast: number().optional(),
|
|
61800
|
+
contrast: number().optional().meta(readingMeta("none")),
|
|
60958
61801
|
/** Normalized 0–100. */
|
|
60959
|
-
saturation: number().optional(),
|
|
61802
|
+
saturation: number().optional().meta(readingMeta("none")),
|
|
60960
61803
|
/** Normalized 0–100. */
|
|
60961
|
-
sharpness: number().optional(),
|
|
61804
|
+
sharpness: number().optional().meta(readingMeta("none")),
|
|
60962
61805
|
mirror: boolean().optional(),
|
|
60963
61806
|
flip: boolean().optional(),
|
|
60964
61807
|
rotate: ImageRotateSchema.optional(),
|
|
60965
61808
|
whiteBalance: WhiteBalanceModeSchema.optional(),
|
|
60966
61809
|
/** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
|
|
60967
|
-
warmth: number().optional(),
|
|
61810
|
+
warmth: number().optional().meta(readingMeta("none")),
|
|
60968
61811
|
exposureMode: ExposureModeSchema.optional(),
|
|
60969
61812
|
backlightMode: BacklightModeSchema.optional(),
|
|
60970
61813
|
lastFetchedAt: number()
|
|
@@ -61243,10 +62086,10 @@ var IntercomAbilitySchema = object({
|
|
|
61243
62086
|
/** Codecs accepted by firmware, in preference order. Driver-authored strings. */
|
|
61244
62087
|
codecs: array(string()),
|
|
61245
62088
|
/** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
|
|
61246
|
-
sampleRate: number(),
|
|
62089
|
+
sampleRate: number().meta(readingMeta("none")),
|
|
61247
62090
|
duplex: _enum(["half", "full"]),
|
|
61248
62091
|
/** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
|
|
61249
|
-
maxBacklogMs: number()
|
|
62092
|
+
maxBacklogMs: number().meta(readingMeta("duration-ms"))
|
|
61250
62093
|
});
|
|
61251
62094
|
var IntercomStatusSchema = object({
|
|
61252
62095
|
/** True while a talk session is open. */
|
|
@@ -61473,13 +62316,13 @@ var LawnMowerControlStatusSchema = object({
|
|
|
61473
62316
|
activity: LawnMowerActivitySchema,
|
|
61474
62317
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
61475
62318
|
* reading. */
|
|
61476
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
62319
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61477
62320
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
61478
62321
|
* task is active / progress is unavailable. */
|
|
61479
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
62322
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61480
62323
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
61481
62324
|
* or null when unknown. */
|
|
61482
|
-
currentCode: number().nullable(),
|
|
62325
|
+
currentCode: number().nullable().meta(readingMeta("none")),
|
|
61483
62326
|
/** Human label for {@link currentCode}, or null when undecodable. */
|
|
61484
62327
|
currentCodeLabel: string().nullable(),
|
|
61485
62328
|
/** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
|
|
@@ -61932,7 +62775,14 @@ var lockControlCapability = {
|
|
|
61932
62775
|
}),
|
|
61933
62776
|
unlock: method(object({
|
|
61934
62777
|
deviceId: number().int().nonnegative(),
|
|
61935
|
-
code: string().min(1).optional()
|
|
62778
|
+
code: string().min(1).optional(),
|
|
62779
|
+
/**
|
|
62780
|
+
* Honoured by a helper lock GROUP only (D944): unlocking several doors
|
|
62781
|
+
* with one call is refused unless the caller says it was confirmed. A
|
|
62782
|
+
* single lock ignores it. The refusal lives in the callee, so no
|
|
62783
|
+
* automation or API caller reaches the doors by skipping a dialog.
|
|
62784
|
+
*/
|
|
62785
|
+
confirm: boolean().optional()
|
|
61936
62786
|
}), _void(), {
|
|
61937
62787
|
kind: "mutation",
|
|
61938
62788
|
auth: "protected"
|
|
@@ -61969,6 +62819,44 @@ var lockControlCapability = {
|
|
|
61969
62819
|
volatileStateFields: ["lastChangedAt"]
|
|
61970
62820
|
};
|
|
61971
62821
|
/**
|
|
62822
|
+
* A monotonic total between resets (D944). Energy uses `power-meter.kwhTotal`
|
|
62823
|
+
* so dashboards keep one series; every other instantaneous reading lands here.
|
|
62824
|
+
* `value` is the time integral in the source quantity's accumulated base
|
|
62825
|
+
* (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
|
|
62826
|
+
*/
|
|
62827
|
+
var MeterStatusSchema = object({
|
|
62828
|
+
value: number().nullable().meta(readingMeta("device", { label: "Total" })),
|
|
62829
|
+
lastFetchedAt: number()
|
|
62830
|
+
});
|
|
62831
|
+
var meterCapability = {
|
|
62832
|
+
name: "meter",
|
|
62833
|
+
scope: "device",
|
|
62834
|
+
deviceNative: true,
|
|
62835
|
+
mode: "singleton",
|
|
62836
|
+
deviceTypes: [DeviceType.Sensor],
|
|
62837
|
+
methods: {},
|
|
62838
|
+
status: {
|
|
62839
|
+
schema: MeterStatusSchema,
|
|
62840
|
+
kind: "push"
|
|
62841
|
+
},
|
|
62842
|
+
runtimeState: MeterStatusSchema,
|
|
62843
|
+
/**
|
|
62844
|
+
* Runtime-state durability: **restored** — the total is a cache of the
|
|
62845
|
+
* history integral. A restart shows it only until the history pass
|
|
62846
|
+
* recomputes the open period; it is not an in-memory buffer.
|
|
62847
|
+
*
|
|
62848
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
62849
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
62850
|
+
*/
|
|
62851
|
+
durability: "restored",
|
|
62852
|
+
history: { fields: { value: {
|
|
62853
|
+
kind: "counter",
|
|
62854
|
+
deadband: .001,
|
|
62855
|
+
minIntervalMs: 6e4
|
|
62856
|
+
} } },
|
|
62857
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
62858
|
+
};
|
|
62859
|
+
/**
|
|
61972
62860
|
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
61973
62861
|
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
61974
62862
|
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
@@ -62012,7 +62900,7 @@ var MediaPlayerStatusSchema = object({
|
|
|
62012
62900
|
state: MediaPlayerStateSchema,
|
|
62013
62901
|
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
62014
62902
|
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
62015
|
-
volumeLevel: number().min(0).max(100).nullable(),
|
|
62903
|
+
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62016
62904
|
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
62017
62905
|
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
62018
62906
|
isMuted: boolean().nullable(),
|
|
@@ -62025,10 +62913,10 @@ var MediaPlayerStatusSchema = object({
|
|
|
62025
62913
|
currentMedia: MediaInfoSchema.nullable(),
|
|
62026
62914
|
/** Current playback position in ms. Null when not seekable or
|
|
62027
62915
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
62028
|
-
positionMs: number().int().nonnegative().nullable(),
|
|
62916
|
+
positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
62029
62917
|
/** Total duration of the current media in ms. Null when unknown
|
|
62030
62918
|
* (live stream) or nothing is playing. */
|
|
62031
|
-
durationMs: number().int().nonnegative().nullable(),
|
|
62919
|
+
durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
62032
62920
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
62033
62921
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
62034
62922
|
shuffle: boolean().nullable(),
|
|
@@ -62337,7 +63225,7 @@ var motionCustomize = {
|
|
|
62337
63225
|
eventName: "onMotionChanged"
|
|
62338
63226
|
},
|
|
62339
63227
|
deviceTypes: [DeviceType.Camera],
|
|
62340
|
-
apply: "
|
|
63228
|
+
apply: "add",
|
|
62341
63229
|
applyAuthority: {
|
|
62342
63230
|
kind: "camera-sensors",
|
|
62343
63231
|
timeoutField: "motionCooldownMs"
|
|
@@ -62409,7 +63297,7 @@ var MotionStatusSchema = object({
|
|
|
62409
63297
|
* it, every provider seeded it at boot, and every session left it
|
|
62410
63298
|
* `detected: true` after its detach.
|
|
62411
63299
|
*/
|
|
62412
|
-
autoClearAfterMs: number().nullable()
|
|
63300
|
+
autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
|
|
62413
63301
|
});
|
|
62414
63302
|
/**
|
|
62415
63303
|
* Payload of `motion.onMotionChanged` event + the corresponding bus
|
|
@@ -62446,12 +63334,18 @@ var motionCapability = {
|
|
|
62446
63334
|
methods: {
|
|
62447
63335
|
/**
|
|
62448
63336
|
* Pull the current motion state synchronously. Convenience shortcut
|
|
62449
|
-
* for consumers that don't need the full status object
|
|
62450
|
-
*
|
|
62451
|
-
* `getStatus` once the auto-injected status surface lands in every
|
|
62452
|
-
* consumer.
|
|
63337
|
+
* for consumers that don't need the full status object. A composed
|
|
63338
|
+
* group answers from its slice (D944): unknown throws, it is not off.
|
|
62453
63339
|
*/
|
|
62454
63340
|
isDetected: method(object({ deviceId: number() }), boolean()) },
|
|
63341
|
+
/**
|
|
63342
|
+
* A composed motion group serves `isDetected` from `detected` (D944).
|
|
63343
|
+
* Without this the cap stays uncomposable (`has-commands`).
|
|
63344
|
+
*/
|
|
63345
|
+
composedMethods: { isDetected: {
|
|
63346
|
+
kind: "slice",
|
|
63347
|
+
field: "detected"
|
|
63348
|
+
} },
|
|
62455
63349
|
events: {
|
|
62456
63350
|
/**
|
|
62457
63351
|
* Fires every time the runner transitions a camera between
|
|
@@ -62591,7 +63485,7 @@ var MotionZoneRegionSchema = object({
|
|
|
62591
63485
|
* the grid region(s). */
|
|
62592
63486
|
var MotionZoneStatusSchema = object({
|
|
62593
63487
|
enabled: boolean(),
|
|
62594
|
-
sensitivity: number(),
|
|
63488
|
+
sensitivity: number().meta(readingMeta("none")),
|
|
62595
63489
|
/** Grid region(s). Today exactly one `grid` shape. */
|
|
62596
63490
|
regions: array(MotionZoneRegionSchema),
|
|
62597
63491
|
lastFetchedAt: number()
|
|
@@ -62961,7 +63855,7 @@ var NavigationStatusSchema = object({
|
|
|
62961
63855
|
following: boolean(),
|
|
62962
63856
|
flash: boolean(),
|
|
62963
63857
|
lightMode: NavigationLightModeSchema,
|
|
62964
|
-
lightLevel: number().min(40).max(100),
|
|
63858
|
+
lightLevel: number().min(40).max(100).meta(readingMeta("none")),
|
|
62965
63859
|
/** Ms epoch when the slice was last updated. */
|
|
62966
63860
|
lastChangedAt: number()
|
|
62967
63861
|
});
|
|
@@ -63095,13 +63989,13 @@ var NetworkLinkStatusSchema = object({
|
|
|
63095
63989
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
63096
63990
|
* SKIP a null rather than coerce it.
|
|
63097
63991
|
*/
|
|
63098
|
-
signalPercent: number().min(0).max(100).nullable(),
|
|
63992
|
+
signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63099
63993
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
63100
|
-
rssiDbm: number().optional(),
|
|
63994
|
+
rssiDbm: number().optional().meta(readingMeta("signal-level")),
|
|
63101
63995
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
63102
63996
|
ssid: string().optional(),
|
|
63103
63997
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
63104
|
-
lastUpdated: number()
|
|
63998
|
+
lastUpdated: number().meta(readingMeta("none"))
|
|
63105
63999
|
});
|
|
63106
64000
|
var networkLinkCapability = {
|
|
63107
64001
|
name: "network-link",
|
|
@@ -63486,7 +64380,7 @@ var NotifierStatusSchema = object({
|
|
|
63486
64380
|
lastError: string().nullable(),
|
|
63487
64381
|
/** Number of deliveries currently buffered server-side (rate-limited
|
|
63488
64382
|
* notifiers may queue). 0 when the provider sends synchronously. */
|
|
63489
|
-
queueDepth: number().int().nonnegative(),
|
|
64383
|
+
queueDepth: number().int().nonnegative().meta(readingMeta("count")),
|
|
63490
64384
|
/** Per-feature capability matrix — authoritative source for the
|
|
63491
64385
|
* compose-form field gating. */
|
|
63492
64386
|
supports: NotifierSupportsSchema
|
|
@@ -63578,11 +64472,9 @@ onSent: { data: object({
|
|
|
63578
64472
|
* cap fits (HA `sensor` whose `device_class` we don't have a typed cap
|
|
63579
64473
|
* for: water flow, distance, weight, frequency, signal strength, …).
|
|
63580
64474
|
*
|
|
63581
|
-
* The
|
|
63582
|
-
*
|
|
63583
|
-
*
|
|
63584
|
-
* The UI reads them directly from the slice — they are the single source
|
|
63585
|
-
* of truth for generic numeric rendering and do NOT live on `sourceInfo`.
|
|
64475
|
+
* The quantity is not on this slice. It is chosen when the device is
|
|
64476
|
+
* adopted and stored once on `SourceInfo.quantity` (D944). `value` is in
|
|
64477
|
+
* that quantity's base unit. `null` is a refused sample (D393).
|
|
63586
64478
|
*
|
|
63587
64479
|
* Prefer the typed alternatives (`temperature-sensor`, `humidity-sensor`,
|
|
63588
64480
|
* `ambient-light-sensor`, `pressure-sensor`, `power-meter`,
|
|
@@ -63591,15 +64483,8 @@ onSent: { data: object({
|
|
|
63591
64483
|
* numeric values land here.
|
|
63592
64484
|
*/
|
|
63593
64485
|
var NumericSensorStatusSchema = object({
|
|
63594
|
-
|
|
63595
|
-
|
|
63596
|
-
* Populated live from the upstream source on each state push.
|
|
63597
|
-
* Absent for unitless sensors. */
|
|
63598
|
-
unit: string().optional(),
|
|
63599
|
-
/** Suggested decimal places for numeric display.
|
|
63600
|
-
* Populated live from the upstream source when provided.
|
|
63601
|
-
* Falls back to auto-formatting when absent. */
|
|
63602
|
-
precision: number().int().min(0).max(10).optional(),
|
|
64486
|
+
/** Reading in the device quantity's base unit. `null` when refused (D393). */
|
|
64487
|
+
value: number().nullable().meta(readingMeta("device")),
|
|
63603
64488
|
/** Ms epoch when the slice was last updated. */
|
|
63604
64489
|
lastFetchedAt: number()
|
|
63605
64490
|
});
|
|
@@ -63623,11 +64508,11 @@ var numericSensorCapability = {
|
|
|
63623
64508
|
*/
|
|
63624
64509
|
durability: "restored",
|
|
63625
64510
|
/**
|
|
63626
|
-
* History (D850): recorded series. The unit is
|
|
64511
|
+
* History (D850): recorded series. The unit is the device quantity's base,
|
|
64512
|
+
* resolved when the series is read — the cap has no single unit.
|
|
63627
64513
|
*/
|
|
63628
64514
|
history: { fields: { value: {
|
|
63629
64515
|
kind: "gauge",
|
|
63630
|
-
unitFrom: "unit",
|
|
63631
64516
|
minIntervalMs: 3e4
|
|
63632
64517
|
} } },
|
|
63633
64518
|
/** Clock fields: written, but excluded from the compare that decides
|
|
@@ -63919,23 +64804,26 @@ var PetFeederStatusSchema = object({
|
|
|
63919
64804
|
/** Food currently in the bowl (grams). Null when the device has not
|
|
63920
64805
|
* reported a reading yet. On dual-hopper models this is the combined
|
|
63921
64806
|
* bowl reading; per-hopper levels live in `food1`/`food2`. */
|
|
63922
|
-
foodLevel: number().nullable(),
|
|
64807
|
+
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
63923
64808
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
63924
64809
|
* single-hopper models. */
|
|
63925
|
-
food1: number().nullable(),
|
|
64810
|
+
food1: number().nullable().meta(readingMeta("none")),
|
|
63926
64811
|
/** Hopper-2 food level (grams) on dual-hopper feeders; null on
|
|
63927
64812
|
* single-hopper models. */
|
|
63928
|
-
food2: number().nullable(),
|
|
64813
|
+
food2: number().nullable().meta(readingMeta("none")),
|
|
63929
64814
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
63930
64815
|
* (`device_class: problem`, on = low). True when the bowl is empty /
|
|
63931
64816
|
* below the feeder's low threshold. */
|
|
63932
64817
|
lowFood: boolean(),
|
|
63933
64818
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
63934
64819
|
* device has no battery reading. */
|
|
63935
|
-
batteryPower: number().min(0).max(100).nullable(),
|
|
63936
|
-
/**
|
|
63937
|
-
*
|
|
63938
|
-
|
|
64820
|
+
batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
64821
|
+
/**
|
|
64822
|
+
* Desiccant life remaining, stored in seconds (duration base, D944).
|
|
64823
|
+
* Providers convert from the days the feeder reports. Null when the
|
|
64824
|
+
* model has no desiccant sensor.
|
|
64825
|
+
*/
|
|
64826
|
+
desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
|
|
63939
64827
|
/** True while a feed is in progress. */
|
|
63940
64828
|
feeding: boolean(),
|
|
63941
64829
|
/** Decoded connectivity / power status (HA petkit device-status enum).
|
|
@@ -63946,7 +64834,7 @@ var PetFeederStatusSchema = object({
|
|
|
63946
64834
|
* with `errorCode` for consumers that want the raw integer. */
|
|
63947
64835
|
error: string().nullable(),
|
|
63948
64836
|
/** Raw device error code (0 / null = no error). */
|
|
63949
|
-
errorCode: number().nullable(),
|
|
64837
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
63950
64838
|
/** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
|
|
63951
64839
|
isDualHopper: boolean(),
|
|
63952
64840
|
/** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
|
|
@@ -63956,7 +64844,7 @@ var PetFeederStatusSchema = object({
|
|
|
63956
64844
|
/** Play a chime when dispensing. */
|
|
63957
64845
|
feedSound: boolean(),
|
|
63958
64846
|
/** Speaker / prompt volume level (device-scaled integer). */
|
|
63959
|
-
volume: number(),
|
|
64847
|
+
volume: number().meta(readingMeta("none")),
|
|
63960
64848
|
/** Ms epoch when the slice was last refreshed from the cloud. */
|
|
63961
64849
|
lastFetchedAt: number()
|
|
63962
64850
|
});
|
|
@@ -64075,7 +64963,7 @@ var petFeederCapability = {
|
|
|
64075
64963
|
},
|
|
64076
64964
|
desiccantLeftDays: {
|
|
64077
64965
|
kind: "gauge",
|
|
64078
|
-
unit: "
|
|
64966
|
+
unit: "s"
|
|
64079
64967
|
}
|
|
64080
64968
|
} }
|
|
64081
64969
|
};
|
|
@@ -64476,43 +65364,88 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
64476
65364
|
*/
|
|
64477
65365
|
var PowerMeterStatusSchema = object({
|
|
64478
65366
|
/** Instantaneous power draw in watts. */
|
|
64479
|
-
watts: number().optional().meta({
|
|
64480
|
-
unit: "W",
|
|
64481
|
-
label: "Power"
|
|
64482
|
-
}),
|
|
65367
|
+
watts: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
64483
65368
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
64484
|
-
kwhTotal: number().optional().meta({
|
|
64485
|
-
unit: "kWh",
|
|
64486
|
-
label: "Energy"
|
|
64487
|
-
}),
|
|
65369
|
+
kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
64488
65370
|
/** Voltage in volts. */
|
|
64489
|
-
volts: number().optional(),
|
|
65371
|
+
volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
64490
65372
|
/** Current in amperes. */
|
|
64491
|
-
amps: number().optional(),
|
|
64492
|
-
/** Apparent power in volt-amperes
|
|
64493
|
-
va: number().optional(),
|
|
65373
|
+
amps: number().optional().meta(readingMeta("current", { label: "Current" })),
|
|
65374
|
+
/** Apparent power in volt-amperes. */
|
|
65375
|
+
va: number().optional().meta(readingMeta("apparent-power")),
|
|
64494
65376
|
/** Power factor (0..1) if reported. */
|
|
64495
|
-
powerFactor: number().min(0).max(1).optional(),
|
|
65377
|
+
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
64496
65378
|
/** Ms epoch when the slice was last updated. */
|
|
64497
|
-
lastFetchedAt: number()
|
|
64498
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
64499
|
-
* `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
|
|
64500
|
-
* Each upstream `sensor.*` entity surfaces ONE device_class, so one
|
|
64501
|
-
* unit per slice is unambiguous. The UI prefers this over the role's
|
|
64502
|
-
* canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
|
|
64503
|
-
unit: string().optional(),
|
|
64504
|
-
/** Suggested decimal places for numeric display.
|
|
64505
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
64506
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
64507
|
-
* auto-formatting when absent. */
|
|
64508
|
-
precision: number().int().min(0).max(10).optional()
|
|
65379
|
+
lastFetchedAt: number()
|
|
64509
65380
|
});
|
|
65381
|
+
/**
|
|
65382
|
+
* Loads that draw metered mains power (D951). The reading is a feature of the
|
|
65383
|
+
* device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
|
|
65384
|
+
* vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
|
|
65385
|
+
* meter or a standalone energy monitor IS the device. Cover, lock, valve,
|
|
65386
|
+
* camera and the like are left out: their draw is not what anyone meters.
|
|
65387
|
+
*/
|
|
65388
|
+
var POWER_METER_DEVICE_TYPES = [
|
|
65389
|
+
DeviceType.Sensor,
|
|
65390
|
+
DeviceType.Switch,
|
|
65391
|
+
DeviceType.Light,
|
|
65392
|
+
DeviceType.Fan,
|
|
65393
|
+
DeviceType.Climate,
|
|
65394
|
+
DeviceType.Thermostat,
|
|
65395
|
+
DeviceType.Humidifier,
|
|
65396
|
+
DeviceType.WaterHeater,
|
|
65397
|
+
DeviceType.MediaPlayer,
|
|
65398
|
+
DeviceType.Washer,
|
|
65399
|
+
DeviceType.Dryer,
|
|
65400
|
+
DeviceType.Dishwasher,
|
|
65401
|
+
DeviceType.Vacuum,
|
|
65402
|
+
DeviceType.LawnMower,
|
|
65403
|
+
DeviceType.PetFeeder
|
|
65404
|
+
];
|
|
65405
|
+
/** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
|
|
65406
|
+
var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
|
|
65407
|
+
/** One scalar target per reading; each is sourced from a device of its own (D951). */
|
|
65408
|
+
function powerTarget(id, unit) {
|
|
65409
|
+
return {
|
|
65410
|
+
id,
|
|
65411
|
+
cardinality: "scalar",
|
|
65412
|
+
vocabulary: {
|
|
65413
|
+
cap: "power-meter",
|
|
65414
|
+
stateField: id
|
|
65415
|
+
},
|
|
65416
|
+
deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
|
|
65417
|
+
apply: "add",
|
|
65418
|
+
path: id,
|
|
65419
|
+
sources: [{
|
|
65420
|
+
kind: "numeric",
|
|
65421
|
+
requiredUnit: unit,
|
|
65422
|
+
transforms: [{ kind: "identity" }]
|
|
65423
|
+
}],
|
|
65424
|
+
companions: [{
|
|
65425
|
+
kind: "clock",
|
|
65426
|
+
path: "lastFetchedAt",
|
|
65427
|
+
when: "always"
|
|
65428
|
+
}],
|
|
65429
|
+
sourceReport: { status: "assignment-status" },
|
|
65430
|
+
advancedExpression: true
|
|
65431
|
+
};
|
|
65432
|
+
}
|
|
65433
|
+
/** "Power and energy" extension: watts, energy, voltage and current from other devices (D951). */
|
|
65434
|
+
var powerMeterCustomize = {
|
|
65435
|
+
version: 1,
|
|
65436
|
+
targets: [
|
|
65437
|
+
powerTarget("watts", "W"),
|
|
65438
|
+
powerTarget("kwhTotal", "kWh"),
|
|
65439
|
+
powerTarget("volts", "V"),
|
|
65440
|
+
powerTarget("amps", "A")
|
|
65441
|
+
]
|
|
65442
|
+
};
|
|
64510
65443
|
var powerMeterCapability = {
|
|
64511
65444
|
name: "power-meter",
|
|
64512
65445
|
scope: "device",
|
|
64513
65446
|
deviceNative: true,
|
|
64514
65447
|
mode: "singleton",
|
|
64515
|
-
deviceTypes:
|
|
65448
|
+
deviceTypes: POWER_METER_DEVICE_TYPES,
|
|
64516
65449
|
methods: {},
|
|
64517
65450
|
status: {
|
|
64518
65451
|
schema: PowerMeterStatusSchema,
|
|
@@ -64570,11 +65503,7 @@ var powerMeterCapability = {
|
|
|
64570
65503
|
/** Clock fields: written, but excluded from the compare that decides
|
|
64571
65504
|
* whether persisting is worth a SQLite commit. */
|
|
64572
65505
|
volatileStateFields: ["lastFetchedAt"],
|
|
64573
|
-
customize:
|
|
64574
|
-
version: 1,
|
|
64575
|
-
advanced: "derive",
|
|
64576
|
-
targets: []
|
|
64577
|
-
}
|
|
65506
|
+
customize: powerMeterCustomize
|
|
64578
65507
|
};
|
|
64579
65508
|
/**
|
|
64580
65509
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
@@ -64594,11 +65523,11 @@ var powerMeterCapability = {
|
|
|
64594
65523
|
*/
|
|
64595
65524
|
var GpsLocationSchema = object({
|
|
64596
65525
|
/** Latitude in decimal degrees, -90..90. */
|
|
64597
|
-
latitude: number().min(-90).max(90),
|
|
65526
|
+
latitude: number().min(-90).max(90).meta(readingMeta("angle")),
|
|
64598
65527
|
/** Longitude in decimal degrees, -180..180. */
|
|
64599
|
-
longitude: number().min(-180).max(180),
|
|
65528
|
+
longitude: number().min(-180).max(180).meta(readingMeta("angle")),
|
|
64600
65529
|
/** Reported accuracy in meters (lower = better). */
|
|
64601
|
-
accuracyMeters: number().nonnegative()
|
|
65530
|
+
accuracyMeters: number().nonnegative().meta(readingMeta("length"))
|
|
64602
65531
|
});
|
|
64603
65532
|
var PresenceStatusSchema = object({
|
|
64604
65533
|
/** `home` / `not_home` / any user-defined zone name. */
|
|
@@ -64610,7 +65539,7 @@ var PresenceStatusSchema = object({
|
|
|
64610
65539
|
gps: GpsLocationSchema.nullable(),
|
|
64611
65540
|
/** Optional battery level of the tracking device (0..100). Null
|
|
64612
65541
|
* when not reported. */
|
|
64613
|
-
batteryPercent: number().min(0).max(100).nullable(),
|
|
65542
|
+
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
64614
65543
|
/** Ms epoch when the slice was last updated. */
|
|
64615
65544
|
lastChangedAt: number()
|
|
64616
65545
|
});
|
|
@@ -64660,19 +65589,10 @@ var presenceCapability = {
|
|
|
64660
65589
|
* `sourceInfo`.
|
|
64661
65590
|
*/
|
|
64662
65591
|
var PressureSensorStatusSchema = object({
|
|
64663
|
-
/** Current pressure in hPa. */
|
|
64664
|
-
hpa: number(),
|
|
65592
|
+
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
65593
|
+
hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
64665
65594
|
/** Ms epoch when the slice was last updated. */
|
|
64666
|
-
lastFetchedAt: number()
|
|
64667
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
64668
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
64669
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
64670
|
-
unit: string().optional(),
|
|
64671
|
-
/** Suggested decimal places for numeric display.
|
|
64672
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
64673
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
64674
|
-
* auto-formatting when absent. */
|
|
64675
|
-
precision: number().int().min(0).max(10).optional()
|
|
65595
|
+
lastFetchedAt: number()
|
|
64676
65596
|
});
|
|
64677
65597
|
var pressureSensorCapability = {
|
|
64678
65598
|
name: "pressure-sensor",
|
|
@@ -65086,8 +66006,8 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
|
|
|
65086
66006
|
}), _void(), { kind: "mutation" });
|
|
65087
66007
|
var PtzAutotrackSettingsSchema = object({
|
|
65088
66008
|
targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
|
|
65089
|
-
stopDelaySeconds: number().int().min(0).max(300),
|
|
65090
|
-
disappearDelaySeconds: number().int().min(0).max(300)
|
|
66009
|
+
stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
|
|
66010
|
+
disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
|
|
65091
66011
|
});
|
|
65092
66012
|
/**
|
|
65093
66013
|
* One option the camera advertises as a valid `targetType`. Drivers
|
|
@@ -65241,7 +66161,7 @@ var RunningStateStatusSchema = object({
|
|
|
65241
66161
|
/** null = unknown — never coerced to false (D393). */
|
|
65242
66162
|
running: boolean().nullable(),
|
|
65243
66163
|
/** Epoch ms the current boolean began; null while `running` is null. */
|
|
65244
|
-
since: number().nullable(),
|
|
66164
|
+
since: number().nullable().meta(readingMeta("none")),
|
|
65245
66165
|
source: RunningStateSourceSchema.meta({ label: "Source" }),
|
|
65246
66166
|
phase: string().nullable(),
|
|
65247
66167
|
/** null until a reading has been published (D393). */
|
|
@@ -65256,7 +66176,6 @@ var RUNNING_DEVICE_TYPES = Object.values(DeviceType);
|
|
|
65256
66176
|
/** Generic Customize target for the running boolean (D936). */
|
|
65257
66177
|
var runningCustomize = {
|
|
65258
66178
|
version: 1,
|
|
65259
|
-
advanced: "derive",
|
|
65260
66179
|
targets: [{
|
|
65261
66180
|
id: "running",
|
|
65262
66181
|
cardinality: "scalar",
|
|
@@ -65265,6 +66184,8 @@ var runningCustomize = {
|
|
|
65265
66184
|
stateField: "running"
|
|
65266
66185
|
},
|
|
65267
66186
|
deviceTypes: [
|
|
66187
|
+
DeviceType.Washer,
|
|
66188
|
+
DeviceType.Dryer,
|
|
65268
66189
|
DeviceType.Dishwasher,
|
|
65269
66190
|
DeviceType.Vacuum,
|
|
65270
66191
|
DeviceType.LawnMower,
|
|
@@ -65276,7 +66197,7 @@ var runningCustomize = {
|
|
|
65276
66197
|
DeviceType.Thermostat,
|
|
65277
66198
|
DeviceType.PetFeeder
|
|
65278
66199
|
],
|
|
65279
|
-
apply: "add
|
|
66200
|
+
apply: "add",
|
|
65280
66201
|
path: "running",
|
|
65281
66202
|
sources: [
|
|
65282
66203
|
{
|
|
@@ -66540,9 +67461,9 @@ var RecordTriggerSchema = _enum([
|
|
|
66540
67461
|
*/
|
|
66541
67462
|
var RecordWindowSchema = object({
|
|
66542
67463
|
trigger: RecordTriggerSchema,
|
|
66543
|
-
day: number().int().min(0).max(6),
|
|
66544
|
-
startMinute: number().int().min(0).max(1439),
|
|
66545
|
-
endMinute: number().int().min(1).max(1440)
|
|
67464
|
+
day: number().int().min(0).max(6).meta(readingMeta("none")),
|
|
67465
|
+
startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
|
|
67466
|
+
endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
|
|
66546
67467
|
});
|
|
66547
67468
|
/** Status of one physical volume, as the camera itself describes it. */
|
|
66548
67469
|
var OnboardStorageVolumeSchema = object({
|
|
@@ -66618,10 +67539,10 @@ var RecordingOnboardStatusSchema = object({
|
|
|
66618
67539
|
/** Master "record to the card at all" switch. */
|
|
66619
67540
|
enabled: boolean().nullable(),
|
|
66620
67541
|
overwriteWhenFull: boolean().nullable(),
|
|
66621
|
-
preRecordSec: number().nullable(),
|
|
66622
|
-
postRecordSec: number().nullable(),
|
|
67542
|
+
preRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
67543
|
+
postRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
66623
67544
|
/** Length of one recorded file, in minutes. */
|
|
66624
|
-
segmentMinutes: number().nullable(),
|
|
67545
|
+
segmentMinutes: number().nullable().meta(readingMeta("none")),
|
|
66625
67546
|
/** The primary track's weekly windows, flattened. */
|
|
66626
67547
|
windows: array(RecordWindowSchema),
|
|
66627
67548
|
/**
|
|
@@ -66637,7 +67558,7 @@ var RecordingOnboardStatusSchema = object({
|
|
|
66637
67558
|
* provider reporting a non-zero count also reports the schedule as not
|
|
66638
67559
|
* writable.
|
|
66639
67560
|
*/
|
|
66640
|
-
unreadableWindows: number(),
|
|
67561
|
+
unreadableWindows: number().meta(readingMeta("count")),
|
|
66641
67562
|
/**
|
|
66642
67563
|
* The camera is scheduled to record and has NO usable storage.
|
|
66643
67564
|
*
|
|
@@ -67482,7 +68403,7 @@ var CamStreamDescriptorSchema = object({
|
|
|
67482
68403
|
url: string().optional(),
|
|
67483
68404
|
codec: string().optional(),
|
|
67484
68405
|
resolution: CamStreamResolutionSchema.optional(),
|
|
67485
|
-
fps: number().positive().optional(),
|
|
68406
|
+
fps: number().positive().optional().meta(readingMeta("rate")),
|
|
67486
68407
|
label: string().optional(),
|
|
67487
68408
|
/** Device-level features (e.g. `battery-operated`) — drives broker policy. */
|
|
67488
68409
|
deviceFeatures: array(string()).optional(),
|
|
@@ -67569,18 +68490,18 @@ var StreamProfileSchema = _enum([
|
|
|
67569
68490
|
]);
|
|
67570
68491
|
/** The current encoding config of one stream profile. */
|
|
67571
68492
|
var StreamProfileConfigSchema = object({
|
|
67572
|
-
width: number(),
|
|
67573
|
-
height: number(),
|
|
68493
|
+
width: number().meta(readingMeta("none")),
|
|
68494
|
+
height: number().meta(readingMeta("none")),
|
|
67574
68495
|
codec: _enum(["h264", "h265"]),
|
|
67575
|
-
framerate: number(),
|
|
67576
|
-
bitrate: number(),
|
|
68496
|
+
framerate: number().meta(readingMeta("rate")),
|
|
68497
|
+
bitrate: number().meta(readingMeta("bitrate")),
|
|
67577
68498
|
bitrateMode: _enum(["vbr", "cbr"]).optional(),
|
|
67578
68499
|
encoderProfile: _enum([
|
|
67579
68500
|
"high",
|
|
67580
68501
|
"main",
|
|
67581
68502
|
"baseline"
|
|
67582
68503
|
]).optional(),
|
|
67583
|
-
gop: number().optional(),
|
|
68504
|
+
gop: number().optional().meta(readingMeta("none")),
|
|
67584
68505
|
/**
|
|
67585
68506
|
* Whether THIS profile currently carries an audio track. READ-ONLY here.
|
|
67586
68507
|
*
|
|
@@ -67754,7 +68675,8 @@ var switchCapability = {
|
|
|
67754
68675
|
deviceTypes: [
|
|
67755
68676
|
DeviceType.Switch,
|
|
67756
68677
|
DeviceType.Siren,
|
|
67757
|
-
DeviceType.Light
|
|
68678
|
+
DeviceType.Light,
|
|
68679
|
+
DeviceType.Fan
|
|
67758
68680
|
],
|
|
67759
68681
|
methods: { setState: method(object({
|
|
67760
68682
|
deviceId: number(),
|
|
@@ -67801,12 +68723,7 @@ var switchCapability = {
|
|
|
67801
68723
|
label: "On",
|
|
67802
68724
|
kind: "boolean"
|
|
67803
68725
|
}
|
|
67804
|
-
}] }
|
|
67805
|
-
customize: {
|
|
67806
|
-
version: 1,
|
|
67807
|
-
advanced: "derive",
|
|
67808
|
-
targets: []
|
|
67809
|
-
}
|
|
68726
|
+
}] }
|
|
67810
68727
|
};
|
|
67811
68728
|
/** C0 controls, DEL and the C1 range: none of them belongs in a name. */
|
|
67812
68729
|
function hasControlCharacter(value) {
|
|
@@ -68389,35 +69306,21 @@ var tamperCapability = {
|
|
|
68389
69306
|
* entries with `device_class: temperature` and any future native
|
|
68390
69307
|
* thermometer.
|
|
68391
69308
|
*
|
|
68392
|
-
*
|
|
68393
|
-
*
|
|
68394
|
-
* (climate-control's `currentTemp`, energy analytics) can compose
|
|
68395
|
-
* without per-source unit fixups. The canonical display unit (`°C`) is
|
|
68396
|
-
* a descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
68397
|
-
* `sourceInfo`.
|
|
69309
|
+
* The number is Celsius (D944). Providers convert on ingest. A failed
|
|
69310
|
+
* conversion is `null`, never a guess. Display unit is a read-time preference.
|
|
68398
69311
|
*
|
|
68399
69312
|
* Status `lastFetchedAt` lets staleness-aware consumers detect a
|
|
68400
69313
|
* frozen feed (provider hung) distinct from a "temperature hasn't
|
|
68401
69314
|
* changed" steady state.
|
|
68402
69315
|
*/
|
|
68403
69316
|
var TemperatureSensorStatusSchema = object({
|
|
68404
|
-
/** Current temperature in Celsius. */
|
|
68405
|
-
celsius: number().meta({
|
|
68406
|
-
|
|
69317
|
+
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
69318
|
+
celsius: number().nullable().meta(readingMeta("temperature", {
|
|
69319
|
+
precision: 1,
|
|
68407
69320
|
label: "Temperature"
|
|
68408
|
-
}),
|
|
69321
|
+
})),
|
|
68409
69322
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
68410
|
-
lastFetchedAt: number()
|
|
68411
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
68412
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
68413
|
-
* role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
|
|
68414
|
-
* Absent → the UI falls back to the role's canonical unit. */
|
|
68415
|
-
unit: string().optional(),
|
|
68416
|
-
/** Suggested decimal places for numeric display.
|
|
68417
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
68418
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
68419
|
-
* auto-formatting when absent. */
|
|
68420
|
-
precision: number().int().min(0).max(10).optional()
|
|
69323
|
+
lastFetchedAt: number()
|
|
68421
69324
|
});
|
|
68422
69325
|
var temperatureSensorCapability = {
|
|
68423
69326
|
name: "temperature-sensor",
|
|
@@ -68448,12 +69351,7 @@ var temperatureSensorCapability = {
|
|
|
68448
69351
|
} } },
|
|
68449
69352
|
/** Clock fields: written, but excluded from the compare that decides
|
|
68450
69353
|
* whether persisting is worth a SQLite commit. */
|
|
68451
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
68452
|
-
customize: {
|
|
68453
|
-
version: 1,
|
|
68454
|
-
advanced: "derive",
|
|
68455
|
-
targets: []
|
|
68456
|
-
}
|
|
69354
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
68457
69355
|
};
|
|
68458
69356
|
/**
|
|
68459
69357
|
* toast — system-scoped singleton capability that streams toast
|
|
@@ -68736,16 +69634,16 @@ var VacuumControlStatusSchema = object({
|
|
|
68736
69634
|
]),
|
|
68737
69635
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
68738
69636
|
* reading. */
|
|
68739
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
69637
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68740
69638
|
/** Current fan-speed token (provider-verbatim). Null when unknown or
|
|
68741
69639
|
* the vacuum has no speed control. */
|
|
68742
69640
|
fanSpeed: string().nullable(),
|
|
68743
69641
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
68744
69642
|
availableFanSpeeds: array(string()),
|
|
68745
69643
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
68746
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
69644
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68747
69645
|
/** Current error code (0 / null = no error). */
|
|
68748
|
-
errorCode: number().nullable(),
|
|
69646
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
68749
69647
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
68750
69648
|
errorLabel: string().nullable(),
|
|
68751
69649
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -68823,7 +69721,7 @@ var ValveStatusSchema = object({
|
|
|
68823
69721
|
]),
|
|
68824
69722
|
/** 0 = fully closed, 100 = fully open. Null when the device has no
|
|
68825
69723
|
* intermediate position surface. */
|
|
68826
|
-
position: number().min(0).max(100).nullable(),
|
|
69724
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68827
69725
|
/** Ms epoch when the slice was last updated. */
|
|
68828
69726
|
lastChangedAt: number()
|
|
68829
69727
|
});
|
|
@@ -68931,9 +69829,9 @@ var vibrationCapability = {
|
|
|
68931
69829
|
*/
|
|
68932
69830
|
var WaterHeaterStatusSchema = object({
|
|
68933
69831
|
/** Current measured temperature. Null when not reported. */
|
|
68934
|
-
currentTemp: number().nullable(),
|
|
69832
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68935
69833
|
/** Target temperature setpoint. Null when no setpoint surface. */
|
|
68936
|
-
targetTemp: number().nullable(),
|
|
69834
|
+
targetTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68937
69835
|
/** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
|
|
68938
69836
|
* `heat_pump` / `high_demand` / `performance` / `off`). Null when the
|
|
68939
69837
|
* device reports an unknown state. */
|
|
@@ -68944,9 +69842,9 @@ var WaterHeaterStatusSchema = object({
|
|
|
68944
69842
|
* has no away surface. */
|
|
68945
69843
|
away: boolean().nullable(),
|
|
68946
69844
|
/** HA `min_temp` attribute. Null when not reported. */
|
|
68947
|
-
minTemp: number().nullable(),
|
|
69845
|
+
minTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68948
69846
|
/** HA `max_temp` attribute. Null when not reported. */
|
|
68949
|
-
maxTemp: number().nullable(),
|
|
69847
|
+
maxTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68950
69848
|
/** Ms epoch when the slice was last updated. */
|
|
68951
69849
|
lastChangedAt: number()
|
|
68952
69850
|
});
|
|
@@ -69031,25 +69929,18 @@ var waterHeaterCapability = {
|
|
|
69031
69929
|
* Forecast deferred — current conditions only for v1.
|
|
69032
69930
|
*/
|
|
69033
69931
|
var WeatherStatusSchema = object({
|
|
69034
|
-
/** Verbatim
|
|
69035
|
-
* when no condition has been reported yet. */
|
|
69932
|
+
/** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
|
|
69036
69933
|
condition: string().nullable(),
|
|
69037
|
-
/** Current temperature in
|
|
69038
|
-
temperature: number().nullable(),
|
|
69039
|
-
/** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
|
|
69040
|
-
temperatureUnit: string().nullable(),
|
|
69934
|
+
/** Current temperature in Celsius. Null when not provided or refused. */
|
|
69935
|
+
temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
|
|
69041
69936
|
/** Relative humidity (0..100). Null when not provided. */
|
|
69042
|
-
humidity: number().min(0).max(100).nullable(),
|
|
69043
|
-
/** Barometric pressure in
|
|
69044
|
-
pressure: number().nullable(),
|
|
69045
|
-
/**
|
|
69046
|
-
|
|
69047
|
-
/** Wind speed in the reported unit. Null when not provided. */
|
|
69048
|
-
windSpeed: number().nullable(),
|
|
69049
|
-
/** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
|
|
69050
|
-
windSpeedUnit: string().nullable(),
|
|
69937
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
69938
|
+
/** Barometric pressure in hPa. Null when not provided or refused. */
|
|
69939
|
+
pressure: number().nullable().meta(readingMeta("pressure")),
|
|
69940
|
+
/** Wind speed in metres per second. Null when not provided or refused. */
|
|
69941
|
+
windSpeed: number().nullable().meta(readingMeta("speed")),
|
|
69051
69942
|
/** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
|
|
69052
|
-
windBearing: number().nullable(),
|
|
69943
|
+
windBearing: number().nullable().meta(readingMeta("angle")),
|
|
69053
69944
|
/** Ms epoch when the slice was last updated. */
|
|
69054
69945
|
lastFetchedAt: number()
|
|
69055
69946
|
});
|
|
@@ -69077,12 +69968,12 @@ var weatherCapability = {
|
|
|
69077
69968
|
*/
|
|
69078
69969
|
durability: "session",
|
|
69079
69970
|
/**
|
|
69080
|
-
* History (D850): recorded series.
|
|
69971
|
+
* History (D850): recorded series. Numbers are the quantity base (D944).
|
|
69081
69972
|
*/
|
|
69082
69973
|
history: { fields: {
|
|
69083
69974
|
temperature: {
|
|
69084
69975
|
kind: "gauge",
|
|
69085
|
-
|
|
69976
|
+
unit: "°C",
|
|
69086
69977
|
deadband: .2
|
|
69087
69978
|
},
|
|
69088
69979
|
humidity: {
|
|
@@ -69092,12 +69983,12 @@ var weatherCapability = {
|
|
|
69092
69983
|
},
|
|
69093
69984
|
pressure: {
|
|
69094
69985
|
kind: "gauge",
|
|
69095
|
-
|
|
69986
|
+
unit: "hPa",
|
|
69096
69987
|
deadband: .5
|
|
69097
69988
|
},
|
|
69098
69989
|
windSpeed: {
|
|
69099
69990
|
kind: "gauge",
|
|
69100
|
-
|
|
69991
|
+
unit: "m/s",
|
|
69101
69992
|
deadband: .5
|
|
69102
69993
|
}
|
|
69103
69994
|
} }
|
|
@@ -69998,8 +70889,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
69998
70889
|
kind: "mutation",
|
|
69999
70890
|
auth: "admin"
|
|
70000
70891
|
});
|
|
70892
|
+
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
70001
70893
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
70002
|
-
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;
|
|
70894
|
+
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, 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.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
70003
70895
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
70004
70896
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
70005
70897
|
new Set(CAP_NAMES);
|
|
@@ -70328,6 +71220,9 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
70328
71220
|
featureProbe: featureProbeCapability,
|
|
70329
71221
|
flood: floodCapability,
|
|
70330
71222
|
gas: gasCapability,
|
|
71223
|
+
groupActive: groupActiveCapability,
|
|
71224
|
+
groupCommands: groupCommandsCapability,
|
|
71225
|
+
groupStatus: groupStatusCapability,
|
|
70331
71226
|
heatingValve: heatingValveCapability,
|
|
70332
71227
|
humidifier: humidifierCapability,
|
|
70333
71228
|
humiditySensor: humiditySensorCapability,
|
|
@@ -70337,6 +71232,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
70337
71232
|
lawnMowerControl: lawnMowerControlCapability,
|
|
70338
71233
|
lockControl: lockControlCapability,
|
|
70339
71234
|
mediaPlayer: mediaPlayerCapability,
|
|
71235
|
+
meter: meterCapability,
|
|
70340
71236
|
motion: motionCapability,
|
|
70341
71237
|
motionTrigger: motionTriggerCapability,
|
|
70342
71238
|
motionZones: motionZonesCapability,
|
|
@@ -71596,7 +72492,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
71596
72492
|
return toDeviceSummary(device, this.addonId);
|
|
71597
72493
|
}
|
|
71598
72494
|
};
|
|
71599
|
-
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher;
|
|
72495
|
+
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
|
|
71600
72496
|
new Set(Object.values(DeviceType));
|
|
71601
72497
|
Object.freeze({
|
|
71602
72498
|
"accessories.getStatus": {
|
|
@@ -71704,6 +72600,13 @@ Object.freeze({
|
|
|
71704
72600
|
access: "view",
|
|
71705
72601
|
role: "admin"
|
|
71706
72602
|
},
|
|
72603
|
+
"addons.getPackageDocs": {
|
|
72604
|
+
capName: "addons",
|
|
72605
|
+
capScope: "system",
|
|
72606
|
+
addonId: null,
|
|
72607
|
+
access: "view",
|
|
72608
|
+
role: "admin"
|
|
72609
|
+
},
|
|
71707
72610
|
"addons.getVersions": {
|
|
71708
72611
|
capName: "addons",
|
|
71709
72612
|
capScope: "system",
|
|
@@ -74812,6 +75715,27 @@ Object.freeze({
|
|
|
74812
75715
|
access: "view",
|
|
74813
75716
|
role: "viewer"
|
|
74814
75717
|
},
|
|
75718
|
+
"groupActive.getStatus": {
|
|
75719
|
+
capName: "group-active",
|
|
75720
|
+
capScope: "device",
|
|
75721
|
+
addonId: null,
|
|
75722
|
+
access: "view",
|
|
75723
|
+
role: "viewer"
|
|
75724
|
+
},
|
|
75725
|
+
"groupCommands.getStatus": {
|
|
75726
|
+
capName: "group-commands",
|
|
75727
|
+
capScope: "device",
|
|
75728
|
+
addonId: null,
|
|
75729
|
+
access: "view",
|
|
75730
|
+
role: "viewer"
|
|
75731
|
+
},
|
|
75732
|
+
"groupStatus.getStatus": {
|
|
75733
|
+
capName: "group-status",
|
|
75734
|
+
capScope: "device",
|
|
75735
|
+
addonId: null,
|
|
75736
|
+
access: "view",
|
|
75737
|
+
role: "viewer"
|
|
75738
|
+
},
|
|
74815
75739
|
"heatingValve.getStatus": {
|
|
74816
75740
|
capName: "heating-valve",
|
|
74817
75741
|
capScope: "device",
|
|
@@ -75764,6 +76688,13 @@ Object.freeze({
|
|
|
75764
76688
|
access: "create",
|
|
75765
76689
|
role: "admin"
|
|
75766
76690
|
},
|
|
76691
|
+
"meter.getStatus": {
|
|
76692
|
+
capName: "meter",
|
|
76693
|
+
capScope: "device",
|
|
76694
|
+
addonId: null,
|
|
76695
|
+
access: "view",
|
|
76696
|
+
role: "viewer"
|
|
76697
|
+
},
|
|
75767
76698
|
"metricsProvider.collectSnapshot": {
|
|
75768
76699
|
capName: "metrics-provider",
|
|
75769
76700
|
capScope: "system",
|
|
@@ -83862,7 +84793,39 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
83862
84793
|
phrase: t("When {device} finishes", "Quando {device} finisce"),
|
|
83863
84794
|
icon: "square"
|
|
83864
84795
|
}
|
|
83865
|
-
}
|
|
84796
|
+
},
|
|
84797
|
+
overrides: { byType: {
|
|
84798
|
+
[DeviceType.Washer]: {
|
|
84799
|
+
state: { running: {
|
|
84800
|
+
whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
|
|
84801
|
+
whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
|
|
84802
|
+
} },
|
|
84803
|
+
events: {
|
|
84804
|
+
onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
|
|
84805
|
+
onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
|
|
84806
|
+
}
|
|
84807
|
+
},
|
|
84808
|
+
[DeviceType.Dryer]: {
|
|
84809
|
+
state: { running: {
|
|
84810
|
+
whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
|
|
84811
|
+
whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
|
|
84812
|
+
} },
|
|
84813
|
+
events: {
|
|
84814
|
+
onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
|
|
84815
|
+
onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
|
|
84816
|
+
}
|
|
84817
|
+
},
|
|
84818
|
+
[DeviceType.Dishwasher]: {
|
|
84819
|
+
state: { running: {
|
|
84820
|
+
whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
|
|
84821
|
+
whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
|
|
84822
|
+
} },
|
|
84823
|
+
events: {
|
|
84824
|
+
onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
|
|
84825
|
+
onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
|
|
84826
|
+
}
|
|
84827
|
+
}
|
|
84828
|
+
} }
|
|
83866
84829
|
},
|
|
83867
84830
|
update: {
|
|
83868
84831
|
scope: "device",
|
|
@@ -84634,6 +85597,41 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84634
85597
|
icon: "flame"
|
|
84635
85598
|
}]
|
|
84636
85599
|
},
|
|
85600
|
+
"group-active": {
|
|
85601
|
+
scope: "device",
|
|
85602
|
+
noun: t("Group state", "Stato del gruppo")
|
|
85603
|
+
},
|
|
85604
|
+
"group-commands": {
|
|
85605
|
+
scope: "device",
|
|
85606
|
+
noun: t("Group command", "Comando di gruppo")
|
|
85607
|
+
},
|
|
85608
|
+
"group-status": {
|
|
85609
|
+
scope: "device",
|
|
85610
|
+
noun: t("Group", "Gruppo"),
|
|
85611
|
+
state: [
|
|
85612
|
+
{
|
|
85613
|
+
kind: "numeric",
|
|
85614
|
+
field: "included",
|
|
85615
|
+
quantity: t("the members reporting", "i membri che rispondono"),
|
|
85616
|
+
label: t("Reporting", "Rispondono"),
|
|
85617
|
+
icon: "users"
|
|
85618
|
+
},
|
|
85619
|
+
{
|
|
85620
|
+
kind: "numeric",
|
|
85621
|
+
field: "excluded",
|
|
85622
|
+
quantity: t("the members left out", "i membri esclusi"),
|
|
85623
|
+
label: t("Excluded", "Esclusi"),
|
|
85624
|
+
icon: "users"
|
|
85625
|
+
},
|
|
85626
|
+
{
|
|
85627
|
+
kind: "numeric",
|
|
85628
|
+
field: "total",
|
|
85629
|
+
quantity: t("the member count", "il numero di membri"),
|
|
85630
|
+
label: t("Members", "Membri"),
|
|
85631
|
+
icon: "sigma"
|
|
85632
|
+
}
|
|
85633
|
+
]
|
|
85634
|
+
},
|
|
84637
85635
|
"humidity-sensor": {
|
|
84638
85636
|
scope: "device",
|
|
84639
85637
|
noun: t("Humidity", "Umidità"),
|
|
@@ -84659,6 +85657,17 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84659
85657
|
}],
|
|
84660
85658
|
events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
|
|
84661
85659
|
},
|
|
85660
|
+
meter: {
|
|
85661
|
+
scope: "device",
|
|
85662
|
+
noun: t("Meter", "Contatore"),
|
|
85663
|
+
state: [{
|
|
85664
|
+
kind: "numeric",
|
|
85665
|
+
field: "value",
|
|
85666
|
+
quantity: t("the total", "il totale"),
|
|
85667
|
+
label: t("Total", "Totale"),
|
|
85668
|
+
icon: "gauge"
|
|
85669
|
+
}]
|
|
85670
|
+
},
|
|
84662
85671
|
"numeric-sensor": {
|
|
84663
85672
|
scope: "device",
|
|
84664
85673
|
noun: t("Sensor", "Sensore"),
|
|
@@ -84667,7 +85676,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84667
85676
|
field: "value",
|
|
84668
85677
|
quantity: t("the reading", "il valore"),
|
|
84669
85678
|
label: t("Reading", "Valore"),
|
|
84670
|
-
unitField: "unit",
|
|
84671
85679
|
icon: "gauge"
|
|
84672
85680
|
}]
|
|
84673
85681
|
},
|
|
@@ -84803,7 +85811,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84803
85811
|
field: "temperature",
|
|
84804
85812
|
quantity: t("the outdoor temperature", "la temperatura esterna"),
|
|
84805
85813
|
label: t("Temperature", "Temperatura"),
|
|
84806
|
-
|
|
85814
|
+
unit: "°C",
|
|
84807
85815
|
icon: "thermometer"
|
|
84808
85816
|
},
|
|
84809
85817
|
{
|
|
@@ -84819,7 +85827,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84819
85827
|
field: "windSpeed",
|
|
84820
85828
|
quantity: t("the wind speed", "la velocità del vento"),
|
|
84821
85829
|
label: t("Wind", "Vento"),
|
|
84822
|
-
|
|
85830
|
+
unit: "m/s",
|
|
84823
85831
|
icon: "wind"
|
|
84824
85832
|
},
|
|
84825
85833
|
{
|
|
@@ -84827,7 +85835,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84827
85835
|
field: "pressure",
|
|
84828
85836
|
quantity: t("the air pressure", "la pressione atmosferica"),
|
|
84829
85837
|
label: t("Pressure", "Pressione"),
|
|
84830
|
-
|
|
85838
|
+
unit: "hPa",
|
|
84831
85839
|
icon: "gauge"
|
|
84832
85840
|
}
|
|
84833
85841
|
]
|
|
@@ -85132,8 +86140,47 @@ function projectAction(cap, action) {
|
|
|
85132
86140
|
};
|
|
85133
86141
|
}
|
|
85134
86142
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
85135
|
-
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;
|
|
86143
|
+
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.Washer, DeviceType.Dryer;
|
|
85136
86144
|
DeviceFeature.BatteryOperated;
|
|
86145
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
86146
|
+
var HA_AS = "a power sensor (device_class power). Home Assistant has no laundry domain. running-state stays unexported until a null reading can be published as unavailable.";
|
|
86147
|
+
var HOMEKIT_REASON = "HomeKit has no laundry-appliance service. The export tab would pair an accessory with nothing on it: running-state is withheld until a null reading can be unavailable, and power is not a HomeKit service this export honours.";
|
|
86148
|
+
var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
|
|
86149
|
+
var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
|
|
86150
|
+
var MATTER_REASON = "No Matter export is built. The closest device types are Laundry Washer (0x0073) and Laundry Dryer (0x0074); a power threshold is not the Laundry Washer Mode cluster.";
|
|
86151
|
+
function withheld(reason) {
|
|
86152
|
+
return {
|
|
86153
|
+
[DeviceType.Washer]: {
|
|
86154
|
+
exported: false,
|
|
86155
|
+
reason
|
|
86156
|
+
},
|
|
86157
|
+
[DeviceType.Dryer]: {
|
|
86158
|
+
exported: false,
|
|
86159
|
+
reason
|
|
86160
|
+
},
|
|
86161
|
+
[DeviceType.Dishwasher]: {
|
|
86162
|
+
exported: false,
|
|
86163
|
+
reason
|
|
86164
|
+
}
|
|
86165
|
+
};
|
|
86166
|
+
}
|
|
86167
|
+
function offered(received) {
|
|
86168
|
+
return {
|
|
86169
|
+
[DeviceType.Washer]: {
|
|
86170
|
+
exported: true,
|
|
86171
|
+
as: received
|
|
86172
|
+
},
|
|
86173
|
+
[DeviceType.Dryer]: {
|
|
86174
|
+
exported: true,
|
|
86175
|
+
as: received
|
|
86176
|
+
},
|
|
86177
|
+
[DeviceType.Dishwasher]: {
|
|
86178
|
+
exported: true,
|
|
86179
|
+
as: received
|
|
86180
|
+
}
|
|
86181
|
+
};
|
|
86182
|
+
}
|
|
86183
|
+
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
85137
86184
|
Object.freeze({
|
|
85138
86185
|
"accessories.getStatus": [{
|
|
85139
86186
|
name: "deviceId",
|
|
@@ -86100,6 +87147,21 @@ Object.freeze({
|
|
|
86100
87147
|
form: "single",
|
|
86101
87148
|
optional: false
|
|
86102
87149
|
}],
|
|
87150
|
+
"groupActive.getStatus": [{
|
|
87151
|
+
name: "deviceId",
|
|
87152
|
+
form: "single",
|
|
87153
|
+
optional: false
|
|
87154
|
+
}],
|
|
87155
|
+
"groupCommands.getStatus": [{
|
|
87156
|
+
name: "deviceId",
|
|
87157
|
+
form: "single",
|
|
87158
|
+
optional: false
|
|
87159
|
+
}],
|
|
87160
|
+
"groupStatus.getStatus": [{
|
|
87161
|
+
name: "deviceId",
|
|
87162
|
+
form: "single",
|
|
87163
|
+
optional: false
|
|
87164
|
+
}],
|
|
86103
87165
|
"heatingValve.getStatus": [{
|
|
86104
87166
|
name: "deviceId",
|
|
86105
87167
|
form: "single",
|
|
@@ -86300,6 +87362,11 @@ Object.freeze({
|
|
|
86300
87362
|
form: "single",
|
|
86301
87363
|
optional: false
|
|
86302
87364
|
}],
|
|
87365
|
+
"meter.getStatus": [{
|
|
87366
|
+
name: "deviceId",
|
|
87367
|
+
form: "single",
|
|
87368
|
+
optional: false
|
|
87369
|
+
}],
|
|
86303
87370
|
"motion.getStatus": [{
|
|
86304
87371
|
name: "deviceId",
|
|
86305
87372
|
form: "single",
|
|
@@ -87907,7 +88974,6 @@ Object.freeze({
|
|
|
87907
88974
|
"network-access": "ingress",
|
|
87908
88975
|
"smtp-provider": "email"
|
|
87909
88976
|
});
|
|
87910
|
-
DeviceType.Camera;
|
|
87911
88977
|
/**
|
|
87912
88978
|
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
87913
88979
|
* `z.infer`, they do not restate the shape.
|
|
@@ -88084,6 +89150,8 @@ new Map(Object.entries({
|
|
|
88084
89150
|
"detection-blind": "device-events",
|
|
88085
89151
|
"motion-noisy": "device-events",
|
|
88086
89152
|
"motion-calm": "device-events",
|
|
89153
|
+
"package-llm-silent": "device-events",
|
|
89154
|
+
"package-llm-answered": "device-events",
|
|
88087
89155
|
"recording-stalled": "device-events",
|
|
88088
89156
|
"recording-healthy": "device-events",
|
|
88089
89157
|
"recording-degraded": "device-events",
|
|
@@ -88167,6 +89235,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
88167
89235
|
label: "Motion without a subject",
|
|
88168
89236
|
kinds: ["motion-noisy", "motion-calm"]
|
|
88169
89237
|
},
|
|
89238
|
+
{
|
|
89239
|
+
id: "package-llm",
|
|
89240
|
+
family: "device-events",
|
|
89241
|
+
label: "Package confirm",
|
|
89242
|
+
kinds: ["package-llm-silent", "package-llm-answered"]
|
|
89243
|
+
},
|
|
88170
89244
|
{
|
|
88171
89245
|
id: "recording",
|
|
88172
89246
|
family: "device-events",
|
|
@@ -88348,6 +89422,8 @@ var DEVICE_EVENTS = [
|
|
|
88348
89422
|
"detection-blind",
|
|
88349
89423
|
"motion-noisy",
|
|
88350
89424
|
"motion-calm",
|
|
89425
|
+
"package-llm-silent",
|
|
89426
|
+
"package-llm-answered",
|
|
88351
89427
|
"recording-stalled",
|
|
88352
89428
|
"recording-healthy",
|
|
88353
89429
|
"recording-degraded",
|
|
@@ -88616,6 +89692,15 @@ new Map([
|
|
|
88616
89692
|
snapshotLabel: "Cameras with constant motion now",
|
|
88617
89693
|
snapshotSection: null
|
|
88618
89694
|
},
|
|
89695
|
+
{
|
|
89696
|
+
id: "package-llm-confirm",
|
|
89697
|
+
family: "device-events",
|
|
89698
|
+
label: "Package confirm (not answering / answering again)",
|
|
89699
|
+
bad: ["package-llm-silent"],
|
|
89700
|
+
recovery: ["package-llm-answered"],
|
|
89701
|
+
snapshotLabel: "Cameras whose package confirm is not answering",
|
|
89702
|
+
snapshotSection: null
|
|
89703
|
+
},
|
|
88619
89704
|
{
|
|
88620
89705
|
id: "recording-health",
|
|
88621
89706
|
family: "device-events",
|