@camstack/addon-provider-unifi 0.2.147 → 0.2.149
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 +25 -0
- package/dist/addon.js +1607 -522
- package/dist/addon.mjs +1607 -522
- package/package.json +4 -2
package/dist/addon.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../types/dist/event-category-
|
|
1
|
+
//#region ../types/dist/event-category-CT-hz5Rb.mjs
|
|
2
2
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
3
3
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4
4
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -324,6 +324,14 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
324
324
|
/** The recovery edge of {@link PipelineMotionNoisy}: the motion fell back, or a detection arrived. */
|
|
325
325
|
EventCategory["PipelineMotionCalm"] = "pipeline.motion-calm";
|
|
326
326
|
/**
|
|
327
|
+
* A camera's package LLM confirm has failed closed three times in a row
|
|
328
|
+
* (D931, 2026-10-26). One edge per camera until
|
|
329
|
+
* {@link PipelinePackageLlmAnswered}.
|
|
330
|
+
*/
|
|
331
|
+
EventCategory["PipelinePackageLlmSilent"] = "pipeline.package-llm-silent";
|
|
332
|
+
/** The recovery edge of {@link PipelinePackageLlmSilent}: a confirm returned yes or no. */
|
|
333
|
+
EventCategory["PipelinePackageLlmAnswered"] = "pipeline.package-llm-answered";
|
|
334
|
+
/**
|
|
327
335
|
* Per-camera pipeline config was mutated by the orchestrator
|
|
328
336
|
* (a settings change via `setAgentInferenceDevices` /
|
|
329
337
|
* `setCameraStepToggle` / `setCameraStepSettings`). Orchestrator
|
|
@@ -5433,7 +5441,7 @@ var ZodIssueCode = {
|
|
|
5433
5441
|
var ZodFirstPartyTypeKind;
|
|
5434
5442
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5435
5443
|
//#endregion
|
|
5436
|
-
//#region ../types/dist/sleep
|
|
5444
|
+
//#region ../types/dist/sleep--jXS9hWo.mjs
|
|
5437
5445
|
/**
|
|
5438
5446
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5439
5447
|
* window to float samples (D455).
|
|
@@ -7987,6 +7995,99 @@ var RecordKindDeclarationSchema = object({
|
|
|
7987
7995
|
}).strict();
|
|
7988
7996
|
/** The kind names: lower-case, dash separated, short enough for an index name. */
|
|
7989
7997
|
var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
|
|
7998
|
+
var READING_QUANTITIES = [
|
|
7999
|
+
"temperature",
|
|
8000
|
+
"pressure",
|
|
8001
|
+
"speed",
|
|
8002
|
+
"precipitation",
|
|
8003
|
+
"precipitation-rate",
|
|
8004
|
+
"length",
|
|
8005
|
+
"area",
|
|
8006
|
+
"illuminance",
|
|
8007
|
+
"irradiance",
|
|
8008
|
+
"power",
|
|
8009
|
+
"energy",
|
|
8010
|
+
"voltage",
|
|
8011
|
+
"current",
|
|
8012
|
+
"apparent-power",
|
|
8013
|
+
"humidity",
|
|
8014
|
+
"percentage",
|
|
8015
|
+
"concentration-volume",
|
|
8016
|
+
"concentration-mass",
|
|
8017
|
+
"concentration-ppb",
|
|
8018
|
+
"volume",
|
|
8019
|
+
"duration",
|
|
8020
|
+
"duration-ms",
|
|
8021
|
+
"information",
|
|
8022
|
+
"sound-level",
|
|
8023
|
+
"signal-level",
|
|
8024
|
+
"colour-temperature",
|
|
8025
|
+
"angle",
|
|
8026
|
+
"bitrate",
|
|
8027
|
+
"rate",
|
|
8028
|
+
"mass",
|
|
8029
|
+
"index",
|
|
8030
|
+
"ratio",
|
|
8031
|
+
"count",
|
|
8032
|
+
"none",
|
|
8033
|
+
"device"
|
|
8034
|
+
];
|
|
8035
|
+
var ReadingQuantitySchema = _enum(READING_QUANTITIES);
|
|
8036
|
+
new Set(READING_QUANTITIES);
|
|
8037
|
+
/**
|
|
8038
|
+
* Stored unit symbol for a quantity. `none` means the number is dimensionless
|
|
8039
|
+
* (or an index / ratio / count). `device` means the cap has no single unit.
|
|
8040
|
+
*/
|
|
8041
|
+
var QUANTITY_BASE = {
|
|
8042
|
+
temperature: "°C",
|
|
8043
|
+
pressure: "hPa",
|
|
8044
|
+
speed: "m/s",
|
|
8045
|
+
precipitation: "mm",
|
|
8046
|
+
"precipitation-rate": "mm/h",
|
|
8047
|
+
length: "m",
|
|
8048
|
+
area: "m²",
|
|
8049
|
+
illuminance: "lx",
|
|
8050
|
+
irradiance: "W/m²",
|
|
8051
|
+
power: "W",
|
|
8052
|
+
energy: "kWh",
|
|
8053
|
+
voltage: "V",
|
|
8054
|
+
current: "A",
|
|
8055
|
+
"apparent-power": "VA",
|
|
8056
|
+
humidity: "%",
|
|
8057
|
+
percentage: "%",
|
|
8058
|
+
"concentration-volume": "ppm",
|
|
8059
|
+
"concentration-mass": "µg/m³",
|
|
8060
|
+
"concentration-ppb": "ppb",
|
|
8061
|
+
volume: "L",
|
|
8062
|
+
duration: "s",
|
|
8063
|
+
"duration-ms": "ms",
|
|
8064
|
+
information: "B",
|
|
8065
|
+
"sound-level": "dBFS",
|
|
8066
|
+
"signal-level": "dBm",
|
|
8067
|
+
"colour-temperature": "mired",
|
|
8068
|
+
angle: "°",
|
|
8069
|
+
bitrate: "kbps",
|
|
8070
|
+
rate: "fps",
|
|
8071
|
+
mass: "kg",
|
|
8072
|
+
index: "none",
|
|
8073
|
+
ratio: "none",
|
|
8074
|
+
count: "none",
|
|
8075
|
+
none: "none",
|
|
8076
|
+
device: "device"
|
|
8077
|
+
};
|
|
8078
|
+
/** The stored unit, or `undefined` when the quantity has no unit symbol. */
|
|
8079
|
+
function baseUnitOf(quantity) {
|
|
8080
|
+
const symbol = QUANTITY_BASE[quantity];
|
|
8081
|
+
if (symbol === "none" || symbol === "device") return void 0;
|
|
8082
|
+
return symbol;
|
|
8083
|
+
}
|
|
8084
|
+
function readingMeta(quantity, extras) {
|
|
8085
|
+
return {
|
|
8086
|
+
quantity,
|
|
8087
|
+
...extras?.precision !== void 0 ? { precision: extras.precision } : {},
|
|
8088
|
+
...extras?.label !== void 0 ? { label: extras.label } : {}
|
|
8089
|
+
};
|
|
8090
|
+
}
|
|
7990
8091
|
/** Shared Zod schemas used across streaming capabilities. */
|
|
7991
8092
|
var CamProfileSchema = _enum([
|
|
7992
8093
|
"high",
|
|
@@ -8014,8 +8115,8 @@ var CamStreamKindSchema = _enum([
|
|
|
8014
8115
|
"derived"
|
|
8015
8116
|
]);
|
|
8016
8117
|
var CamStreamResolutionSchema = object({
|
|
8017
|
-
width: number().int().positive(),
|
|
8018
|
-
height: number().int().positive()
|
|
8118
|
+
width: number().int().positive().meta(readingMeta("none")),
|
|
8119
|
+
height: number().int().positive().meta(readingMeta("none"))
|
|
8019
8120
|
});
|
|
8020
8121
|
var CameraStreamSchema = object({
|
|
8021
8122
|
/** Stable, provider-assigned id unique within the (deviceId) scope. */
|
|
@@ -8533,6 +8634,9 @@ var CAP_NAMES = Object.freeze([
|
|
|
8533
8634
|
"filesystem-browse",
|
|
8534
8635
|
"flood",
|
|
8535
8636
|
"gas",
|
|
8637
|
+
"group-active",
|
|
8638
|
+
"group-commands",
|
|
8639
|
+
"group-status",
|
|
8536
8640
|
"heating-valve",
|
|
8537
8641
|
"humidifier",
|
|
8538
8642
|
"humidity-sensor",
|
|
@@ -8553,6 +8657,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8553
8657
|
"login-method",
|
|
8554
8658
|
"media-player",
|
|
8555
8659
|
"mesh-network",
|
|
8660
|
+
"meter",
|
|
8556
8661
|
"metrics-provider",
|
|
8557
8662
|
"model-convert",
|
|
8558
8663
|
"model-distributor",
|
|
@@ -8662,14 +8767,6 @@ Object.freeze({
|
|
|
8662
8767
|
{
|
|
8663
8768
|
"path": "aqi",
|
|
8664
8769
|
"valueType": "number"
|
|
8665
|
-
},
|
|
8666
|
-
{
|
|
8667
|
-
"path": "unit",
|
|
8668
|
-
"valueType": "string"
|
|
8669
|
-
},
|
|
8670
|
-
{
|
|
8671
|
-
"path": "precision",
|
|
8672
|
-
"valueType": "number"
|
|
8673
8770
|
}
|
|
8674
8771
|
],
|
|
8675
8772
|
"alarm-panel": [
|
|
@@ -8705,20 +8802,10 @@ Object.freeze({
|
|
|
8705
8802
|
"valueType": "boolean"
|
|
8706
8803
|
}
|
|
8707
8804
|
],
|
|
8708
|
-
"ambient-light-sensor": [
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
},
|
|
8713
|
-
{
|
|
8714
|
-
"path": "unit",
|
|
8715
|
-
"valueType": "string"
|
|
8716
|
-
},
|
|
8717
|
-
{
|
|
8718
|
-
"path": "precision",
|
|
8719
|
-
"valueType": "number"
|
|
8720
|
-
}
|
|
8721
|
-
],
|
|
8805
|
+
"ambient-light-sensor": [{
|
|
8806
|
+
"path": "lux",
|
|
8807
|
+
"valueType": "number"
|
|
8808
|
+
}],
|
|
8722
8809
|
"audio-metrics": [
|
|
8723
8810
|
{
|
|
8724
8811
|
"path": "ts",
|
|
@@ -8798,6 +8885,10 @@ Object.freeze({
|
|
|
8798
8885
|
"path": "lastUpdated",
|
|
8799
8886
|
"valueType": "number"
|
|
8800
8887
|
},
|
|
8888
|
+
{
|
|
8889
|
+
"path": "volts",
|
|
8890
|
+
"valueType": "number"
|
|
8891
|
+
},
|
|
8801
8892
|
{
|
|
8802
8893
|
"path": "lastContactAt",
|
|
8803
8894
|
"valueType": "number"
|
|
@@ -9040,10 +9131,6 @@ Object.freeze({
|
|
|
9040
9131
|
"datetime"
|
|
9041
9132
|
]
|
|
9042
9133
|
},
|
|
9043
|
-
{
|
|
9044
|
-
"path": "unit",
|
|
9045
|
-
"valueType": "string"
|
|
9046
|
-
},
|
|
9047
9134
|
{
|
|
9048
9135
|
"path": "min",
|
|
9049
9136
|
"valueType": "number"
|
|
@@ -9055,19 +9142,6 @@ Object.freeze({
|
|
|
9055
9142
|
{
|
|
9056
9143
|
"path": "step",
|
|
9057
9144
|
"valueType": "number"
|
|
9058
|
-
},
|
|
9059
|
-
{
|
|
9060
|
-
"path": "precision",
|
|
9061
|
-
"valueType": "number"
|
|
9062
|
-
},
|
|
9063
|
-
{
|
|
9064
|
-
"path": "format",
|
|
9065
|
-
"valueType": "enum",
|
|
9066
|
-
"enumValues": [
|
|
9067
|
-
"date",
|
|
9068
|
-
"time",
|
|
9069
|
-
"datetime"
|
|
9070
|
-
]
|
|
9071
9145
|
}
|
|
9072
9146
|
],
|
|
9073
9147
|
"cover": [
|
|
@@ -9308,13 +9382,8 @@ Object.freeze({
|
|
|
9308
9382
|
"path": "value",
|
|
9309
9383
|
"valueType": "string"
|
|
9310
9384
|
}, {
|
|
9311
|
-
"path": "
|
|
9312
|
-
"valueType": "
|
|
9313
|
-
"enumValues": [
|
|
9314
|
-
"date",
|
|
9315
|
-
"time",
|
|
9316
|
-
"datetime"
|
|
9317
|
-
]
|
|
9385
|
+
"path": "timestamp",
|
|
9386
|
+
"valueType": "boolean"
|
|
9318
9387
|
}],
|
|
9319
9388
|
"event-emitter": [
|
|
9320
9389
|
{
|
|
@@ -9387,6 +9456,24 @@ Object.freeze({
|
|
|
9387
9456
|
"path": "detected",
|
|
9388
9457
|
"valueType": "boolean"
|
|
9389
9458
|
}],
|
|
9459
|
+
"group-active": [{
|
|
9460
|
+
"path": "active",
|
|
9461
|
+
"valueType": "boolean"
|
|
9462
|
+
}],
|
|
9463
|
+
"group-status": [
|
|
9464
|
+
{
|
|
9465
|
+
"path": "included",
|
|
9466
|
+
"valueType": "number"
|
|
9467
|
+
},
|
|
9468
|
+
{
|
|
9469
|
+
"path": "excluded",
|
|
9470
|
+
"valueType": "number"
|
|
9471
|
+
},
|
|
9472
|
+
{
|
|
9473
|
+
"path": "total",
|
|
9474
|
+
"valueType": "number"
|
|
9475
|
+
}
|
|
9476
|
+
],
|
|
9390
9477
|
"heating-valve": [{
|
|
9391
9478
|
"path": "position",
|
|
9392
9479
|
"valueType": "number"
|
|
@@ -9421,20 +9508,10 @@ Object.freeze({
|
|
|
9421
9508
|
"valueType": "number"
|
|
9422
9509
|
}
|
|
9423
9510
|
],
|
|
9424
|
-
"humidity-sensor": [
|
|
9425
|
-
|
|
9426
|
-
|
|
9427
|
-
|
|
9428
|
-
},
|
|
9429
|
-
{
|
|
9430
|
-
"path": "unit",
|
|
9431
|
-
"valueType": "string"
|
|
9432
|
-
},
|
|
9433
|
-
{
|
|
9434
|
-
"path": "precision",
|
|
9435
|
-
"valueType": "number"
|
|
9436
|
-
}
|
|
9437
|
-
],
|
|
9511
|
+
"humidity-sensor": [{
|
|
9512
|
+
"path": "percent",
|
|
9513
|
+
"valueType": "number"
|
|
9514
|
+
}],
|
|
9438
9515
|
"image": [{
|
|
9439
9516
|
"path": "url",
|
|
9440
9517
|
"valueType": "string"
|
|
@@ -9642,6 +9719,10 @@ Object.freeze({
|
|
|
9642
9719
|
]
|
|
9643
9720
|
}
|
|
9644
9721
|
],
|
|
9722
|
+
"meter": [{
|
|
9723
|
+
"path": "value",
|
|
9724
|
+
"valueType": "number"
|
|
9725
|
+
}],
|
|
9645
9726
|
"motion": [
|
|
9646
9727
|
{
|
|
9647
9728
|
"path": "detected",
|
|
@@ -9773,20 +9854,10 @@ Object.freeze({
|
|
|
9773
9854
|
"valueType": "boolean"
|
|
9774
9855
|
}
|
|
9775
9856
|
],
|
|
9776
|
-
"numeric-sensor": [
|
|
9777
|
-
|
|
9778
|
-
|
|
9779
|
-
|
|
9780
|
-
},
|
|
9781
|
-
{
|
|
9782
|
-
"path": "unit",
|
|
9783
|
-
"valueType": "string"
|
|
9784
|
-
},
|
|
9785
|
-
{
|
|
9786
|
-
"path": "precision",
|
|
9787
|
-
"valueType": "number"
|
|
9788
|
-
}
|
|
9789
|
-
],
|
|
9857
|
+
"numeric-sensor": [{
|
|
9858
|
+
"path": "value",
|
|
9859
|
+
"valueType": "number"
|
|
9860
|
+
}],
|
|
9790
9861
|
"pet-feeder": [
|
|
9791
9862
|
{
|
|
9792
9863
|
"path": "foodLevel",
|
|
@@ -9878,14 +9949,6 @@ Object.freeze({
|
|
|
9878
9949
|
{
|
|
9879
9950
|
"path": "powerFactor",
|
|
9880
9951
|
"valueType": "number"
|
|
9881
|
-
},
|
|
9882
|
-
{
|
|
9883
|
-
"path": "unit",
|
|
9884
|
-
"valueType": "string"
|
|
9885
|
-
},
|
|
9886
|
-
{
|
|
9887
|
-
"path": "precision",
|
|
9888
|
-
"valueType": "number"
|
|
9889
9952
|
}
|
|
9890
9953
|
],
|
|
9891
9954
|
"presence": [
|
|
@@ -9914,20 +9977,10 @@ Object.freeze({
|
|
|
9914
9977
|
"valueType": "number"
|
|
9915
9978
|
}
|
|
9916
9979
|
],
|
|
9917
|
-
"pressure-sensor": [
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
},
|
|
9922
|
-
{
|
|
9923
|
-
"path": "unit",
|
|
9924
|
-
"valueType": "string"
|
|
9925
|
-
},
|
|
9926
|
-
{
|
|
9927
|
-
"path": "precision",
|
|
9928
|
-
"valueType": "number"
|
|
9929
|
-
}
|
|
9930
|
-
],
|
|
9980
|
+
"pressure-sensor": [{
|
|
9981
|
+
"path": "hpa",
|
|
9982
|
+
"valueType": "number"
|
|
9983
|
+
}],
|
|
9931
9984
|
"privacy-mask": [{
|
|
9932
9985
|
"path": "enabled",
|
|
9933
9986
|
"valueType": "boolean"
|
|
@@ -10238,20 +10291,10 @@ Object.freeze({
|
|
|
10238
10291
|
"path": "tampered",
|
|
10239
10292
|
"valueType": "boolean"
|
|
10240
10293
|
}],
|
|
10241
|
-
"temperature-sensor": [
|
|
10242
|
-
|
|
10243
|
-
|
|
10244
|
-
|
|
10245
|
-
},
|
|
10246
|
-
{
|
|
10247
|
-
"path": "unit",
|
|
10248
|
-
"valueType": "string"
|
|
10249
|
-
},
|
|
10250
|
-
{
|
|
10251
|
-
"path": "precision",
|
|
10252
|
-
"valueType": "number"
|
|
10253
|
-
}
|
|
10254
|
-
],
|
|
10294
|
+
"temperature-sensor": [{
|
|
10295
|
+
"path": "celsius",
|
|
10296
|
+
"valueType": "number"
|
|
10297
|
+
}],
|
|
10255
10298
|
"update": [
|
|
10256
10299
|
{
|
|
10257
10300
|
"path": "currentVersion",
|
|
@@ -10362,10 +10405,6 @@ Object.freeze({
|
|
|
10362
10405
|
"path": "temperature",
|
|
10363
10406
|
"valueType": "number"
|
|
10364
10407
|
},
|
|
10365
|
-
{
|
|
10366
|
-
"path": "temperatureUnit",
|
|
10367
|
-
"valueType": "string"
|
|
10368
|
-
},
|
|
10369
10408
|
{
|
|
10370
10409
|
"path": "humidity",
|
|
10371
10410
|
"valueType": "number"
|
|
@@ -10374,18 +10413,10 @@ Object.freeze({
|
|
|
10374
10413
|
"path": "pressure",
|
|
10375
10414
|
"valueType": "number"
|
|
10376
10415
|
},
|
|
10377
|
-
{
|
|
10378
|
-
"path": "pressureUnit",
|
|
10379
|
-
"valueType": "string"
|
|
10380
|
-
},
|
|
10381
10416
|
{
|
|
10382
10417
|
"path": "windSpeed",
|
|
10383
10418
|
"valueType": "number"
|
|
10384
10419
|
},
|
|
10385
|
-
{
|
|
10386
|
-
"path": "windSpeedUnit",
|
|
10387
|
-
"valueType": "string"
|
|
10388
|
-
},
|
|
10389
10420
|
{
|
|
10390
10421
|
"path": "windBearing",
|
|
10391
10422
|
"valueType": "number"
|
|
@@ -10449,7 +10480,7 @@ function userError(code, params, englishFallback) {
|
|
|
10449
10480
|
return new CamstackError(code, params, englishFallback);
|
|
10450
10481
|
}
|
|
10451
10482
|
//#endregion
|
|
10452
|
-
//#region ../types/dist/err-msg-
|
|
10483
|
+
//#region ../types/dist/err-msg-DIorGYeC.mjs
|
|
10453
10484
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
10454
10485
|
DeviceType["Camera"] = "camera";
|
|
10455
10486
|
DeviceType["Hub"] = "hub";
|
|
@@ -10543,8 +10574,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
10543
10574
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
10544
10575
|
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
10545
10576
|
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
10546
|
-
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10577
|
+
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10578
|
+
* A plug-built dishwasher carries running-state and power instead. */
|
|
10547
10579
|
DeviceType["Dishwasher"] = "dishwasher";
|
|
10580
|
+
/** Washing machine. A plug-built one is running while its power stays
|
|
10581
|
+
* above a threshold. No program cap — that surface is the dishwasher's. */
|
|
10582
|
+
DeviceType["Washer"] = "washer";
|
|
10583
|
+
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
10584
|
+
DeviceType["Dryer"] = "dryer";
|
|
10548
10585
|
return DeviceType;
|
|
10549
10586
|
}({});
|
|
10550
10587
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -10802,7 +10839,7 @@ function errMsg(err) {
|
|
|
10802
10839
|
return String(err);
|
|
10803
10840
|
}
|
|
10804
10841
|
//#endregion
|
|
10805
|
-
//#region ../types/dist/composition-
|
|
10842
|
+
//#region ../types/dist/composition-Bb8iwHEJ.mjs
|
|
10806
10843
|
/**
|
|
10807
10844
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
10808
10845
|
*
|
|
@@ -11117,6 +11154,13 @@ var table = {
|
|
|
11117
11154
|
apply: () => {
|
|
11118
11155
|
throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
|
|
11119
11156
|
}
|
|
11157
|
+
},
|
|
11158
|
+
schmitt: {
|
|
11159
|
+
minArgs: 3,
|
|
11160
|
+
maxArgs: 5,
|
|
11161
|
+
apply: () => {
|
|
11162
|
+
throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
|
|
11163
|
+
}
|
|
11120
11164
|
}
|
|
11121
11165
|
};
|
|
11122
11166
|
Object.freeze(Object.assign(Object.create(null), table));
|
|
@@ -11557,15 +11601,232 @@ function validateExpressionSource(src) {
|
|
|
11557
11601
|
}
|
|
11558
11602
|
return null;
|
|
11559
11603
|
}
|
|
11560
|
-
/**
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11604
|
+
/**
|
|
11605
|
+
* One power reading becomes a washer, dryer or dishwasher (D944 helper).
|
|
11606
|
+
*
|
|
11607
|
+
* Standby of the control board is under 5 W; a motor or a heater is far
|
|
11608
|
+
* above that, so 5 W is the line. A door lock or a short pump spike is not
|
|
11609
|
+
* a cycle, so running starts only after 30 s above the line.
|
|
11610
|
+
*
|
|
11611
|
+
* A wash, dry or dishwasher pause (soak, heat-wait, heater off between
|
|
11612
|
+
* bursts) lasts several minutes. Ending the cycle on the first dip would
|
|
11613
|
+
* report "finished" in the middle of the program, so the stop dwell is
|
|
11614
|
+
* 10 minutes on every type. The number is stored per type so one machine
|
|
11615
|
+
* can move later without moving the others.
|
|
11616
|
+
*/
|
|
11617
|
+
var APPLIANCE_TEMPLATE_ID = "appliance";
|
|
11618
|
+
var APPLIANCE_KINDS = [
|
|
11619
|
+
"washer",
|
|
11620
|
+
"dryer",
|
|
11621
|
+
"dishwasher"
|
|
11622
|
+
];
|
|
11623
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
11624
|
+
/**
|
|
11625
|
+
* Statistics and integrals over a history read (D944).
|
|
11626
|
+
*
|
|
11627
|
+
* Pure: the same points always give the same answer, so a restart recomputes
|
|
11628
|
+
* rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
|
|
11629
|
+
* never the mean of the samples. A hole with no held value is not integrated
|
|
11630
|
+
* as zero. A truncated read is not published.
|
|
11631
|
+
*/
|
|
11632
|
+
var STATISTIC_STATS = [
|
|
11633
|
+
"min",
|
|
11634
|
+
"max",
|
|
11635
|
+
"mean",
|
|
11636
|
+
"sum",
|
|
11637
|
+
"count",
|
|
11638
|
+
"change"
|
|
11639
|
+
];
|
|
11640
|
+
/**
|
|
11641
|
+
* The range a statistics or integral helper asks history for (D944).
|
|
11642
|
+
*
|
|
11643
|
+
* Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
|
|
11644
|
+
* or 25 hours across a DST change. A tier is eligible only when its retention
|
|
11645
|
+
* is strictly longer than the longest that range can be. Forever qualifies.
|
|
11646
|
+
* Equal to a tier's retention is a refusal, not a short answer.
|
|
11647
|
+
*/
|
|
11648
|
+
var MINUTE_MS = 6e4;
|
|
11649
|
+
var HOUR_MS = 60 * MINUTE_MS;
|
|
11650
|
+
var DAY_MS = 24 * HOUR_MS;
|
|
11651
|
+
var ROLLING_SPANS = [
|
|
11652
|
+
"1h",
|
|
11653
|
+
"24h",
|
|
11654
|
+
"2d",
|
|
11655
|
+
"7d",
|
|
11656
|
+
"30d",
|
|
11657
|
+
"custom"
|
|
11658
|
+
];
|
|
11659
|
+
var CALENDAR_PERIODS = [
|
|
11660
|
+
"today",
|
|
11661
|
+
"yesterday",
|
|
11662
|
+
"this-week",
|
|
11663
|
+
"this-month"
|
|
11664
|
+
];
|
|
11665
|
+
var RESET_PERIODS = [
|
|
11666
|
+
"never",
|
|
11667
|
+
"daily",
|
|
11668
|
+
"weekly",
|
|
11669
|
+
"monthly"
|
|
11670
|
+
];
|
|
11671
|
+
5 * MINUTE_MS;
|
|
11672
|
+
24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
|
|
11673
|
+
7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
|
|
11674
|
+
var rollingWindowObject = object({
|
|
11675
|
+
kind: literal("rolling"),
|
|
11676
|
+
span: _enum(ROLLING_SPANS),
|
|
11677
|
+
customMinutes: number().int().min(5).optional()
|
|
11678
|
+
}).check((payload) => {
|
|
11679
|
+
if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
|
|
11680
|
+
code: "custom",
|
|
11681
|
+
message: "a custom range needs a duration in minutes",
|
|
11682
|
+
path: ["customMinutes"],
|
|
11683
|
+
input: payload.value
|
|
11684
|
+
});
|
|
11685
|
+
if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
|
|
11686
|
+
code: "custom",
|
|
11687
|
+
message: "only a custom range carries a duration",
|
|
11688
|
+
path: ["customMinutes"],
|
|
11689
|
+
input: payload.value
|
|
11690
|
+
});
|
|
11691
|
+
});
|
|
11692
|
+
var RollingWindowSchema = rollingWindowObject;
|
|
11693
|
+
var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
|
|
11694
|
+
kind: literal("calendar"),
|
|
11695
|
+
period: _enum(CALENDAR_PERIODS)
|
|
11696
|
+
})]);
|
|
11697
|
+
/**
|
|
11698
|
+
* A composition source identity: `(addonId, stableId)`, never a numeric device
|
|
11699
|
+
* id. Split out so helper schemas can name it without a cycle through
|
|
11700
|
+
* `composition.ts` (D659).
|
|
11701
|
+
*/
|
|
11564
11702
|
var CompositionSourceRefSchema = object({
|
|
11565
11703
|
/** The owning addon; `stableId` is unique only within it. */
|
|
11566
11704
|
addonId: string().min(1),
|
|
11567
11705
|
stableId: string().min(1)
|
|
11568
11706
|
});
|
|
11707
|
+
var GROUP_TEMPLATE_IDS = [
|
|
11708
|
+
"temperature",
|
|
11709
|
+
"humidity",
|
|
11710
|
+
"pressure",
|
|
11711
|
+
"illuminance",
|
|
11712
|
+
"power",
|
|
11713
|
+
"energy",
|
|
11714
|
+
"battery",
|
|
11715
|
+
"numeric",
|
|
11716
|
+
"motion",
|
|
11717
|
+
"contact",
|
|
11718
|
+
"leak",
|
|
11719
|
+
"smoke",
|
|
11720
|
+
"occupancy",
|
|
11721
|
+
"binary",
|
|
11722
|
+
"switch",
|
|
11723
|
+
"light",
|
|
11724
|
+
"fan",
|
|
11725
|
+
"cover",
|
|
11726
|
+
"valve",
|
|
11727
|
+
"lock",
|
|
11728
|
+
"media-player",
|
|
11729
|
+
"climate"
|
|
11730
|
+
];
|
|
11731
|
+
var NUMERIC_AGGREGATES = [
|
|
11732
|
+
"mean",
|
|
11733
|
+
"median",
|
|
11734
|
+
"min",
|
|
11735
|
+
"max",
|
|
11736
|
+
"sum"
|
|
11737
|
+
];
|
|
11738
|
+
var BINARY_AGGREGATES = ["any", "all"];
|
|
11739
|
+
/** How an actuator group decides it is on. Unknown members never count as off. */
|
|
11740
|
+
var ACTIVE_CRITERIA = ["at-least-one", "all"];
|
|
11741
|
+
var AGGREGATE_REDUCERS = [
|
|
11742
|
+
"mean",
|
|
11743
|
+
"median",
|
|
11744
|
+
"min",
|
|
11745
|
+
"max",
|
|
11746
|
+
"sum",
|
|
11747
|
+
"any",
|
|
11748
|
+
"all"
|
|
11749
|
+
];
|
|
11750
|
+
var GroupTemplateIdSchema = _enum(GROUP_TEMPLATE_IDS);
|
|
11751
|
+
var AggregateReducerSchema = _enum(AGGREGATE_REDUCERS);
|
|
11752
|
+
var ActiveCriterionSchema = _enum(ACTIVE_CRITERIA);
|
|
11753
|
+
/** One member. The field is stored so occupancy can name motion or binary. */
|
|
11754
|
+
var HelperMemberSchema = object({
|
|
11755
|
+
source: CompositionSourceRefSchema,
|
|
11756
|
+
cap: string().min(1),
|
|
11757
|
+
fieldPath: string().min(1)
|
|
11758
|
+
});
|
|
11759
|
+
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
11760
|
+
object({
|
|
11761
|
+
kind: literal("group"),
|
|
11762
|
+
templateId: GroupTemplateIdSchema,
|
|
11763
|
+
aggregate: AggregateReducerSchema,
|
|
11764
|
+
members: array(HelperMemberSchema).min(1).max(64),
|
|
11765
|
+
/** Actuator groups only. Readings leave this unset. */
|
|
11766
|
+
active: ActiveCriterionSchema.optional()
|
|
11767
|
+
}),
|
|
11768
|
+
object({
|
|
11769
|
+
kind: literal("appliance"),
|
|
11770
|
+
templateId: literal(APPLIANCE_TEMPLATE_ID),
|
|
11771
|
+
appliance: _enum(APPLIANCE_KINDS),
|
|
11772
|
+
thresholdWatts: number().positive(),
|
|
11773
|
+
startDwellMs: number().int().nonnegative(),
|
|
11774
|
+
stopDwellMs: number().int().nonnegative(),
|
|
11775
|
+
member: HelperMemberSchema
|
|
11776
|
+
}),
|
|
11777
|
+
object({
|
|
11778
|
+
kind: literal("statistic"),
|
|
11779
|
+
stat: _enum(STATISTIC_STATS),
|
|
11780
|
+
window: StatisticWindowSchema,
|
|
11781
|
+
source: HelperMemberSchema
|
|
11782
|
+
}),
|
|
11783
|
+
object({
|
|
11784
|
+
kind: literal("integral"),
|
|
11785
|
+
reset: _enum(RESET_PERIODS),
|
|
11786
|
+
output: _enum(["energy", "meter"]),
|
|
11787
|
+
source: HelperMemberSchema
|
|
11788
|
+
}),
|
|
11789
|
+
object({
|
|
11790
|
+
kind: literal("threshold"),
|
|
11791
|
+
source: HelperMemberSchema,
|
|
11792
|
+
high: number(),
|
|
11793
|
+
low: number(),
|
|
11794
|
+
onForMs: number().int().nonnegative(),
|
|
11795
|
+
offForMs: number().int().nonnegative()
|
|
11796
|
+
}),
|
|
11797
|
+
object({
|
|
11798
|
+
kind: literal("arithmetic"),
|
|
11799
|
+
op: _enum([
|
|
11800
|
+
"sum",
|
|
11801
|
+
"difference",
|
|
11802
|
+
"ratio"
|
|
11803
|
+
]),
|
|
11804
|
+
inputs: array(HelperMemberSchema).min(2).max(64)
|
|
11805
|
+
}),
|
|
11806
|
+
object({
|
|
11807
|
+
kind: literal("expression"),
|
|
11808
|
+
expr: string().min(1),
|
|
11809
|
+
inputs: array(object({
|
|
11810
|
+
name: string().min(1),
|
|
11811
|
+
member: HelperMemberSchema
|
|
11812
|
+
})).min(1).max(64),
|
|
11813
|
+
output: _enum(["number", "boolean"])
|
|
11814
|
+
}),
|
|
11815
|
+
object({
|
|
11816
|
+
kind: literal("derivative"),
|
|
11817
|
+
window: RollingWindowSchema,
|
|
11818
|
+
per: _enum([
|
|
11819
|
+
"second",
|
|
11820
|
+
"minute",
|
|
11821
|
+
"hour"
|
|
11822
|
+
]),
|
|
11823
|
+
source: HelperMemberSchema
|
|
11824
|
+
})
|
|
11825
|
+
]);
|
|
11826
|
+
/** A composed device's stableId is this prefix plus the block id. */
|
|
11827
|
+
var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
|
|
11828
|
+
/** Longest snippet a `code` source may carry (slice 4). */
|
|
11829
|
+
var MAX_COMPOSITION_CODE_LENGTH = 2e4;
|
|
11569
11830
|
/** One field of one capability of one source device. */
|
|
11570
11831
|
var CompositionFieldReadSchema = object({
|
|
11571
11832
|
source: CompositionSourceRefSchema,
|
|
@@ -11609,7 +11870,59 @@ var CompositionCodeSourceSchema = object({
|
|
|
11609
11870
|
var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
11610
11871
|
CompositionFromSourceSchema,
|
|
11611
11872
|
CompositionExpressionSourceSchema,
|
|
11612
|
-
CompositionCodeSourceSchema
|
|
11873
|
+
CompositionCodeSourceSchema,
|
|
11874
|
+
object({
|
|
11875
|
+
kind: literal("aggregate"),
|
|
11876
|
+
reducer: _enum([
|
|
11877
|
+
"mean",
|
|
11878
|
+
"median",
|
|
11879
|
+
"min",
|
|
11880
|
+
"max",
|
|
11881
|
+
"sum",
|
|
11882
|
+
"any",
|
|
11883
|
+
"all"
|
|
11884
|
+
]),
|
|
11885
|
+
reads: array(CompositionFieldReadSchema).min(1).max(64),
|
|
11886
|
+
/**
|
|
11887
|
+
* Set when the reads are an enum state, not a yes/no: the listed states
|
|
11888
|
+
* count as active (a cover is `open`). A state outside the list is inactive;
|
|
11889
|
+
* a read that is not a state at all is left out. Only `any` / `all` use it.
|
|
11890
|
+
*/
|
|
11891
|
+
activeWhen: array(string().min(1)).min(1).optional(),
|
|
11892
|
+
/** `value` is the reading; the others are the member counts. */
|
|
11893
|
+
publish: _enum([
|
|
11894
|
+
"value",
|
|
11895
|
+
"included",
|
|
11896
|
+
"excluded",
|
|
11897
|
+
"total"
|
|
11898
|
+
]),
|
|
11899
|
+
/** What a `value` publish does when nobody is included. Counts ignore this. */
|
|
11900
|
+
empty: _enum([
|
|
11901
|
+
"null",
|
|
11902
|
+
"omit",
|
|
11903
|
+
"offline"
|
|
11904
|
+
])
|
|
11905
|
+
}),
|
|
11906
|
+
object({
|
|
11907
|
+
kind: literal("series"),
|
|
11908
|
+
read: CompositionFieldReadSchema,
|
|
11909
|
+
/** `value` is the statistic or the integral. The others are the gap counts. */
|
|
11910
|
+
publish: _enum([
|
|
11911
|
+
"value",
|
|
11912
|
+
"included",
|
|
11913
|
+
"excluded",
|
|
11914
|
+
"total"
|
|
11915
|
+
]),
|
|
11916
|
+
/**
|
|
11917
|
+
* What an unanswered `value` does. Counts ignore this. `offline` leaves the
|
|
11918
|
+
* device unseeded until history answers, so a restart cannot publish 0.
|
|
11919
|
+
*/
|
|
11920
|
+
empty: _enum([
|
|
11921
|
+
"null",
|
|
11922
|
+
"omit",
|
|
11923
|
+
"offline"
|
|
11924
|
+
])
|
|
11925
|
+
})
|
|
11613
11926
|
]);
|
|
11614
11927
|
var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
|
|
11615
11928
|
kind: literal("forward"),
|
|
@@ -11655,7 +11968,13 @@ var CompositionSchema = object({
|
|
|
11655
11968
|
kind: literal("existing"),
|
|
11656
11969
|
device: CompositionSourceRefSchema
|
|
11657
11970
|
})]),
|
|
11658
|
-
features: array(CompositionFeatureSchema).min(1).max(16)
|
|
11971
|
+
features: array(CompositionFeatureSchema).min(1).max(16),
|
|
11972
|
+
/**
|
|
11973
|
+
* Present when the block is a helper template. Absent on a free
|
|
11974
|
+
* composition (the room example, device 4603). The editor reads this,
|
|
11975
|
+
* never the compiled features.
|
|
11976
|
+
*/
|
|
11977
|
+
helper: HelperSpecSchema.optional()
|
|
11659
11978
|
}).superRefine((composition, ctx) => {
|
|
11660
11979
|
const seen = /* @__PURE__ */ new Set();
|
|
11661
11980
|
composition.features.forEach((feature, index) => {
|
|
@@ -16762,7 +17081,22 @@ var BrokerProviderInfoSchema = object({
|
|
|
16762
17081
|
*/
|
|
16763
17082
|
interactiveConnectPath: string().min(1).optional(),
|
|
16764
17083
|
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
16765
|
-
interactiveDisconnectPath: string().min(1).optional()
|
|
17084
|
+
interactiveDisconnectPath: string().min(1).optional(),
|
|
17085
|
+
/**
|
|
17086
|
+
* The kind signs in through a server-held interactive session instead of
|
|
17087
|
+
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
17088
|
+
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
17089
|
+
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
17090
|
+
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
17091
|
+
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
17092
|
+
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
17093
|
+
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
17094
|
+
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
17095
|
+
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
17096
|
+
* offers it as the default path; the fields the session fills are
|
|
17097
|
+
* marked `filledBySignIn` in the kind's settings schema.
|
|
17098
|
+
*/
|
|
17099
|
+
signIn: object({ basePath: string().min(1) }).optional()
|
|
16766
17100
|
}))
|
|
16767
17101
|
});
|
|
16768
17102
|
var ListInputSchema = object({
|
|
@@ -17863,7 +18197,12 @@ var CompositionProblemSchema = object({
|
|
|
17863
18197
|
"item-field-reserved",
|
|
17864
18198
|
"target-missing",
|
|
17865
18199
|
"target-unreadable",
|
|
17866
|
-
"not-replaceable"
|
|
18200
|
+
"not-replaceable",
|
|
18201
|
+
"native-capability-replace",
|
|
18202
|
+
"mixed-quantity",
|
|
18203
|
+
"helper-mismatch",
|
|
18204
|
+
"member-constraint",
|
|
18205
|
+
"retention"
|
|
17867
18206
|
]),
|
|
17868
18207
|
path: string(),
|
|
17869
18208
|
message: string()
|
|
@@ -17884,7 +18223,8 @@ var CompositionUnavailableModeSchema = _enum([
|
|
|
17884
18223
|
"null",
|
|
17885
18224
|
"offline",
|
|
17886
18225
|
"unknown",
|
|
17887
|
-
"release"
|
|
18226
|
+
"release",
|
|
18227
|
+
"omit"
|
|
17888
18228
|
]);
|
|
17889
18229
|
/**
|
|
17890
18230
|
* `derived` reads a source · `clock` reads only `now`, recomputed when a sibling
|
|
@@ -17921,9 +18261,11 @@ var CompositionFieldPlanSchema = object({
|
|
|
17921
18261
|
reads: array(CompositionFieldReadSchema)
|
|
17922
18262
|
});
|
|
17923
18263
|
/**
|
|
17924
|
-
* What a feature does to its target (D663): `new` builds a capability on a
|
|
17925
|
-
* device; on an existing device it `add`s a capability the device lacks
|
|
17926
|
-
*
|
|
18264
|
+
* What a feature does to its target (D663, D944): `new` builds a capability on a
|
|
18265
|
+
* new device; on an existing device it `add`s a capability the device lacks.
|
|
18266
|
+
* Replacing a capability the device already serves is refused
|
|
18267
|
+
* (`native-capability-replace`), not planned. `replace` remains on the claim
|
|
18268
|
+
* wire for a claim the hub already holds.
|
|
17927
18269
|
*/
|
|
17928
18270
|
var CompositionFeatureModeSchema = _enum([
|
|
17929
18271
|
"new",
|
|
@@ -18094,8 +18436,392 @@ Object.freeze({
|
|
|
18094
18436
|
latchedAt: {
|
|
18095
18437
|
kind: "fixed",
|
|
18096
18438
|
result: ["number", "null"]
|
|
18439
|
+
},
|
|
18440
|
+
schmitt: {
|
|
18441
|
+
kind: "fixed",
|
|
18442
|
+
result: ["boolean", "null"]
|
|
18097
18443
|
}
|
|
18098
18444
|
});
|
|
18445
|
+
/**
|
|
18446
|
+
* One catalog row per base reading a group helper can build (D944 §3).
|
|
18447
|
+
* Data, not a form default written into the block.
|
|
18448
|
+
*/
|
|
18449
|
+
var NOW$2 = {
|
|
18450
|
+
fieldPath: "lastFetchedAt",
|
|
18451
|
+
expr: "now"
|
|
18452
|
+
};
|
|
18453
|
+
var CHANGED = {
|
|
18454
|
+
fieldPath: "lastChangedAt",
|
|
18455
|
+
expr: "now"
|
|
18456
|
+
};
|
|
18457
|
+
function numeric(input) {
|
|
18458
|
+
return {
|
|
18459
|
+
...input,
|
|
18460
|
+
family: "numeric",
|
|
18461
|
+
deviceType: DeviceType.Sensor,
|
|
18462
|
+
memberDeviceType: DeviceType.Sensor,
|
|
18463
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
18464
|
+
activeWhen: void 0,
|
|
18465
|
+
commandCaps: [],
|
|
18466
|
+
baseUnit: baseUnitOf(input.quantity),
|
|
18467
|
+
aggregates: NUMERIC_AGGREGATES,
|
|
18468
|
+
accept: [{
|
|
18469
|
+
cap: input.outputCap,
|
|
18470
|
+
fieldPath: input.outputField
|
|
18471
|
+
}]
|
|
18472
|
+
};
|
|
18473
|
+
}
|
|
18474
|
+
function binary(input) {
|
|
18475
|
+
return {
|
|
18476
|
+
...input,
|
|
18477
|
+
family: "binary",
|
|
18478
|
+
deviceType: DeviceType.Sensor,
|
|
18479
|
+
memberDeviceType: DeviceType.Sensor,
|
|
18480
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
18481
|
+
activeWhen: void 0,
|
|
18482
|
+
commandCaps: [],
|
|
18483
|
+
quantity: "none",
|
|
18484
|
+
baseUnit: void 0,
|
|
18485
|
+
aggregates: BINARY_AGGREGATES,
|
|
18486
|
+
empty: "offline"
|
|
18487
|
+
};
|
|
18488
|
+
}
|
|
18489
|
+
var GROUP_TEMPLATES = [
|
|
18490
|
+
numeric({
|
|
18491
|
+
id: "temperature",
|
|
18492
|
+
summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
|
|
18493
|
+
role: DeviceRole.TemperatureSensor,
|
|
18494
|
+
outputCap: "temperature-sensor",
|
|
18495
|
+
outputField: "celsius",
|
|
18496
|
+
quantity: "temperature",
|
|
18497
|
+
defaultAggregate: "mean",
|
|
18498
|
+
empty: "null",
|
|
18499
|
+
companions: [NOW$2]
|
|
18500
|
+
}),
|
|
18501
|
+
numeric({
|
|
18502
|
+
id: "humidity",
|
|
18503
|
+
summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18504
|
+
role: DeviceRole.HumiditySensor,
|
|
18505
|
+
outputCap: "humidity-sensor",
|
|
18506
|
+
outputField: "percent",
|
|
18507
|
+
quantity: "humidity",
|
|
18508
|
+
defaultAggregate: "mean",
|
|
18509
|
+
empty: "null",
|
|
18510
|
+
companions: [NOW$2]
|
|
18511
|
+
}),
|
|
18512
|
+
numeric({
|
|
18513
|
+
id: "pressure",
|
|
18514
|
+
summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
|
|
18515
|
+
role: DeviceRole.PressureSensor,
|
|
18516
|
+
outputCap: "pressure-sensor",
|
|
18517
|
+
outputField: "hpa",
|
|
18518
|
+
quantity: "pressure",
|
|
18519
|
+
defaultAggregate: "mean",
|
|
18520
|
+
empty: "null",
|
|
18521
|
+
companions: [NOW$2]
|
|
18522
|
+
}),
|
|
18523
|
+
numeric({
|
|
18524
|
+
id: "illuminance",
|
|
18525
|
+
summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
|
|
18526
|
+
role: DeviceRole.AmbientLightSensor,
|
|
18527
|
+
outputCap: "ambient-light-sensor",
|
|
18528
|
+
outputField: "lux",
|
|
18529
|
+
quantity: "illuminance",
|
|
18530
|
+
defaultAggregate: "mean",
|
|
18531
|
+
empty: "null",
|
|
18532
|
+
companions: [NOW$2]
|
|
18533
|
+
}),
|
|
18534
|
+
numeric({
|
|
18535
|
+
id: "power",
|
|
18536
|
+
summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
|
|
18537
|
+
role: DeviceRole.PowerSensor,
|
|
18538
|
+
outputCap: "power-meter",
|
|
18539
|
+
outputField: "watts",
|
|
18540
|
+
quantity: "power",
|
|
18541
|
+
defaultAggregate: "sum",
|
|
18542
|
+
empty: "omit",
|
|
18543
|
+
companions: [NOW$2]
|
|
18544
|
+
}),
|
|
18545
|
+
numeric({
|
|
18546
|
+
id: "energy",
|
|
18547
|
+
summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
|
|
18548
|
+
role: DeviceRole.EnergySensor,
|
|
18549
|
+
outputCap: "power-meter",
|
|
18550
|
+
outputField: "kwhTotal",
|
|
18551
|
+
quantity: "energy",
|
|
18552
|
+
defaultAggregate: "sum",
|
|
18553
|
+
empty: "omit",
|
|
18554
|
+
companions: [NOW$2]
|
|
18555
|
+
}),
|
|
18556
|
+
numeric({
|
|
18557
|
+
id: "battery",
|
|
18558
|
+
summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18559
|
+
role: DeviceRole.BatterySensor,
|
|
18560
|
+
outputCap: "battery",
|
|
18561
|
+
outputField: "percentage",
|
|
18562
|
+
quantity: "percentage",
|
|
18563
|
+
defaultAggregate: "mean",
|
|
18564
|
+
empty: "null",
|
|
18565
|
+
companions: [
|
|
18566
|
+
{
|
|
18567
|
+
fieldPath: "charging",
|
|
18568
|
+
expr: "\"unknown\""
|
|
18569
|
+
},
|
|
18570
|
+
{
|
|
18571
|
+
fieldPath: "sleeping",
|
|
18572
|
+
expr: "false"
|
|
18573
|
+
},
|
|
18574
|
+
{
|
|
18575
|
+
fieldPath: "lastUpdated",
|
|
18576
|
+
expr: "now"
|
|
18577
|
+
}
|
|
18578
|
+
]
|
|
18579
|
+
}),
|
|
18580
|
+
numeric({
|
|
18581
|
+
id: "numeric",
|
|
18582
|
+
summary: "A numeric reading in the members’ shared quantity. Mixed quantities are refused. Nobody reporting publishes null, not 0.",
|
|
18583
|
+
role: DeviceRole.NumericSensor,
|
|
18584
|
+
outputCap: "numeric-sensor",
|
|
18585
|
+
outputField: "value",
|
|
18586
|
+
quantity: "device",
|
|
18587
|
+
defaultAggregate: "mean",
|
|
18588
|
+
empty: "null",
|
|
18589
|
+
companions: [NOW$2]
|
|
18590
|
+
}),
|
|
18591
|
+
binary({
|
|
18592
|
+
id: "motion",
|
|
18593
|
+
summary: "Any or all motion. Offline and unknown members are left out, never read as off. Nobody reporting takes the device offline.",
|
|
18594
|
+
role: DeviceRole.MotionSensor,
|
|
18595
|
+
outputCap: "motion",
|
|
18596
|
+
outputField: "detected",
|
|
18597
|
+
defaultAggregate: "any",
|
|
18598
|
+
accept: [{
|
|
18599
|
+
cap: "motion",
|
|
18600
|
+
fieldPath: "detected"
|
|
18601
|
+
}],
|
|
18602
|
+
companions: [{
|
|
18603
|
+
fieldPath: "lastDetectedAt",
|
|
18604
|
+
expr: "null"
|
|
18605
|
+
}, {
|
|
18606
|
+
fieldPath: "autoClearAfterMs",
|
|
18607
|
+
expr: "null"
|
|
18608
|
+
}]
|
|
18609
|
+
}),
|
|
18610
|
+
binary({
|
|
18611
|
+
id: "contact",
|
|
18612
|
+
summary: "Any or all open contacts. Offline and unknown members are left out, never read as closed.",
|
|
18613
|
+
role: DeviceRole.ContactSensor,
|
|
18614
|
+
outputCap: "contact",
|
|
18615
|
+
outputField: "entryOpen",
|
|
18616
|
+
defaultAggregate: "any",
|
|
18617
|
+
accept: [{
|
|
18618
|
+
cap: "contact",
|
|
18619
|
+
fieldPath: "entryOpen"
|
|
18620
|
+
}],
|
|
18621
|
+
companions: [CHANGED]
|
|
18622
|
+
}),
|
|
18623
|
+
binary({
|
|
18624
|
+
id: "leak",
|
|
18625
|
+
summary: "Any or all leak detections. Offline and unknown members are left out, never read as dry.",
|
|
18626
|
+
role: DeviceRole.LeakSensor,
|
|
18627
|
+
outputCap: "flood",
|
|
18628
|
+
outputField: "flooded",
|
|
18629
|
+
defaultAggregate: "any",
|
|
18630
|
+
accept: [{
|
|
18631
|
+
cap: "flood",
|
|
18632
|
+
fieldPath: "flooded"
|
|
18633
|
+
}],
|
|
18634
|
+
companions: [CHANGED]
|
|
18635
|
+
}),
|
|
18636
|
+
binary({
|
|
18637
|
+
id: "smoke",
|
|
18638
|
+
summary: "Any or all smoke detections. Offline and unknown members are left out, never read as clear.",
|
|
18639
|
+
role: DeviceRole.SmokeSensor,
|
|
18640
|
+
outputCap: "smoke",
|
|
18641
|
+
outputField: "detected",
|
|
18642
|
+
defaultAggregate: "any",
|
|
18643
|
+
accept: [{
|
|
18644
|
+
cap: "smoke",
|
|
18645
|
+
fieldPath: "detected"
|
|
18646
|
+
}],
|
|
18647
|
+
companions: [CHANGED]
|
|
18648
|
+
}),
|
|
18649
|
+
binary({
|
|
18650
|
+
id: "occupancy",
|
|
18651
|
+
summary: "Any or all of motion or binary sensors, published as a binary. Presence zones are not aggregated. Nobody reporting is offline, not off.",
|
|
18652
|
+
role: DeviceRole.BinarySensor,
|
|
18653
|
+
outputCap: "binary",
|
|
18654
|
+
outputField: "on",
|
|
18655
|
+
defaultAggregate: "any",
|
|
18656
|
+
accept: [{
|
|
18657
|
+
cap: "motion",
|
|
18658
|
+
fieldPath: "detected"
|
|
18659
|
+
}, {
|
|
18660
|
+
cap: "binary",
|
|
18661
|
+
fieldPath: "on"
|
|
18662
|
+
}],
|
|
18663
|
+
companions: [CHANGED]
|
|
18664
|
+
}),
|
|
18665
|
+
binary({
|
|
18666
|
+
id: "binary",
|
|
18667
|
+
summary: "Any or all generic binary sensors. Offline and unknown members are left out, never read as off.",
|
|
18668
|
+
role: DeviceRole.BinarySensor,
|
|
18669
|
+
outputCap: "binary",
|
|
18670
|
+
outputField: "on",
|
|
18671
|
+
defaultAggregate: "any",
|
|
18672
|
+
accept: [{
|
|
18673
|
+
cap: "binary",
|
|
18674
|
+
fieldPath: "on"
|
|
18675
|
+
}],
|
|
18676
|
+
companions: [CHANGED]
|
|
18677
|
+
}),
|
|
18678
|
+
actuator({
|
|
18679
|
+
id: "switch",
|
|
18680
|
+
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.",
|
|
18681
|
+
deviceType: DeviceType.Switch,
|
|
18682
|
+
outputCap: "switch",
|
|
18683
|
+
outputField: "on",
|
|
18684
|
+
member: {
|
|
18685
|
+
cap: "switch",
|
|
18686
|
+
fieldPath: "on"
|
|
18687
|
+
},
|
|
18688
|
+
commandCaps: ["switch"]
|
|
18689
|
+
}),
|
|
18690
|
+
actuator({
|
|
18691
|
+
id: "light",
|
|
18692
|
+
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.",
|
|
18693
|
+
deviceType: DeviceType.Light,
|
|
18694
|
+
outputCap: "switch",
|
|
18695
|
+
outputField: "on",
|
|
18696
|
+
member: {
|
|
18697
|
+
cap: "switch",
|
|
18698
|
+
fieldPath: "on"
|
|
18699
|
+
},
|
|
18700
|
+
commandCaps: [
|
|
18701
|
+
"switch",
|
|
18702
|
+
"brightness",
|
|
18703
|
+
"color"
|
|
18704
|
+
]
|
|
18705
|
+
}),
|
|
18706
|
+
actuator({
|
|
18707
|
+
id: "fan",
|
|
18708
|
+
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.",
|
|
18709
|
+
deviceType: DeviceType.Fan,
|
|
18710
|
+
outputCap: "switch",
|
|
18711
|
+
outputField: "on",
|
|
18712
|
+
member: {
|
|
18713
|
+
cap: "switch",
|
|
18714
|
+
fieldPath: "on"
|
|
18715
|
+
},
|
|
18716
|
+
commandCaps: ["switch", "fan-control"]
|
|
18717
|
+
}),
|
|
18718
|
+
actuator({
|
|
18719
|
+
id: "cover",
|
|
18720
|
+
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.",
|
|
18721
|
+
deviceType: DeviceType.Cover,
|
|
18722
|
+
outputCap: "group-active",
|
|
18723
|
+
outputField: "active",
|
|
18724
|
+
member: {
|
|
18725
|
+
cap: "cover",
|
|
18726
|
+
fieldPath: "state"
|
|
18727
|
+
},
|
|
18728
|
+
activeWhen: [
|
|
18729
|
+
"open",
|
|
18730
|
+
"opening",
|
|
18731
|
+
"stopped"
|
|
18732
|
+
],
|
|
18733
|
+
commandCaps: ["cover"]
|
|
18734
|
+
}),
|
|
18735
|
+
actuator({
|
|
18736
|
+
id: "valve",
|
|
18737
|
+
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.",
|
|
18738
|
+
deviceType: DeviceType.Valve,
|
|
18739
|
+
outputCap: "group-active",
|
|
18740
|
+
outputField: "active",
|
|
18741
|
+
member: {
|
|
18742
|
+
cap: "valve",
|
|
18743
|
+
fieldPath: "state"
|
|
18744
|
+
},
|
|
18745
|
+
activeWhen: [
|
|
18746
|
+
"open",
|
|
18747
|
+
"opening",
|
|
18748
|
+
"stopped"
|
|
18749
|
+
],
|
|
18750
|
+
commandCaps: ["valve"]
|
|
18751
|
+
}),
|
|
18752
|
+
actuator({
|
|
18753
|
+
id: "lock",
|
|
18754
|
+
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.",
|
|
18755
|
+
deviceType: DeviceType.Lock,
|
|
18756
|
+
outputCap: "group-active",
|
|
18757
|
+
outputField: "active",
|
|
18758
|
+
member: {
|
|
18759
|
+
cap: "lock-control",
|
|
18760
|
+
fieldPath: "state"
|
|
18761
|
+
},
|
|
18762
|
+
activeWhen: ["locked"],
|
|
18763
|
+
commandCaps: ["lock-control"]
|
|
18764
|
+
}),
|
|
18765
|
+
actuator({
|
|
18766
|
+
id: "media-player",
|
|
18767
|
+
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.",
|
|
18768
|
+
deviceType: DeviceType.MediaPlayer,
|
|
18769
|
+
outputCap: "group-active",
|
|
18770
|
+
outputField: "active",
|
|
18771
|
+
member: {
|
|
18772
|
+
cap: "media-player",
|
|
18773
|
+
fieldPath: "state"
|
|
18774
|
+
},
|
|
18775
|
+
activeWhen: ["playing", "buffering"],
|
|
18776
|
+
commandCaps: ["media-player"]
|
|
18777
|
+
}),
|
|
18778
|
+
actuator({
|
|
18779
|
+
id: "climate",
|
|
18780
|
+
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.",
|
|
18781
|
+
deviceType: DeviceType.Thermostat,
|
|
18782
|
+
memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
|
|
18783
|
+
outputCap: "group-active",
|
|
18784
|
+
outputField: "active",
|
|
18785
|
+
member: {
|
|
18786
|
+
cap: "climate-control",
|
|
18787
|
+
fieldPath: "mode"
|
|
18788
|
+
},
|
|
18789
|
+
activeWhen: [
|
|
18790
|
+
"heat",
|
|
18791
|
+
"cool",
|
|
18792
|
+
"auto",
|
|
18793
|
+
"heat_cool",
|
|
18794
|
+
"fan_only",
|
|
18795
|
+
"dry"
|
|
18796
|
+
],
|
|
18797
|
+
commandCaps: ["climate-control"]
|
|
18798
|
+
})
|
|
18799
|
+
];
|
|
18800
|
+
function actuator(input) {
|
|
18801
|
+
return {
|
|
18802
|
+
id: input.id,
|
|
18803
|
+
summary: input.summary,
|
|
18804
|
+
deviceType: input.deviceType,
|
|
18805
|
+
outputCap: input.outputCap,
|
|
18806
|
+
outputField: input.outputField,
|
|
18807
|
+
commandCaps: input.commandCaps,
|
|
18808
|
+
family: "actuator",
|
|
18809
|
+
role: void 0,
|
|
18810
|
+
memberDeviceType: input.deviceType,
|
|
18811
|
+
memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
|
|
18812
|
+
activeWhen: input.activeWhen,
|
|
18813
|
+
quantity: "none",
|
|
18814
|
+
baseUnit: void 0,
|
|
18815
|
+
aggregates: BINARY_AGGREGATES,
|
|
18816
|
+
defaultAggregate: "any",
|
|
18817
|
+
empty: "offline",
|
|
18818
|
+
accept: [input.member],
|
|
18819
|
+
companions: [CHANGED]
|
|
18820
|
+
};
|
|
18821
|
+
}
|
|
18822
|
+
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
18823
|
+
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
18824
|
+
DeviceType.Camera;
|
|
18099
18825
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
18100
18826
|
/**
|
|
18101
18827
|
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
@@ -19922,12 +20648,12 @@ function buildDeviceAdminUrl(parts) {
|
|
|
19922
20648
|
* providers detect renames by matching `uniqueId` and patch `id`
|
|
19923
20649
|
* in-place so dispatch keeps working.
|
|
19924
20650
|
*
|
|
19925
|
-
* **
|
|
19926
|
-
*
|
|
19927
|
-
*
|
|
19928
|
-
*
|
|
19929
|
-
* (`ha-domain-mapping`)
|
|
19930
|
-
* stored after that point.
|
|
20651
|
+
* **Display unit and precision are NOT stored here.** They are a read-time
|
|
20652
|
+
* preference (D944). A polymorphic reading stores its `quantity` here once,
|
|
20653
|
+
* at adopt, and a date/time value stores `dateTimeShape` the same way —
|
|
20654
|
+
* neither is repeated on each sample. The `device_class` tag is an
|
|
20655
|
+
* HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
|
|
20656
|
+
* to derive the CamStack `DeviceRole`; it is never stored after that point.
|
|
19931
20657
|
*
|
|
19932
20658
|
* The `raw` escape hatch carries provider-specific attributes that don't
|
|
19933
20659
|
* fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
|
|
@@ -19941,6 +20667,14 @@ var SourceInfoSchema = object({
|
|
|
19941
20667
|
/** Immutable upstream identifier when available (HA `unique_id`, … ).
|
|
19942
20668
|
* Used to detect rename when `id` changes. */
|
|
19943
20669
|
uniqueId: string().optional(),
|
|
20670
|
+
/** Quantity of a polymorphic reading, written once at adopt (D944). */
|
|
20671
|
+
quantity: ReadingQuantitySchema.optional(),
|
|
20672
|
+
/** Date / time / datetime shape of a string reading, written once at adopt. */
|
|
20673
|
+
dateTimeShape: _enum([
|
|
20674
|
+
"date",
|
|
20675
|
+
"time",
|
|
20676
|
+
"datetime"
|
|
20677
|
+
]).optional(),
|
|
19944
20678
|
/** Provider-specific extras that don't fit the structured slots.
|
|
19945
20679
|
* Whitelist what goes here per provider — do NOT dump entire HA
|
|
19946
20680
|
* attribute blobs (would explode the meta JSON column). */
|
|
@@ -41045,7 +41779,8 @@ method(object({
|
|
|
41045
41779
|
"enum"
|
|
41046
41780
|
]),
|
|
41047
41781
|
enumValues: array(string()).optional(),
|
|
41048
|
-
item: boolean().optional()
|
|
41782
|
+
item: boolean().optional(),
|
|
41783
|
+
quantity: ReadingQuantitySchema.optional()
|
|
41049
41784
|
})).readonly(),
|
|
41050
41785
|
itemArray: object({
|
|
41051
41786
|
path: string(),
|
|
@@ -43406,7 +44141,7 @@ var NetworkAccessStatusSchema = object({
|
|
|
43406
44141
|
});
|
|
43407
44142
|
/**
|
|
43408
44143
|
* Optional, richer endpoint shape returned by providers that expose
|
|
43409
|
-
* MORE than one ingress concurrently (Tailscale
|
|
44144
|
+
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
43410
44145
|
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
43411
44146
|
* the originating provider config (mode + sourcePort) so the
|
|
43412
44147
|
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
@@ -44889,8 +45624,8 @@ var MaskPolygonShapeSchema = object({
|
|
|
44889
45624
|
/** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
|
|
44890
45625
|
var MaskGridShapeSchema = object({
|
|
44891
45626
|
kind: literal("grid"),
|
|
44892
|
-
gridWidth: number(),
|
|
44893
|
-
gridHeight: number(),
|
|
45627
|
+
gridWidth: number().meta(readingMeta("none")),
|
|
45628
|
+
gridHeight: number().meta(readingMeta("none")),
|
|
44894
45629
|
cells: array(boolean())
|
|
44895
45630
|
});
|
|
44896
45631
|
discriminatedUnion("kind", [
|
|
@@ -45002,6 +45737,8 @@ var NcSystemEventKindSchema = _enum([
|
|
|
45002
45737
|
"detection-blind",
|
|
45003
45738
|
"motion-noisy",
|
|
45004
45739
|
"motion-calm",
|
|
45740
|
+
"package-llm-silent",
|
|
45741
|
+
"package-llm-answered",
|
|
45005
45742
|
"recording-stalled",
|
|
45006
45743
|
"recording-healthy",
|
|
45007
45744
|
"recording-degraded",
|
|
@@ -46091,7 +46828,20 @@ var NcRuleInputSchema = object({
|
|
|
46091
46828
|
* save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
|
|
46092
46829
|
* path does not run Zod defaults.
|
|
46093
46830
|
*/
|
|
46094
|
-
groupingSec: number().int().min(5).max(60).optional()
|
|
46831
|
+
groupingSec: number().int().min(5).max(60).optional(),
|
|
46832
|
+
/**
|
|
46833
|
+
* ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
|
|
46834
|
+
* carries "Correct" / "Wrong" buttons (and, where the channel has room, a
|
|
46835
|
+
* "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
|
|
46836
|
+
* same mark the Library writes, and records whose notification it was and
|
|
46837
|
+
* when. A channel with no buttons gets the same two choices as links.
|
|
46838
|
+
*
|
|
46839
|
+
* `.optional()`, never `.default()` (a Zod default does not run on the addon
|
|
46840
|
+
* cap path): absent and `false` both mean off, which is what every rule
|
|
46841
|
+
* authored before this field meant. An editor turns it off by writing
|
|
46842
|
+
* `false`, so it needs no `clear` key.
|
|
46843
|
+
*/
|
|
46844
|
+
askVerdict: boolean().optional()
|
|
46095
46845
|
});
|
|
46096
46846
|
/**
|
|
46097
46847
|
* Partial patch for `updateRule` — any subset of the input fields, plus the
|
|
@@ -56093,6 +56843,28 @@ var PackageVersionInfoSchema = object({
|
|
|
56093
56843
|
deprecated: string().optional(),
|
|
56094
56844
|
distTags: array(string()).readonly()
|
|
56095
56845
|
});
|
|
56846
|
+
/**
|
|
56847
|
+
* One published document. `ok` is markdown from the registry. `missing` means
|
|
56848
|
+
* the registry answered and the file was not there. `error` is a registry or
|
|
56849
|
+
* tarball failure — never an empty string standing in for either.
|
|
56850
|
+
*/
|
|
56851
|
+
var PackageDocBodySchema = discriminatedUnion("status", [
|
|
56852
|
+
object({
|
|
56853
|
+
status: literal("ok"),
|
|
56854
|
+
markdown: string()
|
|
56855
|
+
}),
|
|
56856
|
+
object({ status: literal("missing") }),
|
|
56857
|
+
object({
|
|
56858
|
+
status: literal("error"),
|
|
56859
|
+
message: string()
|
|
56860
|
+
})
|
|
56861
|
+
]);
|
|
56862
|
+
var PackageDocsSchema = object({
|
|
56863
|
+
name: string(),
|
|
56864
|
+
version: string(),
|
|
56865
|
+
readme: PackageDocBodySchema,
|
|
56866
|
+
changelog: PackageDocBodySchema
|
|
56867
|
+
});
|
|
56096
56868
|
var SearchResultSchema = object({
|
|
56097
56869
|
name: string(),
|
|
56098
56870
|
version: string(),
|
|
@@ -56311,7 +57083,10 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
56311
57083
|
}), object({ success: literal(true) }), {
|
|
56312
57084
|
kind: "mutation",
|
|
56313
57085
|
auth: "admin"
|
|
56314
|
-
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
|
|
57086
|
+
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
|
|
57087
|
+
name: string().min(1),
|
|
57088
|
+
version: string().min(1).optional()
|
|
57089
|
+
}), PackageDocsSchema, { auth: "admin" }), method(object({ addonId: string() }), RestartAddonResultSchema, {
|
|
56315
57090
|
kind: "mutation",
|
|
56316
57091
|
auth: "admin"
|
|
56317
57092
|
}), method(object({ packageName: string() }), object({ success: literal(true) }), {
|
|
@@ -56370,27 +57145,17 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
56370
57145
|
*/
|
|
56371
57146
|
var AirQualitySensorStatusSchema = object({
|
|
56372
57147
|
/** Carbon dioxide concentration in ppm. */
|
|
56373
|
-
co2Ppm: number().min(0).optional(),
|
|
57148
|
+
co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
56374
57149
|
/** Total volatile organic compounds in ppb. */
|
|
56375
|
-
vocPpb: number().min(0).optional(),
|
|
57150
|
+
vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
56376
57151
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
56377
|
-
pm25: number().min(0).optional(),
|
|
57152
|
+
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56378
57153
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
56379
|
-
pm10: number().min(0).optional(),
|
|
57154
|
+
pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56380
57155
|
/** Composite AQI value (typically 0..500). */
|
|
56381
|
-
aqi: number().optional(),
|
|
57156
|
+
aqi: number().optional().meta(readingMeta("index")),
|
|
56382
57157
|
/** Ms epoch when the slice was last updated. */
|
|
56383
|
-
lastFetchedAt: number()
|
|
56384
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
56385
|
-
* `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
|
|
56386
|
-
* upstream `sensor.*` entity surfaces ONE device_class, so one unit
|
|
56387
|
-
* per slice is unambiguous. */
|
|
56388
|
-
unit: string().optional(),
|
|
56389
|
-
/** Suggested decimal places for numeric display.
|
|
56390
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
56391
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
56392
|
-
* auto-formatting when absent. */
|
|
56393
|
-
precision: number().int().min(0).max(10).optional()
|
|
57158
|
+
lastFetchedAt: number()
|
|
56394
57159
|
});
|
|
56395
57160
|
var airQualitySensorCapability = {
|
|
56396
57161
|
name: "air-quality-sensor",
|
|
@@ -56427,12 +57192,12 @@ var airQualitySensorCapability = {
|
|
|
56427
57192
|
},
|
|
56428
57193
|
pm25: {
|
|
56429
57194
|
kind: "gauge",
|
|
56430
|
-
unit: "
|
|
57195
|
+
unit: "µg/m³",
|
|
56431
57196
|
deadband: 1
|
|
56432
57197
|
},
|
|
56433
57198
|
pm10: {
|
|
56434
57199
|
kind: "gauge",
|
|
56435
|
-
unit: "
|
|
57200
|
+
unit: "µg/m³",
|
|
56436
57201
|
deadband: 1
|
|
56437
57202
|
},
|
|
56438
57203
|
aqi: {
|
|
@@ -56450,22 +57215,13 @@ var airQualitySensorCapability = {
|
|
|
56450
57215
|
* entries with `device_class: illuminance`.
|
|
56451
57216
|
*/
|
|
56452
57217
|
var AmbientLightSensorStatusSchema = object({
|
|
56453
|
-
/** Current illuminance in lux (lx). */
|
|
56454
|
-
lux: number().min(0).meta({
|
|
56455
|
-
|
|
57218
|
+
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
57219
|
+
lux: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
57220
|
+
precision: 0,
|
|
56456
57221
|
label: "Illuminance"
|
|
56457
|
-
}),
|
|
57222
|
+
})),
|
|
56458
57223
|
/** Ms epoch when the slice was last updated. */
|
|
56459
|
-
lastFetchedAt: number()
|
|
56460
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
56461
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
56462
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
56463
|
-
unit: string().optional(),
|
|
56464
|
-
/** Suggested decimal places for numeric display.
|
|
56465
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
56466
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
56467
|
-
* auto-formatting when absent. */
|
|
56468
|
-
precision: number().int().min(0).max(10).optional()
|
|
57224
|
+
lastFetchedAt: number()
|
|
56469
57225
|
});
|
|
56470
57226
|
var ambientLightSensorCapability = {
|
|
56471
57227
|
name: "ambient-light-sensor",
|
|
@@ -56497,12 +57253,7 @@ var ambientLightSensorCapability = {
|
|
|
56497
57253
|
} } },
|
|
56498
57254
|
/** Clock fields: written, but excluded from the compare that decides
|
|
56499
57255
|
* whether persisting is worth a SQLite commit. */
|
|
56500
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
56501
|
-
customize: {
|
|
56502
|
-
version: 1,
|
|
56503
|
-
advanced: "derive",
|
|
56504
|
-
targets: []
|
|
56505
|
-
}
|
|
57256
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
56506
57257
|
};
|
|
56507
57258
|
/**
|
|
56508
57259
|
* Per-class audio metrics aggregated over a sliding window.
|
|
@@ -56729,10 +57480,7 @@ var BatteryStatusSchema = object({
|
|
|
56729
57480
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
56730
57481
|
* declines to band a non-finite reading, which is the correct shape.
|
|
56731
57482
|
*/
|
|
56732
|
-
percentage: number().min(0).max(100).nullable().meta({
|
|
56733
|
-
unit: "%",
|
|
56734
|
-
label: "Charge"
|
|
56735
|
-
}),
|
|
57483
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
56736
57484
|
/**
|
|
56737
57485
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
56738
57486
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -56759,7 +57507,14 @@ var BatteryStatusSchema = object({
|
|
|
56759
57507
|
*/
|
|
56760
57508
|
sleeping: boolean().meta({ label: "Sleeping" }),
|
|
56761
57509
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
56762
|
-
lastUpdated: number(),
|
|
57510
|
+
lastUpdated: number().meta(readingMeta("none")),
|
|
57511
|
+
/**
|
|
57512
|
+
* Battery voltage in volts, for a cell the firmware reports as voltage
|
|
57513
|
+
* rather than a percentage (Ecowitt capacitor packs). Absent on cameras
|
|
57514
|
+
* that only report charge. A second reading on this cap, not a second
|
|
57515
|
+
* `numeric-sensor` on the same device.
|
|
57516
|
+
*/
|
|
57517
|
+
volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
56763
57518
|
/**
|
|
56764
57519
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
56765
57520
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -56806,12 +57561,12 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
56806
57561
|
DeviceType.Switch,
|
|
56807
57562
|
DeviceType.PetFeeder,
|
|
56808
57563
|
DeviceType.Container,
|
|
56809
|
-
DeviceType.Thermostat
|
|
57564
|
+
DeviceType.Thermostat,
|
|
57565
|
+
DeviceType.EventEmitter
|
|
56810
57566
|
];
|
|
56811
57567
|
/** Generic Customize target for the percentage reading (D936). */
|
|
56812
57568
|
var batteryCustomize = {
|
|
56813
57569
|
version: 1,
|
|
56814
|
-
advanced: "derive",
|
|
56815
57570
|
targets: [{
|
|
56816
57571
|
id: "percentage",
|
|
56817
57572
|
cardinality: "scalar",
|
|
@@ -56820,7 +57575,7 @@ var batteryCustomize = {
|
|
|
56820
57575
|
stateField: "percentage"
|
|
56821
57576
|
},
|
|
56822
57577
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
56823
|
-
apply: "add
|
|
57578
|
+
apply: "add",
|
|
56824
57579
|
path: "percentage",
|
|
56825
57580
|
sources: [{
|
|
56826
57581
|
kind: "field",
|
|
@@ -56941,10 +57696,16 @@ onStatusChanged: { data: object({
|
|
|
56941
57696
|
/**
|
|
56942
57697
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
56943
57698
|
*/
|
|
56944
|
-
history: { fields: {
|
|
56945
|
-
|
|
56946
|
-
|
|
56947
|
-
|
|
57699
|
+
history: { fields: {
|
|
57700
|
+
percentage: {
|
|
57701
|
+
kind: "gauge",
|
|
57702
|
+
unit: "%"
|
|
57703
|
+
},
|
|
57704
|
+
volts: {
|
|
57705
|
+
kind: "gauge",
|
|
57706
|
+
unit: "V"
|
|
57707
|
+
}
|
|
57708
|
+
} },
|
|
56948
57709
|
customize: batteryCustomize,
|
|
56949
57710
|
/** Clock fields: written, but excluded from the compare that decides
|
|
56950
57711
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -56991,12 +57752,7 @@ var binaryCapability = {
|
|
|
56991
57752
|
history: { fields: { on: { kind: "boolean" } } },
|
|
56992
57753
|
/** Clock fields: written, but excluded from the compare that decides
|
|
56993
57754
|
* whether persisting is worth a SQLite commit. */
|
|
56994
|
-
volatileStateFields: ["lastChangedAt"]
|
|
56995
|
-
customize: {
|
|
56996
|
-
version: 1,
|
|
56997
|
-
advanced: "derive",
|
|
56998
|
-
targets: []
|
|
56999
|
-
}
|
|
57755
|
+
volatileStateFields: ["lastChangedAt"]
|
|
57000
57756
|
};
|
|
57001
57757
|
/**
|
|
57002
57758
|
* Dimmable-light brightness control. Co-exists with `switch` on the
|
|
@@ -57012,7 +57768,7 @@ var binaryCapability = {
|
|
|
57012
57768
|
*/
|
|
57013
57769
|
var BrightnessStatusSchema = object({
|
|
57014
57770
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
57015
|
-
percentage: number().min(0).max(100),
|
|
57771
|
+
percentage: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
57016
57772
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
57017
57773
|
lastChangedAt: number()
|
|
57018
57774
|
});
|
|
@@ -57483,21 +58239,21 @@ var ClimateControlStatusSchema = object({
|
|
|
57483
58239
|
/** Single setpoint in Celsius. Used by single-target modes
|
|
57484
58240
|
* (heat / cool / dry). Null when the device is in a range mode
|
|
57485
58241
|
* (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
|
|
57486
|
-
target: number().nullable(),
|
|
58242
|
+
target: number().nullable().meta(readingMeta("temperature")),
|
|
57487
58243
|
/** Upper bound of the dual setpoint in Celsius. Populated only
|
|
57488
58244
|
* in `heat_cool` mode. */
|
|
57489
|
-
targetHigh: number().nullable(),
|
|
58245
|
+
targetHigh: number().nullable().meta(readingMeta("temperature")),
|
|
57490
58246
|
/** Lower bound of the dual setpoint in Celsius. Populated only
|
|
57491
58247
|
* in `heat_cool` mode. */
|
|
57492
|
-
targetLow: number().nullable(),
|
|
58248
|
+
targetLow: number().nullable().meta(readingMeta("temperature")),
|
|
57493
58249
|
/** Target relative humidity (0..100). Null when the device has no
|
|
57494
58250
|
* humidity control. */
|
|
57495
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
58251
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
57496
58252
|
/** Current measured temperature in Celsius. Read-only — pushed by
|
|
57497
58253
|
* the device's internal sensor. */
|
|
57498
|
-
currentTemp: number().nullable(),
|
|
58254
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
57499
58255
|
/** Current measured relative humidity (0..100). Read-only. */
|
|
57500
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
58256
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
57501
58257
|
/** Vertical louver swing on/off. Null when the device has no vertical
|
|
57502
58258
|
* swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
|
|
57503
58259
|
swingVertical: boolean().nullable(),
|
|
@@ -57505,11 +58261,11 @@ var ClimateControlStatusSchema = object({
|
|
|
57505
58261
|
* horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
|
|
57506
58262
|
swingHorizontal: boolean().nullable(),
|
|
57507
58263
|
/** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
57508
|
-
minTemp: number().nullable().optional(),
|
|
58264
|
+
minTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
57509
58265
|
/** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
57510
|
-
maxTemp: number().nullable().optional(),
|
|
58266
|
+
maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
57511
58267
|
/** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
|
|
57512
|
-
tempStep: number().positive().nullable().optional(),
|
|
58268
|
+
tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
|
|
57513
58269
|
/** Ms epoch when the slice was last updated (push or command). */
|
|
57514
58270
|
lastFetchedAt: number()
|
|
57515
58271
|
});
|
|
@@ -57651,17 +58407,17 @@ var climateControlCapability = {
|
|
|
57651
58407
|
* modes to expose in the picker without a slice fetch.
|
|
57652
58408
|
*/
|
|
57653
58409
|
var RgbTripletSchema = object({
|
|
57654
|
-
r: number().int().min(0).max(255),
|
|
57655
|
-
g: number().int().min(0).max(255),
|
|
57656
|
-
b: number().int().min(0).max(255)
|
|
58410
|
+
r: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
58411
|
+
g: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
58412
|
+
b: number().int().min(0).max(255).meta(readingMeta("none"))
|
|
57657
58413
|
});
|
|
57658
58414
|
var HsvTripletSchema = object({
|
|
57659
58415
|
/** Hue in degrees, 0..360. */
|
|
57660
|
-
h: number().min(0).max(360),
|
|
58416
|
+
h: number().min(0).max(360).meta(readingMeta("none")),
|
|
57661
58417
|
/** Saturation as 0..100 inclusive. */
|
|
57662
|
-
s: number().min(0).max(100),
|
|
58418
|
+
s: number().min(0).max(100).meta(readingMeta("none")),
|
|
57663
58419
|
/** Value/brightness as 0..100 inclusive. */
|
|
57664
|
-
v: number().min(0).max(100)
|
|
58420
|
+
v: number().min(0).max(100).meta(readingMeta("none"))
|
|
57665
58421
|
});
|
|
57666
58422
|
var ColorInputSchema = discriminatedUnion("mode", [
|
|
57667
58423
|
object({
|
|
@@ -57690,7 +58446,7 @@ var ColorStatusSchema = object({
|
|
|
57690
58446
|
/** Populated when `mode === 'hsv'`. */
|
|
57691
58447
|
hsv: HsvTripletSchema.optional(),
|
|
57692
58448
|
/** Populated when `mode === 'mired'`. */
|
|
57693
|
-
mireds: number().int().optional(),
|
|
58449
|
+
mireds: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
57694
58450
|
/** Ms epoch of the last operator-driven change. */
|
|
57695
58451
|
lastChangedAt: number()
|
|
57696
58452
|
});
|
|
@@ -57885,18 +58641,12 @@ _enum([
|
|
|
57885
58641
|
"empty",
|
|
57886
58642
|
"full"
|
|
57887
58643
|
]);
|
|
57888
|
-
/**
|
|
57889
|
-
|
|
57890
|
-
|
|
57891
|
-
"days",
|
|
57892
|
-
"hours",
|
|
57893
|
-
"cycles"
|
|
57894
|
-
]);
|
|
57895
|
-
/** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
|
|
57896
|
-
* days). `value` null = unknown (D393). */
|
|
58644
|
+
/** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
|
|
58645
|
+
var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
|
|
58646
|
+
/** Remaining life. `value` null = unknown (D393). Duration is seconds. */
|
|
57897
58647
|
var ConsumableRemainingSchema = object({
|
|
57898
|
-
value: number().min(0).nullable(),
|
|
57899
|
-
|
|
58648
|
+
value: number().min(0).nullable().meta(readingMeta("device")),
|
|
58649
|
+
kind: ConsumableRemainingKindSchema
|
|
57900
58650
|
});
|
|
57901
58651
|
/** A single consumable item. Either a continuous `level` (remaining
|
|
57902
58652
|
* life %, or fill % for a `supply` / `waste` item) or a discrete `status`
|
|
@@ -57909,7 +58659,7 @@ var ConsumableItemSchema = object({
|
|
|
57909
58659
|
/** Display name. */
|
|
57910
58660
|
label: string().min(1),
|
|
57911
58661
|
/** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
|
|
57912
|
-
level: number().min(0).max(100).nullable(),
|
|
58662
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
57913
58663
|
/** The provider's discrete verdict when known (binary mode). */
|
|
57914
58664
|
status: ConsumableStatusSchema.nullable(),
|
|
57915
58665
|
/** What the item is — decides whether low or full is bad. Absent = `wear`. */
|
|
@@ -57991,8 +58741,8 @@ var consumablesCustomize = {
|
|
|
57991
58741
|
transforms: [{ kind: "identity" }]
|
|
57992
58742
|
}],
|
|
57993
58743
|
literals: [{
|
|
57994
|
-
path: "remaining.
|
|
57995
|
-
values: [...
|
|
58744
|
+
path: "remaining.kind",
|
|
58745
|
+
values: [...ConsumableRemainingKindSchema.options]
|
|
57996
58746
|
}]
|
|
57997
58747
|
},
|
|
57998
58748
|
{
|
|
@@ -58121,7 +58871,6 @@ reset: method(object({
|
|
|
58121
58871
|
},
|
|
58122
58872
|
"remaining.value": {
|
|
58123
58873
|
kind: "gauge",
|
|
58124
|
-
unitFrom: "remaining.unit",
|
|
58125
58874
|
deadband: 1,
|
|
58126
58875
|
displayName: "Remaining"
|
|
58127
58876
|
}
|
|
@@ -58191,10 +58940,9 @@ var contactCapability = {
|
|
|
58191
58940
|
* (empty array for the other kinds). It lives in the slice because
|
|
58192
58941
|
* HA `select.*` entities may update their options at runtime.
|
|
58193
58942
|
*
|
|
58194
|
-
*
|
|
58195
|
-
*
|
|
58196
|
-
*
|
|
58197
|
-
* push — consistent with `options` and the sensor-unit approach.
|
|
58943
|
+
* Numeric `min` / `max` / `step` live in the slice and share the device
|
|
58944
|
+
* quantity (`SourceInfo.quantity`, D944). Display precision is a read-time
|
|
58945
|
+
* preference. A datetime control's shape is `SourceInfo.dateTimeShape`.
|
|
58198
58946
|
*/
|
|
58199
58947
|
var ControlStatusSchema = object({
|
|
58200
58948
|
kind: _enum([
|
|
@@ -58206,39 +58954,17 @@ var ControlStatusSchema = object({
|
|
|
58206
58954
|
/** Current value. Numeric kind → number; select/text/datetime → string.
|
|
58207
58955
|
* Datetime values are ISO 8601 strings (full timestamp, date-only,
|
|
58208
58956
|
* or time-only — HA accepts all three forms). */
|
|
58209
|
-
value: union([number(), string()]),
|
|
58957
|
+
value: union([number().meta(readingMeta("device")), string()]),
|
|
58210
58958
|
/** Acceptable values when `kind === 'select'`. Empty for other kinds. */
|
|
58211
58959
|
options: array(string()),
|
|
58212
58960
|
/** Ms epoch when the slice was last updated. */
|
|
58213
58961
|
lastChangedAt: number(),
|
|
58214
|
-
/**
|
|
58215
|
-
|
|
58216
|
-
|
|
58217
|
-
|
|
58218
|
-
/**
|
|
58219
|
-
|
|
58220
|
-
min: number().optional(),
|
|
58221
|
-
/** Maximum allowed value. Populated live from HA `attributes.max`.
|
|
58222
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58223
|
-
max: number().optional(),
|
|
58224
|
-
/** Step increment. Populated live from HA `attributes.step`.
|
|
58225
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58226
|
-
step: number().optional(),
|
|
58227
|
-
/** Suggested decimal places for numeric display. Populated live from HA
|
|
58228
|
-
* `attributes.suggested_display_precision`, or derived from `step` when the
|
|
58229
|
-
* HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
|
|
58230
|
-
* other kinds. Falls back to auto-formatting when absent. */
|
|
58231
|
-
precision: number().int().min(0).max(10).optional(),
|
|
58232
|
-
/** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
|
|
58233
|
-
* 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
|
|
58234
|
-
* (UI defaults to 'datetime'). Derived live from the device's HA domain
|
|
58235
|
-
* (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
|
|
58236
|
-
* `has_date`/`has_time` attributes on each push. */
|
|
58237
|
-
format: _enum([
|
|
58238
|
-
"date",
|
|
58239
|
-
"time",
|
|
58240
|
-
"datetime"
|
|
58241
|
-
]).optional()
|
|
58962
|
+
/** Minimum allowed value, in the device quantity's base unit. */
|
|
58963
|
+
min: number().optional().meta(readingMeta("device")),
|
|
58964
|
+
/** Maximum allowed value, in the device quantity's base unit. */
|
|
58965
|
+
max: number().optional().meta(readingMeta("device")),
|
|
58966
|
+
/** Step increment, in the device quantity's base unit. */
|
|
58967
|
+
step: number().optional().meta(readingMeta("device"))
|
|
58242
58968
|
});
|
|
58243
58969
|
var ControlSetValueInputSchema = discriminatedUnion("kind", [
|
|
58244
58970
|
object({
|
|
@@ -58304,9 +59030,9 @@ var CoverStatusSchema = object({
|
|
|
58304
59030
|
]),
|
|
58305
59031
|
/** 0 = fully closed, 100 = fully open. Null when the device has
|
|
58306
59032
|
* no intermediate position surface. */
|
|
58307
|
-
position: number().min(0).max(100).nullable(),
|
|
59033
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58308
59034
|
/** 0 = closed slats, 100 = open slats. Null when no tilt support. */
|
|
58309
|
-
tiltPosition: number().min(0).max(100).nullable(),
|
|
59035
|
+
tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58310
59036
|
/** Ms epoch when the slice was last updated. */
|
|
58311
59037
|
lastChangedAt: number()
|
|
58312
59038
|
});
|
|
@@ -58409,9 +59135,9 @@ var NormalizedRangeSchema$1 = object({
|
|
|
58409
59135
|
var DayNightStatusSchema = object({
|
|
58410
59136
|
mode: DayNightModeSchema,
|
|
58411
59137
|
/** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
|
|
58412
|
-
sensitivity: number().optional(),
|
|
59138
|
+
sensitivity: number().optional().meta(readingMeta("none")),
|
|
58413
59139
|
/** Delay before the IR-cut filter flips, in seconds. */
|
|
58414
|
-
switchDelaySec: number().optional(),
|
|
59140
|
+
switchDelaySec: number().optional().meta(readingMeta("duration")),
|
|
58415
59141
|
lastFetchedAt: number()
|
|
58416
59142
|
});
|
|
58417
59143
|
/**
|
|
@@ -58629,11 +59355,11 @@ var DishwasherStatusSchema = object({
|
|
|
58629
59355
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
58630
59356
|
availablePrograms: array(string()),
|
|
58631
59357
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
58632
|
-
remainingSeconds: number().int().nonnegative().nullable(),
|
|
59358
|
+
remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
58633
59359
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
58634
|
-
delayedStartSeconds: number().int().nonnegative().nullable(),
|
|
59360
|
+
delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
58635
59361
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
58636
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
59362
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58637
59363
|
/** True when the door is reported open; null when the door state is not known. */
|
|
58638
59364
|
doorOpen: boolean().nullable(),
|
|
58639
59365
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -58657,7 +59383,7 @@ var DishwasherStatusSchema = object({
|
|
|
58657
59383
|
/** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
|
|
58658
59384
|
machineCareDue: boolean().nullable(),
|
|
58659
59385
|
/** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
|
|
58660
|
-
machineCareRemainingRuns: number().int().nonnegative().nullable(),
|
|
59386
|
+
machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
|
|
58661
59387
|
/** Option states: null when the appliance does not expose the option or has not said. */
|
|
58662
59388
|
halfLoad: boolean().nullable(),
|
|
58663
59389
|
hygienePlus: boolean().nullable(),
|
|
@@ -58672,7 +59398,7 @@ var DishwasherStatusSchema = object({
|
|
|
58672
59398
|
/** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
|
|
58673
59399
|
activeEvents: array(string()),
|
|
58674
59400
|
/** Numeric error code when the appliance reports one; null otherwise. */
|
|
58675
|
-
errorCode: number().nullable(),
|
|
59401
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
58676
59402
|
/** Name of the raised alert-level event while the appliance is in error; null otherwise. */
|
|
58677
59403
|
errorLabel: string().nullable(),
|
|
58678
59404
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
@@ -58903,7 +59629,7 @@ var DoorbellStatusSchema = object({
|
|
|
58903
59629
|
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
58904
59630
|
lastPressedAt: number().nullable(),
|
|
58905
59631
|
/** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
|
|
58906
|
-
pressCountSinceStart: number()
|
|
59632
|
+
pressCountSinceStart: number().meta(readingMeta("count"))
|
|
58907
59633
|
});
|
|
58908
59634
|
var DoorbellPressEventSchema = object({
|
|
58909
59635
|
deviceId: number(),
|
|
@@ -58968,17 +59694,7 @@ onPressed: {
|
|
|
58968
59694
|
history: "none",
|
|
58969
59695
|
customize: doorbellCustomize
|
|
58970
59696
|
};
|
|
58971
|
-
|
|
58972
|
-
* Enum-state sensor — a string value picked from a finite option set.
|
|
58973
|
-
* Drives HA `sensor` entries with `state_class: enum` (HVAC action
|
|
58974
|
-
* states, weather conditions, contact-source channels, mode strings).
|
|
58975
|
-
* The option set is stable across observations; it is persisted in the
|
|
58976
|
-
* device config blob (via the control slice at adoption) and is NOT
|
|
58977
|
-
* stored in `sourceInfo`.
|
|
58978
|
-
*/
|
|
58979
|
-
/** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
|
|
58980
|
-
* enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
|
|
58981
|
-
var EnumSensorDateTimeFormatSchema = _enum([
|
|
59697
|
+
_enum([
|
|
58982
59698
|
"date",
|
|
58983
59699
|
"time",
|
|
58984
59700
|
"datetime"
|
|
@@ -58986,12 +59702,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
|
|
|
58986
59702
|
var EnumSensorStatusSchema = object({
|
|
58987
59703
|
value: string(),
|
|
58988
59704
|
/**
|
|
58989
|
-
* Set
|
|
58990
|
-
* as
|
|
58991
|
-
*
|
|
58992
|
-
* a time-only sensor → `'time'`.
|
|
59705
|
+
* Set when `value` is a timestamp rather than an enum state, so history
|
|
59706
|
+
* does not learn ISO strings as states. The display shape (date / time /
|
|
59707
|
+
* datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
|
|
58993
59708
|
*/
|
|
58994
|
-
|
|
59709
|
+
timestamp: boolean().optional(),
|
|
58995
59710
|
/** Ms epoch when the slice was last updated. */
|
|
58996
59711
|
lastFetchedAt: number()
|
|
58997
59712
|
});
|
|
@@ -59018,11 +59733,11 @@ var enumSensorCapability = {
|
|
|
59018
59733
|
* History (D850): the value as an `enum` series (an appliance's program or
|
|
59019
59734
|
* operation state, an HVAC action). The option set is per device, so the
|
|
59020
59735
|
* value list is LEARNED as values are first seen (bounded). A sensor with a
|
|
59021
|
-
* `
|
|
59736
|
+
* `timestamp` carries a clock, not a state, and is not recorded.
|
|
59022
59737
|
*/
|
|
59023
59738
|
history: { fields: { value: {
|
|
59024
59739
|
kind: "enum",
|
|
59025
|
-
skipWhenSet: "
|
|
59740
|
+
skipWhenSet: "timestamp"
|
|
59026
59741
|
} } },
|
|
59027
59742
|
/** Clock fields: written, but excluded from the compare that decides
|
|
59028
59743
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -59042,13 +59757,13 @@ var EventFireSchema = object({
|
|
|
59042
59757
|
deviceId: number(),
|
|
59043
59758
|
eventType: string(),
|
|
59044
59759
|
data: record(string(), unknown()).nullable(),
|
|
59045
|
-
timestamp: number(),
|
|
59046
|
-
seq: number()
|
|
59760
|
+
timestamp: number().meta(readingMeta("none")),
|
|
59761
|
+
seq: number().meta(readingMeta("count"))
|
|
59047
59762
|
});
|
|
59048
59763
|
var EventEmitterStatusSchema = object({
|
|
59049
59764
|
eventTypes: array(string()),
|
|
59050
59765
|
lastEvent: EventFireSchema.nullable(),
|
|
59051
|
-
eventCountSinceStart: number()
|
|
59766
|
+
eventCountSinceStart: number().meta(readingMeta("count"))
|
|
59052
59767
|
});
|
|
59053
59768
|
var eventEmitterCapability = {
|
|
59054
59769
|
name: "event-emitter",
|
|
@@ -59461,10 +60176,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
59461
60176
|
var FanControlStatusSchema = object({
|
|
59462
60177
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
59463
60178
|
* speed surface (single-speed fan). */
|
|
59464
|
-
percentage: number().min(0).max(100).nullable(),
|
|
60179
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59465
60180
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
59466
60181
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
59467
|
-
percentageStep: number().positive().optional(),
|
|
60182
|
+
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
59468
60183
|
/** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
|
|
59469
60184
|
* — empty string when no preset is active or supported. */
|
|
59470
60185
|
preset: string(),
|
|
@@ -59585,7 +60300,7 @@ var FeatureProbeStatusSchema = object({
|
|
|
59585
60300
|
/** Camera/firmware model string. `null` when the firmware doesn't expose it. */
|
|
59586
60301
|
model: string().nullable(),
|
|
59587
60302
|
/** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
|
|
59588
|
-
channelCount: number().nullable(),
|
|
60303
|
+
channelCount: number().nullable().meta(readingMeta("count")),
|
|
59589
60304
|
/**
|
|
59590
60305
|
* Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
|
|
59591
60306
|
* completes — drivers' `getAccessoryChildren()` should treat zero as
|
|
@@ -59708,9 +60423,151 @@ var gasCapability = {
|
|
|
59708
60423
|
* whether persisting is worth a SQLite commit. */
|
|
59709
60424
|
volatileStateFields: ["lastChangedAt"]
|
|
59710
60425
|
};
|
|
60426
|
+
var GroupCommandsSchema = object({
|
|
60427
|
+
/** One row per member that has been commanded. A later command replaces that member's row. */
|
|
60428
|
+
results: array(object({
|
|
60429
|
+
deviceId: number().int().nonnegative(),
|
|
60430
|
+
cap: string().min(1),
|
|
60431
|
+
method: string().min(1),
|
|
60432
|
+
ok: boolean(),
|
|
60433
|
+
error: string().optional(),
|
|
60434
|
+
/** Ms epoch of the attempt. */
|
|
60435
|
+
at: number()
|
|
60436
|
+
})) });
|
|
60437
|
+
var groupCommandsCapability = {
|
|
60438
|
+
name: "group-commands",
|
|
60439
|
+
scope: "device",
|
|
60440
|
+
deviceNative: true,
|
|
60441
|
+
mode: "singleton",
|
|
60442
|
+
deviceTypes: [
|
|
60443
|
+
DeviceType.Switch,
|
|
60444
|
+
DeviceType.Light,
|
|
60445
|
+
DeviceType.Fan,
|
|
60446
|
+
DeviceType.Cover,
|
|
60447
|
+
DeviceType.Valve,
|
|
60448
|
+
DeviceType.Lock,
|
|
60449
|
+
DeviceType.MediaPlayer,
|
|
60450
|
+
DeviceType.Thermostat,
|
|
60451
|
+
DeviceType.Climate
|
|
60452
|
+
],
|
|
60453
|
+
methods: {},
|
|
60454
|
+
status: {
|
|
60455
|
+
schema: GroupCommandsSchema,
|
|
60456
|
+
kind: "push"
|
|
60457
|
+
},
|
|
60458
|
+
runtimeState: GroupCommandsSchema,
|
|
60459
|
+
/**
|
|
60460
|
+
* Runtime-state durability: **session** — the last attempt. A restored
|
|
60461
|
+
* failure would outlive the command that caused it.
|
|
60462
|
+
*
|
|
60463
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
60464
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
60465
|
+
*/
|
|
60466
|
+
durability: "session",
|
|
60467
|
+
history: "none"
|
|
60468
|
+
};
|
|
60469
|
+
/**
|
|
60470
|
+
* Whether a helper actuator group counts as active (D944): at least one
|
|
60471
|
+
* included member is, or every included member is. What "active" means is the
|
|
60472
|
+
* type's: a cover is open, a lock is locked, a media player is playing, a
|
|
60473
|
+
* climate device is not off. A group of lights, switches and fans reports
|
|
60474
|
+
* `switch.on` instead; this cap is for the types whose own state is not a
|
|
60475
|
+
* yes/no. Unknown members are left out (`group-status` counts them) and an
|
|
60476
|
+
* empty included set takes the device offline, never `active: false`.
|
|
60477
|
+
*/
|
|
60478
|
+
var GroupActiveSchema = object({
|
|
60479
|
+
active: boolean().meta({ label: "Active" }),
|
|
60480
|
+
/** Ms epoch when the slice was last updated. */
|
|
60481
|
+
lastChangedAt: number()
|
|
60482
|
+
});
|
|
60483
|
+
var groupActiveCapability = {
|
|
60484
|
+
name: "group-active",
|
|
60485
|
+
scope: "device",
|
|
60486
|
+
deviceNative: true,
|
|
60487
|
+
mode: "singleton",
|
|
60488
|
+
deviceTypes: [
|
|
60489
|
+
DeviceType.Cover,
|
|
60490
|
+
DeviceType.Valve,
|
|
60491
|
+
DeviceType.Lock,
|
|
60492
|
+
DeviceType.MediaPlayer,
|
|
60493
|
+
DeviceType.Climate,
|
|
60494
|
+
DeviceType.Thermostat
|
|
60495
|
+
],
|
|
60496
|
+
methods: {},
|
|
60497
|
+
status: {
|
|
60498
|
+
schema: GroupActiveSchema,
|
|
60499
|
+
kind: "push"
|
|
60500
|
+
},
|
|
60501
|
+
runtimeState: GroupActiveSchema,
|
|
60502
|
+
/**
|
|
60503
|
+
* Runtime-state durability: **session** — recomputed from the members on the
|
|
60504
|
+
* next source event; a restored value would outlive the members it named.
|
|
60505
|
+
*
|
|
60506
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
60507
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
60508
|
+
*/
|
|
60509
|
+
durability: "session",
|
|
60510
|
+
history: "none"
|
|
60511
|
+
};
|
|
60512
|
+
/**
|
|
60513
|
+
* How many members of a helper group answered (D944). Counts are integers,
|
|
60514
|
+
* not measurements: zero included is a real count, never a stand-in reading.
|
|
60515
|
+
* The reading itself stays on the output capability. History is none — the
|
|
60516
|
+
* series that later statistics read is the output cap's own history block.
|
|
60517
|
+
*/
|
|
60518
|
+
var GroupStatusSchema = object({
|
|
60519
|
+
/** Members whose current value entered the aggregate. */
|
|
60520
|
+
included: number().int().nonnegative().meta(readingMeta("count", {
|
|
60521
|
+
precision: 0,
|
|
60522
|
+
label: "Reporting"
|
|
60523
|
+
})),
|
|
60524
|
+
/** Members left out: offline, unknown, or not a finite reading (D393). */
|
|
60525
|
+
excluded: number().int().nonnegative().meta(readingMeta("count", {
|
|
60526
|
+
precision: 0,
|
|
60527
|
+
label: "Excluded"
|
|
60528
|
+
})),
|
|
60529
|
+
/** `included` + `excluded`. */
|
|
60530
|
+
total: number().int().nonnegative().meta(readingMeta("count", {
|
|
60531
|
+
precision: 0,
|
|
60532
|
+
label: "Members"
|
|
60533
|
+
}))
|
|
60534
|
+
});
|
|
60535
|
+
var groupStatusCapability = {
|
|
60536
|
+
name: "group-status",
|
|
60537
|
+
scope: "device",
|
|
60538
|
+
deviceNative: true,
|
|
60539
|
+
mode: "singleton",
|
|
60540
|
+
deviceTypes: [
|
|
60541
|
+
DeviceType.Sensor,
|
|
60542
|
+
DeviceType.Switch,
|
|
60543
|
+
DeviceType.Light,
|
|
60544
|
+
DeviceType.Fan,
|
|
60545
|
+
DeviceType.Cover,
|
|
60546
|
+
DeviceType.Valve,
|
|
60547
|
+
DeviceType.Lock,
|
|
60548
|
+
DeviceType.MediaPlayer,
|
|
60549
|
+
DeviceType.Thermostat,
|
|
60550
|
+
DeviceType.Climate
|
|
60551
|
+
],
|
|
60552
|
+
methods: {},
|
|
60553
|
+
status: {
|
|
60554
|
+
schema: GroupStatusSchema,
|
|
60555
|
+
kind: "push"
|
|
60556
|
+
},
|
|
60557
|
+
runtimeState: GroupStatusSchema,
|
|
60558
|
+
/**
|
|
60559
|
+
* Runtime-state durability: **session** — recomputed from members on the
|
|
60560
|
+
* next source event; a restored count would outlive the readings it named.
|
|
60561
|
+
*
|
|
60562
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
60563
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
60564
|
+
*/
|
|
60565
|
+
durability: "session",
|
|
60566
|
+
history: "none"
|
|
60567
|
+
};
|
|
59711
60568
|
var HeatingValveStatusSchema = object({
|
|
59712
60569
|
/** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
|
|
59713
|
-
position: number().min(0).max(100).nullable(),
|
|
60570
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59714
60571
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
59715
60572
|
lastFetchedAt: number()
|
|
59716
60573
|
});
|
|
@@ -59753,9 +60610,9 @@ var HumidifierStatusSchema = object({
|
|
|
59753
60610
|
/** Whether the humidifier is currently on. */
|
|
59754
60611
|
on: boolean(),
|
|
59755
60612
|
/** Current measured relative humidity (0..100). Null when not reported. */
|
|
59756
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
60613
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
59757
60614
|
/** Target relative humidity (0..100). Null when no setpoint surface. */
|
|
59758
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
60615
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
59759
60616
|
/** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
|
|
59760
60617
|
* device has no mode surface. */
|
|
59761
60618
|
mode: string().nullable(),
|
|
@@ -59765,9 +60622,9 @@ var HumidifierStatusSchema = object({
|
|
|
59765
60622
|
* `idle` / `off`). Null when the device doesn't report it. */
|
|
59766
60623
|
action: string().nullable(),
|
|
59767
60624
|
/** HA `min_humidity` attribute. Null → UI uses 0. */
|
|
59768
|
-
minHumidity: number().nullable(),
|
|
60625
|
+
minHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
59769
60626
|
/** HA `max_humidity` attribute. Null → UI uses 100. */
|
|
59770
|
-
maxHumidity: number().nullable(),
|
|
60627
|
+
maxHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
59771
60628
|
/** Ms epoch when the slice was last updated. */
|
|
59772
60629
|
lastChangedAt: number()
|
|
59773
60630
|
});
|
|
@@ -59837,22 +60694,13 @@ var humidifierCapability = {
|
|
|
59837
60694
|
* `sourceInfo`.
|
|
59838
60695
|
*/
|
|
59839
60696
|
var HumiditySensorStatusSchema = object({
|
|
59840
|
-
/** Current relative humidity, 0..100. */
|
|
59841
|
-
percent: number().min(0).max(100).meta({
|
|
59842
|
-
|
|
60697
|
+
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
60698
|
+
percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
60699
|
+
precision: 0,
|
|
59843
60700
|
label: "Humidity"
|
|
59844
|
-
}),
|
|
60701
|
+
})),
|
|
59845
60702
|
/** Ms epoch when the slice was last updated. */
|
|
59846
|
-
lastFetchedAt: number()
|
|
59847
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
59848
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
59849
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
59850
|
-
unit: string().optional(),
|
|
59851
|
-
/** Suggested decimal places for numeric display.
|
|
59852
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
59853
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
59854
|
-
* auto-formatting when absent. */
|
|
59855
|
-
precision: number().int().min(0).max(10).optional()
|
|
60703
|
+
lastFetchedAt: number()
|
|
59856
60704
|
});
|
|
59857
60705
|
var humiditySensorCapability = {
|
|
59858
60706
|
name: "humidity-sensor",
|
|
@@ -59883,12 +60731,7 @@ var humiditySensorCapability = {
|
|
|
59883
60731
|
} } },
|
|
59884
60732
|
/** Clock fields: written, but excluded from the compare that decides
|
|
59885
60733
|
* whether persisting is worth a SQLite commit. */
|
|
59886
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
59887
|
-
customize: {
|
|
59888
|
-
version: 1,
|
|
59889
|
-
advanced: "derive",
|
|
59890
|
-
targets: []
|
|
59891
|
-
}
|
|
60734
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
59892
60735
|
};
|
|
59893
60736
|
/**
|
|
59894
60737
|
* Image display cap. Models a single still image exposed by an integration —
|
|
@@ -59909,7 +60752,7 @@ var ImageStatusSchema = object({
|
|
|
59909
60752
|
* entity exposes no `entity_picture` (yet). */
|
|
59910
60753
|
url: string().nullable(),
|
|
59911
60754
|
/** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
|
|
59912
|
-
lastUpdated: number().nullable()
|
|
60755
|
+
lastUpdated: number().nullable().meta(readingMeta("none"))
|
|
59913
60756
|
});
|
|
59914
60757
|
var imageCapability = {
|
|
59915
60758
|
name: "image",
|
|
@@ -59997,19 +60840,19 @@ var NormalizedRangeSchema = object({
|
|
|
59997
60840
|
*/
|
|
59998
60841
|
var ImageSettingsStatusSchema = object({
|
|
59999
60842
|
/** Normalized 0–100. */
|
|
60000
|
-
brightness: number().optional(),
|
|
60843
|
+
brightness: number().optional().meta(readingMeta("none")),
|
|
60001
60844
|
/** Normalized 0–100. */
|
|
60002
|
-
contrast: number().optional(),
|
|
60845
|
+
contrast: number().optional().meta(readingMeta("none")),
|
|
60003
60846
|
/** Normalized 0–100. */
|
|
60004
|
-
saturation: number().optional(),
|
|
60847
|
+
saturation: number().optional().meta(readingMeta("none")),
|
|
60005
60848
|
/** Normalized 0–100. */
|
|
60006
|
-
sharpness: number().optional(),
|
|
60849
|
+
sharpness: number().optional().meta(readingMeta("none")),
|
|
60007
60850
|
mirror: boolean().optional(),
|
|
60008
60851
|
flip: boolean().optional(),
|
|
60009
60852
|
rotate: ImageRotateSchema.optional(),
|
|
60010
60853
|
whiteBalance: WhiteBalanceModeSchema.optional(),
|
|
60011
60854
|
/** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
|
|
60012
|
-
warmth: number().optional(),
|
|
60855
|
+
warmth: number().optional().meta(readingMeta("none")),
|
|
60013
60856
|
exposureMode: ExposureModeSchema.optional(),
|
|
60014
60857
|
backlightMode: BacklightModeSchema.optional(),
|
|
60015
60858
|
lastFetchedAt: number()
|
|
@@ -60288,10 +61131,10 @@ var IntercomAbilitySchema = object({
|
|
|
60288
61131
|
/** Codecs accepted by firmware, in preference order. Driver-authored strings. */
|
|
60289
61132
|
codecs: array(string()),
|
|
60290
61133
|
/** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
|
|
60291
|
-
sampleRate: number(),
|
|
61134
|
+
sampleRate: number().meta(readingMeta("none")),
|
|
60292
61135
|
duplex: _enum(["half", "full"]),
|
|
60293
61136
|
/** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
|
|
60294
|
-
maxBacklogMs: number()
|
|
61137
|
+
maxBacklogMs: number().meta(readingMeta("duration-ms"))
|
|
60295
61138
|
});
|
|
60296
61139
|
var IntercomStatusSchema = object({
|
|
60297
61140
|
/** True while a talk session is open. */
|
|
@@ -60518,13 +61361,13 @@ var LawnMowerControlStatusSchema = object({
|
|
|
60518
61361
|
activity: LawnMowerActivitySchema,
|
|
60519
61362
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
60520
61363
|
* reading. */
|
|
60521
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
61364
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60522
61365
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
60523
61366
|
* task is active / progress is unavailable. */
|
|
60524
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
61367
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60525
61368
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
60526
61369
|
* or null when unknown. */
|
|
60527
|
-
currentCode: number().nullable(),
|
|
61370
|
+
currentCode: number().nullable().meta(readingMeta("none")),
|
|
60528
61371
|
/** Human label for {@link currentCode}, or null when undecodable. */
|
|
60529
61372
|
currentCodeLabel: string().nullable(),
|
|
60530
61373
|
/** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
|
|
@@ -60977,7 +61820,14 @@ var lockControlCapability = {
|
|
|
60977
61820
|
}),
|
|
60978
61821
|
unlock: method(object({
|
|
60979
61822
|
deviceId: number().int().nonnegative(),
|
|
60980
|
-
code: string().min(1).optional()
|
|
61823
|
+
code: string().min(1).optional(),
|
|
61824
|
+
/**
|
|
61825
|
+
* Honoured by a helper lock GROUP only (D944): unlocking several doors
|
|
61826
|
+
* with one call is refused unless the caller says it was confirmed. A
|
|
61827
|
+
* single lock ignores it. The refusal lives in the callee, so no
|
|
61828
|
+
* automation or API caller reaches the doors by skipping a dialog.
|
|
61829
|
+
*/
|
|
61830
|
+
confirm: boolean().optional()
|
|
60981
61831
|
}), _void(), {
|
|
60982
61832
|
kind: "mutation",
|
|
60983
61833
|
auth: "protected"
|
|
@@ -61014,6 +61864,44 @@ var lockControlCapability = {
|
|
|
61014
61864
|
volatileStateFields: ["lastChangedAt"]
|
|
61015
61865
|
};
|
|
61016
61866
|
/**
|
|
61867
|
+
* A monotonic total between resets (D944). Energy uses `power-meter.kwhTotal`
|
|
61868
|
+
* so dashboards keep one series; every other instantaneous reading lands here.
|
|
61869
|
+
* `value` is the time integral in the source quantity's accumulated base
|
|
61870
|
+
* (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
|
|
61871
|
+
*/
|
|
61872
|
+
var MeterStatusSchema = object({
|
|
61873
|
+
value: number().nullable().meta(readingMeta("device", { label: "Total" })),
|
|
61874
|
+
lastFetchedAt: number()
|
|
61875
|
+
});
|
|
61876
|
+
var meterCapability = {
|
|
61877
|
+
name: "meter",
|
|
61878
|
+
scope: "device",
|
|
61879
|
+
deviceNative: true,
|
|
61880
|
+
mode: "singleton",
|
|
61881
|
+
deviceTypes: [DeviceType.Sensor],
|
|
61882
|
+
methods: {},
|
|
61883
|
+
status: {
|
|
61884
|
+
schema: MeterStatusSchema,
|
|
61885
|
+
kind: "push"
|
|
61886
|
+
},
|
|
61887
|
+
runtimeState: MeterStatusSchema,
|
|
61888
|
+
/**
|
|
61889
|
+
* Runtime-state durability: **restored** — the total is a cache of the
|
|
61890
|
+
* history integral. A restart shows it only until the history pass
|
|
61891
|
+
* recomputes the open period; it is not an in-memory buffer.
|
|
61892
|
+
*
|
|
61893
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
61894
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
61895
|
+
*/
|
|
61896
|
+
durability: "restored",
|
|
61897
|
+
history: { fields: { value: {
|
|
61898
|
+
kind: "counter",
|
|
61899
|
+
deadband: .001,
|
|
61900
|
+
minIntervalMs: 6e4
|
|
61901
|
+
} } },
|
|
61902
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
61903
|
+
};
|
|
61904
|
+
/**
|
|
61017
61905
|
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
61018
61906
|
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
61019
61907
|
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
@@ -61057,7 +61945,7 @@ var MediaPlayerStatusSchema = object({
|
|
|
61057
61945
|
state: MediaPlayerStateSchema,
|
|
61058
61946
|
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
61059
61947
|
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
61060
|
-
volumeLevel: number().min(0).max(100).nullable(),
|
|
61948
|
+
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61061
61949
|
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
61062
61950
|
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
61063
61951
|
isMuted: boolean().nullable(),
|
|
@@ -61070,10 +61958,10 @@ var MediaPlayerStatusSchema = object({
|
|
|
61070
61958
|
currentMedia: MediaInfoSchema.nullable(),
|
|
61071
61959
|
/** Current playback position in ms. Null when not seekable or
|
|
61072
61960
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
61073
|
-
positionMs: number().int().nonnegative().nullable(),
|
|
61961
|
+
positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61074
61962
|
/** Total duration of the current media in ms. Null when unknown
|
|
61075
61963
|
* (live stream) or nothing is playing. */
|
|
61076
|
-
durationMs: number().int().nonnegative().nullable(),
|
|
61964
|
+
durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61077
61965
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
61078
61966
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
61079
61967
|
shuffle: boolean().nullable(),
|
|
@@ -61382,7 +62270,7 @@ var motionCustomize = {
|
|
|
61382
62270
|
eventName: "onMotionChanged"
|
|
61383
62271
|
},
|
|
61384
62272
|
deviceTypes: [DeviceType.Camera],
|
|
61385
|
-
apply: "
|
|
62273
|
+
apply: "add",
|
|
61386
62274
|
applyAuthority: {
|
|
61387
62275
|
kind: "camera-sensors",
|
|
61388
62276
|
timeoutField: "motionCooldownMs"
|
|
@@ -61454,7 +62342,7 @@ var MotionStatusSchema = object({
|
|
|
61454
62342
|
* it, every provider seeded it at boot, and every session left it
|
|
61455
62343
|
* `detected: true` after its detach.
|
|
61456
62344
|
*/
|
|
61457
|
-
autoClearAfterMs: number().nullable()
|
|
62345
|
+
autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
|
|
61458
62346
|
});
|
|
61459
62347
|
/**
|
|
61460
62348
|
* Payload of `motion.onMotionChanged` event + the corresponding bus
|
|
@@ -61491,12 +62379,18 @@ var motionCapability = {
|
|
|
61491
62379
|
methods: {
|
|
61492
62380
|
/**
|
|
61493
62381
|
* Pull the current motion state synchronously. Convenience shortcut
|
|
61494
|
-
* for consumers that don't need the full status object
|
|
61495
|
-
*
|
|
61496
|
-
* `getStatus` once the auto-injected status surface lands in every
|
|
61497
|
-
* consumer.
|
|
62382
|
+
* for consumers that don't need the full status object. A composed
|
|
62383
|
+
* group answers from its slice (D944): unknown throws, it is not off.
|
|
61498
62384
|
*/
|
|
61499
62385
|
isDetected: method(object({ deviceId: number() }), boolean()) },
|
|
62386
|
+
/**
|
|
62387
|
+
* A composed motion group serves `isDetected` from `detected` (D944).
|
|
62388
|
+
* Without this the cap stays uncomposable (`has-commands`).
|
|
62389
|
+
*/
|
|
62390
|
+
composedMethods: { isDetected: {
|
|
62391
|
+
kind: "slice",
|
|
62392
|
+
field: "detected"
|
|
62393
|
+
} },
|
|
61500
62394
|
events: {
|
|
61501
62395
|
/**
|
|
61502
62396
|
* Fires every time the runner transitions a camera between
|
|
@@ -61636,7 +62530,7 @@ var MotionZoneRegionSchema = object({
|
|
|
61636
62530
|
* the grid region(s). */
|
|
61637
62531
|
var MotionZoneStatusSchema = object({
|
|
61638
62532
|
enabled: boolean(),
|
|
61639
|
-
sensitivity: number(),
|
|
62533
|
+
sensitivity: number().meta(readingMeta("none")),
|
|
61640
62534
|
/** Grid region(s). Today exactly one `grid` shape. */
|
|
61641
62535
|
regions: array(MotionZoneRegionSchema),
|
|
61642
62536
|
lastFetchedAt: number()
|
|
@@ -62006,7 +62900,7 @@ var NavigationStatusSchema = object({
|
|
|
62006
62900
|
following: boolean(),
|
|
62007
62901
|
flash: boolean(),
|
|
62008
62902
|
lightMode: NavigationLightModeSchema,
|
|
62009
|
-
lightLevel: number().min(40).max(100),
|
|
62903
|
+
lightLevel: number().min(40).max(100).meta(readingMeta("none")),
|
|
62010
62904
|
/** Ms epoch when the slice was last updated. */
|
|
62011
62905
|
lastChangedAt: number()
|
|
62012
62906
|
});
|
|
@@ -62140,13 +63034,13 @@ var NetworkLinkStatusSchema = object({
|
|
|
62140
63034
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
62141
63035
|
* SKIP a null rather than coerce it.
|
|
62142
63036
|
*/
|
|
62143
|
-
signalPercent: number().min(0).max(100).nullable(),
|
|
63037
|
+
signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62144
63038
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
62145
|
-
rssiDbm: number().optional(),
|
|
63039
|
+
rssiDbm: number().optional().meta(readingMeta("signal-level")),
|
|
62146
63040
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
62147
63041
|
ssid: string().optional(),
|
|
62148
63042
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
62149
|
-
lastUpdated: number()
|
|
63043
|
+
lastUpdated: number().meta(readingMeta("none"))
|
|
62150
63044
|
});
|
|
62151
63045
|
var networkLinkCapability = {
|
|
62152
63046
|
name: "network-link",
|
|
@@ -62531,7 +63425,7 @@ var NotifierStatusSchema = object({
|
|
|
62531
63425
|
lastError: string().nullable(),
|
|
62532
63426
|
/** Number of deliveries currently buffered server-side (rate-limited
|
|
62533
63427
|
* notifiers may queue). 0 when the provider sends synchronously. */
|
|
62534
|
-
queueDepth: number().int().nonnegative(),
|
|
63428
|
+
queueDepth: number().int().nonnegative().meta(readingMeta("count")),
|
|
62535
63429
|
/** Per-feature capability matrix — authoritative source for the
|
|
62536
63430
|
* compose-form field gating. */
|
|
62537
63431
|
supports: NotifierSupportsSchema
|
|
@@ -62623,11 +63517,9 @@ onSent: { data: object({
|
|
|
62623
63517
|
* cap fits (HA `sensor` whose `device_class` we don't have a typed cap
|
|
62624
63518
|
* for: water flow, distance, weight, frequency, signal strength, …).
|
|
62625
63519
|
*
|
|
62626
|
-
* The
|
|
62627
|
-
*
|
|
62628
|
-
*
|
|
62629
|
-
* The UI reads them directly from the slice — they are the single source
|
|
62630
|
-
* of truth for generic numeric rendering and do NOT live on `sourceInfo`.
|
|
63520
|
+
* The quantity is not on this slice. It is chosen when the device is
|
|
63521
|
+
* adopted and stored once on `SourceInfo.quantity` (D944). `value` is in
|
|
63522
|
+
* that quantity's base unit. `null` is a refused sample (D393).
|
|
62631
63523
|
*
|
|
62632
63524
|
* Prefer the typed alternatives (`temperature-sensor`, `humidity-sensor`,
|
|
62633
63525
|
* `ambient-light-sensor`, `pressure-sensor`, `power-meter`,
|
|
@@ -62636,15 +63528,8 @@ onSent: { data: object({
|
|
|
62636
63528
|
* numeric values land here.
|
|
62637
63529
|
*/
|
|
62638
63530
|
var NumericSensorStatusSchema = object({
|
|
62639
|
-
|
|
62640
|
-
|
|
62641
|
-
* Populated live from the upstream source on each state push.
|
|
62642
|
-
* Absent for unitless sensors. */
|
|
62643
|
-
unit: string().optional(),
|
|
62644
|
-
/** Suggested decimal places for numeric display.
|
|
62645
|
-
* Populated live from the upstream source when provided.
|
|
62646
|
-
* Falls back to auto-formatting when absent. */
|
|
62647
|
-
precision: number().int().min(0).max(10).optional(),
|
|
63531
|
+
/** Reading in the device quantity's base unit. `null` when refused (D393). */
|
|
63532
|
+
value: number().nullable().meta(readingMeta("device")),
|
|
62648
63533
|
/** Ms epoch when the slice was last updated. */
|
|
62649
63534
|
lastFetchedAt: number()
|
|
62650
63535
|
});
|
|
@@ -62668,11 +63553,11 @@ var numericSensorCapability = {
|
|
|
62668
63553
|
*/
|
|
62669
63554
|
durability: "restored",
|
|
62670
63555
|
/**
|
|
62671
|
-
* History (D850): recorded series. The unit is
|
|
63556
|
+
* History (D850): recorded series. The unit is the device quantity's base,
|
|
63557
|
+
* resolved when the series is read — the cap has no single unit.
|
|
62672
63558
|
*/
|
|
62673
63559
|
history: { fields: { value: {
|
|
62674
63560
|
kind: "gauge",
|
|
62675
|
-
unitFrom: "unit",
|
|
62676
63561
|
minIntervalMs: 3e4
|
|
62677
63562
|
} } },
|
|
62678
63563
|
/** Clock fields: written, but excluded from the compare that decides
|
|
@@ -62964,23 +63849,26 @@ var PetFeederStatusSchema = object({
|
|
|
62964
63849
|
/** Food currently in the bowl (grams). Null when the device has not
|
|
62965
63850
|
* reported a reading yet. On dual-hopper models this is the combined
|
|
62966
63851
|
* bowl reading; per-hopper levels live in `food1`/`food2`. */
|
|
62967
|
-
foodLevel: number().nullable(),
|
|
63852
|
+
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
62968
63853
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
62969
63854
|
* single-hopper models. */
|
|
62970
|
-
food1: number().nullable(),
|
|
63855
|
+
food1: number().nullable().meta(readingMeta("none")),
|
|
62971
63856
|
/** Hopper-2 food level (grams) on dual-hopper feeders; null on
|
|
62972
63857
|
* single-hopper models. */
|
|
62973
|
-
food2: number().nullable(),
|
|
63858
|
+
food2: number().nullable().meta(readingMeta("none")),
|
|
62974
63859
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
62975
63860
|
* (`device_class: problem`, on = low). True when the bowl is empty /
|
|
62976
63861
|
* below the feeder's low threshold. */
|
|
62977
63862
|
lowFood: boolean(),
|
|
62978
63863
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
62979
63864
|
* device has no battery reading. */
|
|
62980
|
-
batteryPower: number().min(0).max(100).nullable(),
|
|
62981
|
-
/**
|
|
62982
|
-
*
|
|
62983
|
-
|
|
63865
|
+
batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63866
|
+
/**
|
|
63867
|
+
* Desiccant life remaining, stored in seconds (duration base, D944).
|
|
63868
|
+
* Providers convert from the days the feeder reports. Null when the
|
|
63869
|
+
* model has no desiccant sensor.
|
|
63870
|
+
*/
|
|
63871
|
+
desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
|
|
62984
63872
|
/** True while a feed is in progress. */
|
|
62985
63873
|
feeding: boolean(),
|
|
62986
63874
|
/** Decoded connectivity / power status (HA petkit device-status enum).
|
|
@@ -62991,7 +63879,7 @@ var PetFeederStatusSchema = object({
|
|
|
62991
63879
|
* with `errorCode` for consumers that want the raw integer. */
|
|
62992
63880
|
error: string().nullable(),
|
|
62993
63881
|
/** Raw device error code (0 / null = no error). */
|
|
62994
|
-
errorCode: number().nullable(),
|
|
63882
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
62995
63883
|
/** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
|
|
62996
63884
|
isDualHopper: boolean(),
|
|
62997
63885
|
/** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
|
|
@@ -63001,7 +63889,7 @@ var PetFeederStatusSchema = object({
|
|
|
63001
63889
|
/** Play a chime when dispensing. */
|
|
63002
63890
|
feedSound: boolean(),
|
|
63003
63891
|
/** Speaker / prompt volume level (device-scaled integer). */
|
|
63004
|
-
volume: number(),
|
|
63892
|
+
volume: number().meta(readingMeta("none")),
|
|
63005
63893
|
/** Ms epoch when the slice was last refreshed from the cloud. */
|
|
63006
63894
|
lastFetchedAt: number()
|
|
63007
63895
|
});
|
|
@@ -63120,7 +64008,7 @@ var petFeederCapability = {
|
|
|
63120
64008
|
},
|
|
63121
64009
|
desiccantLeftDays: {
|
|
63122
64010
|
kind: "gauge",
|
|
63123
|
-
unit: "
|
|
64011
|
+
unit: "s"
|
|
63124
64012
|
}
|
|
63125
64013
|
} }
|
|
63126
64014
|
};
|
|
@@ -63521,43 +64409,88 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
63521
64409
|
*/
|
|
63522
64410
|
var PowerMeterStatusSchema = object({
|
|
63523
64411
|
/** Instantaneous power draw in watts. */
|
|
63524
|
-
watts: number().optional().meta({
|
|
63525
|
-
unit: "W",
|
|
63526
|
-
label: "Power"
|
|
63527
|
-
}),
|
|
64412
|
+
watts: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
63528
64413
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
63529
|
-
kwhTotal: number().optional().meta({
|
|
63530
|
-
unit: "kWh",
|
|
63531
|
-
label: "Energy"
|
|
63532
|
-
}),
|
|
64414
|
+
kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
63533
64415
|
/** Voltage in volts. */
|
|
63534
|
-
volts: number().optional(),
|
|
64416
|
+
volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
63535
64417
|
/** Current in amperes. */
|
|
63536
|
-
amps: number().optional(),
|
|
63537
|
-
/** Apparent power in volt-amperes
|
|
63538
|
-
va: number().optional(),
|
|
64418
|
+
amps: number().optional().meta(readingMeta("current", { label: "Current" })),
|
|
64419
|
+
/** Apparent power in volt-amperes. */
|
|
64420
|
+
va: number().optional().meta(readingMeta("apparent-power")),
|
|
63539
64421
|
/** Power factor (0..1) if reported. */
|
|
63540
|
-
powerFactor: number().min(0).max(1).optional(),
|
|
64422
|
+
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
63541
64423
|
/** Ms epoch when the slice was last updated. */
|
|
63542
|
-
lastFetchedAt: number()
|
|
63543
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
63544
|
-
* `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
|
|
63545
|
-
* Each upstream `sensor.*` entity surfaces ONE device_class, so one
|
|
63546
|
-
* unit per slice is unambiguous. The UI prefers this over the role's
|
|
63547
|
-
* canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
|
|
63548
|
-
unit: string().optional(),
|
|
63549
|
-
/** Suggested decimal places for numeric display.
|
|
63550
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
63551
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
63552
|
-
* auto-formatting when absent. */
|
|
63553
|
-
precision: number().int().min(0).max(10).optional()
|
|
64424
|
+
lastFetchedAt: number()
|
|
63554
64425
|
});
|
|
64426
|
+
/**
|
|
64427
|
+
* Loads that draw metered mains power (D951). The reading is a feature of the
|
|
64428
|
+
* device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
|
|
64429
|
+
* vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
|
|
64430
|
+
* meter or a standalone energy monitor IS the device. Cover, lock, valve,
|
|
64431
|
+
* camera and the like are left out: their draw is not what anyone meters.
|
|
64432
|
+
*/
|
|
64433
|
+
var POWER_METER_DEVICE_TYPES = [
|
|
64434
|
+
DeviceType.Sensor,
|
|
64435
|
+
DeviceType.Switch,
|
|
64436
|
+
DeviceType.Light,
|
|
64437
|
+
DeviceType.Fan,
|
|
64438
|
+
DeviceType.Climate,
|
|
64439
|
+
DeviceType.Thermostat,
|
|
64440
|
+
DeviceType.Humidifier,
|
|
64441
|
+
DeviceType.WaterHeater,
|
|
64442
|
+
DeviceType.MediaPlayer,
|
|
64443
|
+
DeviceType.Washer,
|
|
64444
|
+
DeviceType.Dryer,
|
|
64445
|
+
DeviceType.Dishwasher,
|
|
64446
|
+
DeviceType.Vacuum,
|
|
64447
|
+
DeviceType.LawnMower,
|
|
64448
|
+
DeviceType.PetFeeder
|
|
64449
|
+
];
|
|
64450
|
+
/** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
|
|
64451
|
+
var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
|
|
64452
|
+
/** One scalar target per reading; each is sourced from a device of its own (D951). */
|
|
64453
|
+
function powerTarget(id, unit) {
|
|
64454
|
+
return {
|
|
64455
|
+
id,
|
|
64456
|
+
cardinality: "scalar",
|
|
64457
|
+
vocabulary: {
|
|
64458
|
+
cap: "power-meter",
|
|
64459
|
+
stateField: id
|
|
64460
|
+
},
|
|
64461
|
+
deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
|
|
64462
|
+
apply: "add",
|
|
64463
|
+
path: id,
|
|
64464
|
+
sources: [{
|
|
64465
|
+
kind: "numeric",
|
|
64466
|
+
requiredUnit: unit,
|
|
64467
|
+
transforms: [{ kind: "identity" }]
|
|
64468
|
+
}],
|
|
64469
|
+
companions: [{
|
|
64470
|
+
kind: "clock",
|
|
64471
|
+
path: "lastFetchedAt",
|
|
64472
|
+
when: "always"
|
|
64473
|
+
}],
|
|
64474
|
+
sourceReport: { status: "assignment-status" },
|
|
64475
|
+
advancedExpression: true
|
|
64476
|
+
};
|
|
64477
|
+
}
|
|
64478
|
+
/** "Power and energy" extension: watts, energy, voltage and current from other devices (D951). */
|
|
64479
|
+
var powerMeterCustomize = {
|
|
64480
|
+
version: 1,
|
|
64481
|
+
targets: [
|
|
64482
|
+
powerTarget("watts", "W"),
|
|
64483
|
+
powerTarget("kwhTotal", "kWh"),
|
|
64484
|
+
powerTarget("volts", "V"),
|
|
64485
|
+
powerTarget("amps", "A")
|
|
64486
|
+
]
|
|
64487
|
+
};
|
|
63555
64488
|
var powerMeterCapability = {
|
|
63556
64489
|
name: "power-meter",
|
|
63557
64490
|
scope: "device",
|
|
63558
64491
|
deviceNative: true,
|
|
63559
64492
|
mode: "singleton",
|
|
63560
|
-
deviceTypes:
|
|
64493
|
+
deviceTypes: POWER_METER_DEVICE_TYPES,
|
|
63561
64494
|
methods: {},
|
|
63562
64495
|
status: {
|
|
63563
64496
|
schema: PowerMeterStatusSchema,
|
|
@@ -63615,11 +64548,7 @@ var powerMeterCapability = {
|
|
|
63615
64548
|
/** Clock fields: written, but excluded from the compare that decides
|
|
63616
64549
|
* whether persisting is worth a SQLite commit. */
|
|
63617
64550
|
volatileStateFields: ["lastFetchedAt"],
|
|
63618
|
-
customize:
|
|
63619
|
-
version: 1,
|
|
63620
|
-
advanced: "derive",
|
|
63621
|
-
targets: []
|
|
63622
|
-
}
|
|
64551
|
+
customize: powerMeterCustomize
|
|
63623
64552
|
};
|
|
63624
64553
|
/**
|
|
63625
64554
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
@@ -63639,11 +64568,11 @@ var powerMeterCapability = {
|
|
|
63639
64568
|
*/
|
|
63640
64569
|
var GpsLocationSchema = object({
|
|
63641
64570
|
/** Latitude in decimal degrees, -90..90. */
|
|
63642
|
-
latitude: number().min(-90).max(90),
|
|
64571
|
+
latitude: number().min(-90).max(90).meta(readingMeta("angle")),
|
|
63643
64572
|
/** Longitude in decimal degrees, -180..180. */
|
|
63644
|
-
longitude: number().min(-180).max(180),
|
|
64573
|
+
longitude: number().min(-180).max(180).meta(readingMeta("angle")),
|
|
63645
64574
|
/** Reported accuracy in meters (lower = better). */
|
|
63646
|
-
accuracyMeters: number().nonnegative()
|
|
64575
|
+
accuracyMeters: number().nonnegative().meta(readingMeta("length"))
|
|
63647
64576
|
});
|
|
63648
64577
|
var PresenceStatusSchema = object({
|
|
63649
64578
|
/** `home` / `not_home` / any user-defined zone name. */
|
|
@@ -63655,7 +64584,7 @@ var PresenceStatusSchema = object({
|
|
|
63655
64584
|
gps: GpsLocationSchema.nullable(),
|
|
63656
64585
|
/** Optional battery level of the tracking device (0..100). Null
|
|
63657
64586
|
* when not reported. */
|
|
63658
|
-
batteryPercent: number().min(0).max(100).nullable(),
|
|
64587
|
+
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63659
64588
|
/** Ms epoch when the slice was last updated. */
|
|
63660
64589
|
lastChangedAt: number()
|
|
63661
64590
|
});
|
|
@@ -63705,19 +64634,10 @@ var presenceCapability = {
|
|
|
63705
64634
|
* `sourceInfo`.
|
|
63706
64635
|
*/
|
|
63707
64636
|
var PressureSensorStatusSchema = object({
|
|
63708
|
-
/** Current pressure in hPa. */
|
|
63709
|
-
hpa: number(),
|
|
64637
|
+
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
64638
|
+
hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
63710
64639
|
/** Ms epoch when the slice was last updated. */
|
|
63711
|
-
lastFetchedAt: number()
|
|
63712
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
63713
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
63714
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
63715
|
-
unit: string().optional(),
|
|
63716
|
-
/** Suggested decimal places for numeric display.
|
|
63717
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
63718
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
63719
|
-
* auto-formatting when absent. */
|
|
63720
|
-
precision: number().int().min(0).max(10).optional()
|
|
64640
|
+
lastFetchedAt: number()
|
|
63721
64641
|
});
|
|
63722
64642
|
var pressureSensorCapability = {
|
|
63723
64643
|
name: "pressure-sensor",
|
|
@@ -64131,8 +65051,8 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
|
|
|
64131
65051
|
}), _void(), { kind: "mutation" });
|
|
64132
65052
|
var PtzAutotrackSettingsSchema = object({
|
|
64133
65053
|
targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
|
|
64134
|
-
stopDelaySeconds: number().int().min(0).max(300),
|
|
64135
|
-
disappearDelaySeconds: number().int().min(0).max(300)
|
|
65054
|
+
stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
|
|
65055
|
+
disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
|
|
64136
65056
|
});
|
|
64137
65057
|
/**
|
|
64138
65058
|
* One option the camera advertises as a valid `targetType`. Drivers
|
|
@@ -64286,7 +65206,7 @@ var RunningStateStatusSchema = object({
|
|
|
64286
65206
|
/** null = unknown — never coerced to false (D393). */
|
|
64287
65207
|
running: boolean().nullable(),
|
|
64288
65208
|
/** Epoch ms the current boolean began; null while `running` is null. */
|
|
64289
|
-
since: number().nullable(),
|
|
65209
|
+
since: number().nullable().meta(readingMeta("none")),
|
|
64290
65210
|
source: RunningStateSourceSchema.meta({ label: "Source" }),
|
|
64291
65211
|
phase: string().nullable(),
|
|
64292
65212
|
/** null until a reading has been published (D393). */
|
|
@@ -64301,7 +65221,6 @@ var RUNNING_DEVICE_TYPES = Object.values(DeviceType);
|
|
|
64301
65221
|
/** Generic Customize target for the running boolean (D936). */
|
|
64302
65222
|
var runningCustomize = {
|
|
64303
65223
|
version: 1,
|
|
64304
|
-
advanced: "derive",
|
|
64305
65224
|
targets: [{
|
|
64306
65225
|
id: "running",
|
|
64307
65226
|
cardinality: "scalar",
|
|
@@ -64310,6 +65229,8 @@ var runningCustomize = {
|
|
|
64310
65229
|
stateField: "running"
|
|
64311
65230
|
},
|
|
64312
65231
|
deviceTypes: [
|
|
65232
|
+
DeviceType.Washer,
|
|
65233
|
+
DeviceType.Dryer,
|
|
64313
65234
|
DeviceType.Dishwasher,
|
|
64314
65235
|
DeviceType.Vacuum,
|
|
64315
65236
|
DeviceType.LawnMower,
|
|
@@ -64321,7 +65242,7 @@ var runningCustomize = {
|
|
|
64321
65242
|
DeviceType.Thermostat,
|
|
64322
65243
|
DeviceType.PetFeeder
|
|
64323
65244
|
],
|
|
64324
|
-
apply: "add
|
|
65245
|
+
apply: "add",
|
|
64325
65246
|
path: "running",
|
|
64326
65247
|
sources: [
|
|
64327
65248
|
{
|
|
@@ -65585,9 +66506,9 @@ var RecordTriggerSchema = _enum([
|
|
|
65585
66506
|
*/
|
|
65586
66507
|
var RecordWindowSchema = object({
|
|
65587
66508
|
trigger: RecordTriggerSchema,
|
|
65588
|
-
day: number().int().min(0).max(6),
|
|
65589
|
-
startMinute: number().int().min(0).max(1439),
|
|
65590
|
-
endMinute: number().int().min(1).max(1440)
|
|
66509
|
+
day: number().int().min(0).max(6).meta(readingMeta("none")),
|
|
66510
|
+
startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
|
|
66511
|
+
endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
|
|
65591
66512
|
});
|
|
65592
66513
|
/** Status of one physical volume, as the camera itself describes it. */
|
|
65593
66514
|
var OnboardStorageVolumeSchema = object({
|
|
@@ -65663,10 +66584,10 @@ var RecordingOnboardStatusSchema = object({
|
|
|
65663
66584
|
/** Master "record to the card at all" switch. */
|
|
65664
66585
|
enabled: boolean().nullable(),
|
|
65665
66586
|
overwriteWhenFull: boolean().nullable(),
|
|
65666
|
-
preRecordSec: number().nullable(),
|
|
65667
|
-
postRecordSec: number().nullable(),
|
|
66587
|
+
preRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
66588
|
+
postRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
65668
66589
|
/** Length of one recorded file, in minutes. */
|
|
65669
|
-
segmentMinutes: number().nullable(),
|
|
66590
|
+
segmentMinutes: number().nullable().meta(readingMeta("none")),
|
|
65670
66591
|
/** The primary track's weekly windows, flattened. */
|
|
65671
66592
|
windows: array(RecordWindowSchema),
|
|
65672
66593
|
/**
|
|
@@ -65682,7 +66603,7 @@ var RecordingOnboardStatusSchema = object({
|
|
|
65682
66603
|
* provider reporting a non-zero count also reports the schedule as not
|
|
65683
66604
|
* writable.
|
|
65684
66605
|
*/
|
|
65685
|
-
unreadableWindows: number(),
|
|
66606
|
+
unreadableWindows: number().meta(readingMeta("count")),
|
|
65686
66607
|
/**
|
|
65687
66608
|
* The camera is scheduled to record and has NO usable storage.
|
|
65688
66609
|
*
|
|
@@ -66527,7 +67448,7 @@ var CamStreamDescriptorSchema = object({
|
|
|
66527
67448
|
url: string().optional(),
|
|
66528
67449
|
codec: string().optional(),
|
|
66529
67450
|
resolution: CamStreamResolutionSchema.optional(),
|
|
66530
|
-
fps: number().positive().optional(),
|
|
67451
|
+
fps: number().positive().optional().meta(readingMeta("rate")),
|
|
66531
67452
|
label: string().optional(),
|
|
66532
67453
|
/** Device-level features (e.g. `battery-operated`) — drives broker policy. */
|
|
66533
67454
|
deviceFeatures: array(string()).optional(),
|
|
@@ -66614,18 +67535,18 @@ var StreamProfileSchema = _enum([
|
|
|
66614
67535
|
]);
|
|
66615
67536
|
/** The current encoding config of one stream profile. */
|
|
66616
67537
|
var StreamProfileConfigSchema = object({
|
|
66617
|
-
width: number(),
|
|
66618
|
-
height: number(),
|
|
67538
|
+
width: number().meta(readingMeta("none")),
|
|
67539
|
+
height: number().meta(readingMeta("none")),
|
|
66619
67540
|
codec: _enum(["h264", "h265"]),
|
|
66620
|
-
framerate: number(),
|
|
66621
|
-
bitrate: number(),
|
|
67541
|
+
framerate: number().meta(readingMeta("rate")),
|
|
67542
|
+
bitrate: number().meta(readingMeta("bitrate")),
|
|
66622
67543
|
bitrateMode: _enum(["vbr", "cbr"]).optional(),
|
|
66623
67544
|
encoderProfile: _enum([
|
|
66624
67545
|
"high",
|
|
66625
67546
|
"main",
|
|
66626
67547
|
"baseline"
|
|
66627
67548
|
]).optional(),
|
|
66628
|
-
gop: number().optional(),
|
|
67549
|
+
gop: number().optional().meta(readingMeta("none")),
|
|
66629
67550
|
/**
|
|
66630
67551
|
* Whether THIS profile currently carries an audio track. READ-ONLY here.
|
|
66631
67552
|
*
|
|
@@ -66799,7 +67720,8 @@ var switchCapability = {
|
|
|
66799
67720
|
deviceTypes: [
|
|
66800
67721
|
DeviceType.Switch,
|
|
66801
67722
|
DeviceType.Siren,
|
|
66802
|
-
DeviceType.Light
|
|
67723
|
+
DeviceType.Light,
|
|
67724
|
+
DeviceType.Fan
|
|
66803
67725
|
],
|
|
66804
67726
|
methods: { setState: method(object({
|
|
66805
67727
|
deviceId: number(),
|
|
@@ -66846,12 +67768,7 @@ var switchCapability = {
|
|
|
66846
67768
|
label: "On",
|
|
66847
67769
|
kind: "boolean"
|
|
66848
67770
|
}
|
|
66849
|
-
}] }
|
|
66850
|
-
customize: {
|
|
66851
|
-
version: 1,
|
|
66852
|
-
advanced: "derive",
|
|
66853
|
-
targets: []
|
|
66854
|
-
}
|
|
67771
|
+
}] }
|
|
66855
67772
|
};
|
|
66856
67773
|
/** C0 controls, DEL and the C1 range: none of them belongs in a name. */
|
|
66857
67774
|
function hasControlCharacter(value) {
|
|
@@ -67434,35 +68351,21 @@ var tamperCapability = {
|
|
|
67434
68351
|
* entries with `device_class: temperature` and any future native
|
|
67435
68352
|
* thermometer.
|
|
67436
68353
|
*
|
|
67437
|
-
*
|
|
67438
|
-
*
|
|
67439
|
-
* (climate-control's `currentTemp`, energy analytics) can compose
|
|
67440
|
-
* without per-source unit fixups. The canonical display unit (`°C`) is
|
|
67441
|
-
* a descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
67442
|
-
* `sourceInfo`.
|
|
68354
|
+
* The number is Celsius (D944). Providers convert on ingest. A failed
|
|
68355
|
+
* conversion is `null`, never a guess. Display unit is a read-time preference.
|
|
67443
68356
|
*
|
|
67444
68357
|
* Status `lastFetchedAt` lets staleness-aware consumers detect a
|
|
67445
68358
|
* frozen feed (provider hung) distinct from a "temperature hasn't
|
|
67446
68359
|
* changed" steady state.
|
|
67447
68360
|
*/
|
|
67448
68361
|
var TemperatureSensorStatusSchema = object({
|
|
67449
|
-
/** Current temperature in Celsius. */
|
|
67450
|
-
celsius: number().meta({
|
|
67451
|
-
|
|
68362
|
+
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
68363
|
+
celsius: number().nullable().meta(readingMeta("temperature", {
|
|
68364
|
+
precision: 1,
|
|
67452
68365
|
label: "Temperature"
|
|
67453
|
-
}),
|
|
68366
|
+
})),
|
|
67454
68367
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
67455
|
-
lastFetchedAt: number()
|
|
67456
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
67457
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
67458
|
-
* role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
|
|
67459
|
-
* Absent → the UI falls back to the role's canonical unit. */
|
|
67460
|
-
unit: string().optional(),
|
|
67461
|
-
/** Suggested decimal places for numeric display.
|
|
67462
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
67463
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
67464
|
-
* auto-formatting when absent. */
|
|
67465
|
-
precision: number().int().min(0).max(10).optional()
|
|
68368
|
+
lastFetchedAt: number()
|
|
67466
68369
|
});
|
|
67467
68370
|
var temperatureSensorCapability = {
|
|
67468
68371
|
name: "temperature-sensor",
|
|
@@ -67493,12 +68396,7 @@ var temperatureSensorCapability = {
|
|
|
67493
68396
|
} } },
|
|
67494
68397
|
/** Clock fields: written, but excluded from the compare that decides
|
|
67495
68398
|
* whether persisting is worth a SQLite commit. */
|
|
67496
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
67497
|
-
customize: {
|
|
67498
|
-
version: 1,
|
|
67499
|
-
advanced: "derive",
|
|
67500
|
-
targets: []
|
|
67501
|
-
}
|
|
68399
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
67502
68400
|
};
|
|
67503
68401
|
/**
|
|
67504
68402
|
* toast — system-scoped singleton capability that streams toast
|
|
@@ -67781,16 +68679,16 @@ var VacuumControlStatusSchema = object({
|
|
|
67781
68679
|
]),
|
|
67782
68680
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
67783
68681
|
* reading. */
|
|
67784
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
68682
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
67785
68683
|
/** Current fan-speed token (provider-verbatim). Null when unknown or
|
|
67786
68684
|
* the vacuum has no speed control. */
|
|
67787
68685
|
fanSpeed: string().nullable(),
|
|
67788
68686
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
67789
68687
|
availableFanSpeeds: array(string()),
|
|
67790
68688
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
67791
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
68689
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
67792
68690
|
/** Current error code (0 / null = no error). */
|
|
67793
|
-
errorCode: number().nullable(),
|
|
68691
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
67794
68692
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
67795
68693
|
errorLabel: string().nullable(),
|
|
67796
68694
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -67868,7 +68766,7 @@ var ValveStatusSchema = object({
|
|
|
67868
68766
|
]),
|
|
67869
68767
|
/** 0 = fully closed, 100 = fully open. Null when the device has no
|
|
67870
68768
|
* intermediate position surface. */
|
|
67871
|
-
position: number().min(0).max(100).nullable(),
|
|
68769
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
67872
68770
|
/** Ms epoch when the slice was last updated. */
|
|
67873
68771
|
lastChangedAt: number()
|
|
67874
68772
|
});
|
|
@@ -67976,9 +68874,9 @@ var vibrationCapability = {
|
|
|
67976
68874
|
*/
|
|
67977
68875
|
var WaterHeaterStatusSchema = object({
|
|
67978
68876
|
/** Current measured temperature. Null when not reported. */
|
|
67979
|
-
currentTemp: number().nullable(),
|
|
68877
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
67980
68878
|
/** Target temperature setpoint. Null when no setpoint surface. */
|
|
67981
|
-
targetTemp: number().nullable(),
|
|
68879
|
+
targetTemp: number().nullable().meta(readingMeta("temperature")),
|
|
67982
68880
|
/** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
|
|
67983
68881
|
* `heat_pump` / `high_demand` / `performance` / `off`). Null when the
|
|
67984
68882
|
* device reports an unknown state. */
|
|
@@ -67989,9 +68887,9 @@ var WaterHeaterStatusSchema = object({
|
|
|
67989
68887
|
* has no away surface. */
|
|
67990
68888
|
away: boolean().nullable(),
|
|
67991
68889
|
/** HA `min_temp` attribute. Null when not reported. */
|
|
67992
|
-
minTemp: number().nullable(),
|
|
68890
|
+
minTemp: number().nullable().meta(readingMeta("temperature")),
|
|
67993
68891
|
/** HA `max_temp` attribute. Null when not reported. */
|
|
67994
|
-
maxTemp: number().nullable(),
|
|
68892
|
+
maxTemp: number().nullable().meta(readingMeta("temperature")),
|
|
67995
68893
|
/** Ms epoch when the slice was last updated. */
|
|
67996
68894
|
lastChangedAt: number()
|
|
67997
68895
|
});
|
|
@@ -68076,25 +68974,18 @@ var waterHeaterCapability = {
|
|
|
68076
68974
|
* Forecast deferred — current conditions only for v1.
|
|
68077
68975
|
*/
|
|
68078
68976
|
var WeatherStatusSchema = object({
|
|
68079
|
-
/** Verbatim
|
|
68080
|
-
* when no condition has been reported yet. */
|
|
68977
|
+
/** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
|
|
68081
68978
|
condition: string().nullable(),
|
|
68082
|
-
/** Current temperature in
|
|
68083
|
-
temperature: number().nullable(),
|
|
68084
|
-
/** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
|
|
68085
|
-
temperatureUnit: string().nullable(),
|
|
68979
|
+
/** Current temperature in Celsius. Null when not provided or refused. */
|
|
68980
|
+
temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
|
|
68086
68981
|
/** Relative humidity (0..100). Null when not provided. */
|
|
68087
|
-
humidity: number().min(0).max(100).nullable(),
|
|
68088
|
-
/** Barometric pressure in
|
|
68089
|
-
pressure: number().nullable(),
|
|
68090
|
-
/**
|
|
68091
|
-
|
|
68092
|
-
/** Wind speed in the reported unit. Null when not provided. */
|
|
68093
|
-
windSpeed: number().nullable(),
|
|
68094
|
-
/** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
|
|
68095
|
-
windSpeedUnit: string().nullable(),
|
|
68982
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
68983
|
+
/** Barometric pressure in hPa. Null when not provided or refused. */
|
|
68984
|
+
pressure: number().nullable().meta(readingMeta("pressure")),
|
|
68985
|
+
/** Wind speed in metres per second. Null when not provided or refused. */
|
|
68986
|
+
windSpeed: number().nullable().meta(readingMeta("speed")),
|
|
68096
68987
|
/** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
|
|
68097
|
-
windBearing: number().nullable(),
|
|
68988
|
+
windBearing: number().nullable().meta(readingMeta("angle")),
|
|
68098
68989
|
/** Ms epoch when the slice was last updated. */
|
|
68099
68990
|
lastFetchedAt: number()
|
|
68100
68991
|
});
|
|
@@ -68122,12 +69013,12 @@ var weatherCapability = {
|
|
|
68122
69013
|
*/
|
|
68123
69014
|
durability: "session",
|
|
68124
69015
|
/**
|
|
68125
|
-
* History (D850): recorded series.
|
|
69016
|
+
* History (D850): recorded series. Numbers are the quantity base (D944).
|
|
68126
69017
|
*/
|
|
68127
69018
|
history: { fields: {
|
|
68128
69019
|
temperature: {
|
|
68129
69020
|
kind: "gauge",
|
|
68130
|
-
|
|
69021
|
+
unit: "°C",
|
|
68131
69022
|
deadband: .2
|
|
68132
69023
|
},
|
|
68133
69024
|
humidity: {
|
|
@@ -68137,12 +69028,12 @@ var weatherCapability = {
|
|
|
68137
69028
|
},
|
|
68138
69029
|
pressure: {
|
|
68139
69030
|
kind: "gauge",
|
|
68140
|
-
|
|
69031
|
+
unit: "hPa",
|
|
68141
69032
|
deadband: .5
|
|
68142
69033
|
},
|
|
68143
69034
|
windSpeed: {
|
|
68144
69035
|
kind: "gauge",
|
|
68145
|
-
|
|
69036
|
+
unit: "m/s",
|
|
68146
69037
|
deadband: .5
|
|
68147
69038
|
}
|
|
68148
69039
|
} }
|
|
@@ -69043,8 +69934,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
69043
69934
|
kind: "mutation",
|
|
69044
69935
|
auth: "admin"
|
|
69045
69936
|
});
|
|
69937
|
+
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
69046
69938
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
69047
|
-
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;
|
|
69939
|
+
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;
|
|
69048
69940
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
69049
69941
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
69050
69942
|
new Set(CAP_NAMES);
|
|
@@ -69373,6 +70265,9 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
69373
70265
|
featureProbe: featureProbeCapability,
|
|
69374
70266
|
flood: floodCapability,
|
|
69375
70267
|
gas: gasCapability,
|
|
70268
|
+
groupActive: groupActiveCapability,
|
|
70269
|
+
groupCommands: groupCommandsCapability,
|
|
70270
|
+
groupStatus: groupStatusCapability,
|
|
69376
70271
|
heatingValve: heatingValveCapability,
|
|
69377
70272
|
humidifier: humidifierCapability,
|
|
69378
70273
|
humiditySensor: humiditySensorCapability,
|
|
@@ -69382,6 +70277,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
69382
70277
|
lawnMowerControl: lawnMowerControlCapability,
|
|
69383
70278
|
lockControl: lockControlCapability,
|
|
69384
70279
|
mediaPlayer: mediaPlayerCapability,
|
|
70280
|
+
meter: meterCapability,
|
|
69385
70281
|
motion: motionCapability,
|
|
69386
70282
|
motionTrigger: motionTriggerCapability,
|
|
69387
70283
|
motionZones: motionZonesCapability,
|
|
@@ -70641,7 +71537,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
70641
71537
|
return toDeviceSummary(device, this.addonId);
|
|
70642
71538
|
}
|
|
70643
71539
|
};
|
|
70644
|
-
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;
|
|
71540
|
+
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;
|
|
70645
71541
|
new Set(Object.values(DeviceType));
|
|
70646
71542
|
Object.freeze({
|
|
70647
71543
|
"accessories.getStatus": {
|
|
@@ -70749,6 +71645,13 @@ Object.freeze({
|
|
|
70749
71645
|
access: "view",
|
|
70750
71646
|
role: "admin"
|
|
70751
71647
|
},
|
|
71648
|
+
"addons.getPackageDocs": {
|
|
71649
|
+
capName: "addons",
|
|
71650
|
+
capScope: "system",
|
|
71651
|
+
addonId: null,
|
|
71652
|
+
access: "view",
|
|
71653
|
+
role: "admin"
|
|
71654
|
+
},
|
|
70752
71655
|
"addons.getVersions": {
|
|
70753
71656
|
capName: "addons",
|
|
70754
71657
|
capScope: "system",
|
|
@@ -73857,6 +74760,27 @@ Object.freeze({
|
|
|
73857
74760
|
access: "view",
|
|
73858
74761
|
role: "viewer"
|
|
73859
74762
|
},
|
|
74763
|
+
"groupActive.getStatus": {
|
|
74764
|
+
capName: "group-active",
|
|
74765
|
+
capScope: "device",
|
|
74766
|
+
addonId: null,
|
|
74767
|
+
access: "view",
|
|
74768
|
+
role: "viewer"
|
|
74769
|
+
},
|
|
74770
|
+
"groupCommands.getStatus": {
|
|
74771
|
+
capName: "group-commands",
|
|
74772
|
+
capScope: "device",
|
|
74773
|
+
addonId: null,
|
|
74774
|
+
access: "view",
|
|
74775
|
+
role: "viewer"
|
|
74776
|
+
},
|
|
74777
|
+
"groupStatus.getStatus": {
|
|
74778
|
+
capName: "group-status",
|
|
74779
|
+
capScope: "device",
|
|
74780
|
+
addonId: null,
|
|
74781
|
+
access: "view",
|
|
74782
|
+
role: "viewer"
|
|
74783
|
+
},
|
|
73860
74784
|
"heatingValve.getStatus": {
|
|
73861
74785
|
capName: "heating-valve",
|
|
73862
74786
|
capScope: "device",
|
|
@@ -74809,6 +75733,13 @@ Object.freeze({
|
|
|
74809
75733
|
access: "create",
|
|
74810
75734
|
role: "admin"
|
|
74811
75735
|
},
|
|
75736
|
+
"meter.getStatus": {
|
|
75737
|
+
capName: "meter",
|
|
75738
|
+
capScope: "device",
|
|
75739
|
+
addonId: null,
|
|
75740
|
+
access: "view",
|
|
75741
|
+
role: "viewer"
|
|
75742
|
+
},
|
|
74812
75743
|
"metricsProvider.collectSnapshot": {
|
|
74813
75744
|
capName: "metrics-provider",
|
|
74814
75745
|
capScope: "system",
|
|
@@ -82907,7 +83838,39 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
82907
83838
|
phrase: t("When {device} finishes", "Quando {device} finisce"),
|
|
82908
83839
|
icon: "square"
|
|
82909
83840
|
}
|
|
82910
|
-
}
|
|
83841
|
+
},
|
|
83842
|
+
overrides: { byType: {
|
|
83843
|
+
[DeviceType.Washer]: {
|
|
83844
|
+
state: { running: {
|
|
83845
|
+
whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
|
|
83846
|
+
whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
|
|
83847
|
+
} },
|
|
83848
|
+
events: {
|
|
83849
|
+
onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
|
|
83850
|
+
onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
|
|
83851
|
+
}
|
|
83852
|
+
},
|
|
83853
|
+
[DeviceType.Dryer]: {
|
|
83854
|
+
state: { running: {
|
|
83855
|
+
whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
|
|
83856
|
+
whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
|
|
83857
|
+
} },
|
|
83858
|
+
events: {
|
|
83859
|
+
onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
|
|
83860
|
+
onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
|
|
83861
|
+
}
|
|
83862
|
+
},
|
|
83863
|
+
[DeviceType.Dishwasher]: {
|
|
83864
|
+
state: { running: {
|
|
83865
|
+
whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
|
|
83866
|
+
whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
|
|
83867
|
+
} },
|
|
83868
|
+
events: {
|
|
83869
|
+
onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
|
|
83870
|
+
onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
|
|
83871
|
+
}
|
|
83872
|
+
}
|
|
83873
|
+
} }
|
|
82911
83874
|
},
|
|
82912
83875
|
update: {
|
|
82913
83876
|
scope: "device",
|
|
@@ -83679,6 +84642,41 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83679
84642
|
icon: "flame"
|
|
83680
84643
|
}]
|
|
83681
84644
|
},
|
|
84645
|
+
"group-active": {
|
|
84646
|
+
scope: "device",
|
|
84647
|
+
noun: t("Group state", "Stato del gruppo")
|
|
84648
|
+
},
|
|
84649
|
+
"group-commands": {
|
|
84650
|
+
scope: "device",
|
|
84651
|
+
noun: t("Group command", "Comando di gruppo")
|
|
84652
|
+
},
|
|
84653
|
+
"group-status": {
|
|
84654
|
+
scope: "device",
|
|
84655
|
+
noun: t("Group", "Gruppo"),
|
|
84656
|
+
state: [
|
|
84657
|
+
{
|
|
84658
|
+
kind: "numeric",
|
|
84659
|
+
field: "included",
|
|
84660
|
+
quantity: t("the members reporting", "i membri che rispondono"),
|
|
84661
|
+
label: t("Reporting", "Rispondono"),
|
|
84662
|
+
icon: "users"
|
|
84663
|
+
},
|
|
84664
|
+
{
|
|
84665
|
+
kind: "numeric",
|
|
84666
|
+
field: "excluded",
|
|
84667
|
+
quantity: t("the members left out", "i membri esclusi"),
|
|
84668
|
+
label: t("Excluded", "Esclusi"),
|
|
84669
|
+
icon: "users"
|
|
84670
|
+
},
|
|
84671
|
+
{
|
|
84672
|
+
kind: "numeric",
|
|
84673
|
+
field: "total",
|
|
84674
|
+
quantity: t("the member count", "il numero di membri"),
|
|
84675
|
+
label: t("Members", "Membri"),
|
|
84676
|
+
icon: "sigma"
|
|
84677
|
+
}
|
|
84678
|
+
]
|
|
84679
|
+
},
|
|
83682
84680
|
"humidity-sensor": {
|
|
83683
84681
|
scope: "device",
|
|
83684
84682
|
noun: t("Humidity", "Umidità"),
|
|
@@ -83704,6 +84702,17 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83704
84702
|
}],
|
|
83705
84703
|
events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
|
|
83706
84704
|
},
|
|
84705
|
+
meter: {
|
|
84706
|
+
scope: "device",
|
|
84707
|
+
noun: t("Meter", "Contatore"),
|
|
84708
|
+
state: [{
|
|
84709
|
+
kind: "numeric",
|
|
84710
|
+
field: "value",
|
|
84711
|
+
quantity: t("the total", "il totale"),
|
|
84712
|
+
label: t("Total", "Totale"),
|
|
84713
|
+
icon: "gauge"
|
|
84714
|
+
}]
|
|
84715
|
+
},
|
|
83707
84716
|
"numeric-sensor": {
|
|
83708
84717
|
scope: "device",
|
|
83709
84718
|
noun: t("Sensor", "Sensore"),
|
|
@@ -83712,7 +84721,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83712
84721
|
field: "value",
|
|
83713
84722
|
quantity: t("the reading", "il valore"),
|
|
83714
84723
|
label: t("Reading", "Valore"),
|
|
83715
|
-
unitField: "unit",
|
|
83716
84724
|
icon: "gauge"
|
|
83717
84725
|
}]
|
|
83718
84726
|
},
|
|
@@ -83848,7 +84856,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83848
84856
|
field: "temperature",
|
|
83849
84857
|
quantity: t("the outdoor temperature", "la temperatura esterna"),
|
|
83850
84858
|
label: t("Temperature", "Temperatura"),
|
|
83851
|
-
|
|
84859
|
+
unit: "°C",
|
|
83852
84860
|
icon: "thermometer"
|
|
83853
84861
|
},
|
|
83854
84862
|
{
|
|
@@ -83864,7 +84872,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83864
84872
|
field: "windSpeed",
|
|
83865
84873
|
quantity: t("the wind speed", "la velocità del vento"),
|
|
83866
84874
|
label: t("Wind", "Vento"),
|
|
83867
|
-
|
|
84875
|
+
unit: "m/s",
|
|
83868
84876
|
icon: "wind"
|
|
83869
84877
|
},
|
|
83870
84878
|
{
|
|
@@ -83872,7 +84880,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83872
84880
|
field: "pressure",
|
|
83873
84881
|
quantity: t("the air pressure", "la pressione atmosferica"),
|
|
83874
84882
|
label: t("Pressure", "Pressione"),
|
|
83875
|
-
|
|
84883
|
+
unit: "hPa",
|
|
83876
84884
|
icon: "gauge"
|
|
83877
84885
|
}
|
|
83878
84886
|
]
|
|
@@ -84177,8 +85185,47 @@ function projectAction(cap, action) {
|
|
|
84177
85185
|
};
|
|
84178
85186
|
}
|
|
84179
85187
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
84180
|
-
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;
|
|
85188
|
+
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;
|
|
84181
85189
|
DeviceFeature.BatteryOperated;
|
|
85190
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
85191
|
+
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.";
|
|
85192
|
+
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.";
|
|
85193
|
+
var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
|
|
85194
|
+
var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
|
|
85195
|
+
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.";
|
|
85196
|
+
function withheld(reason) {
|
|
85197
|
+
return {
|
|
85198
|
+
[DeviceType.Washer]: {
|
|
85199
|
+
exported: false,
|
|
85200
|
+
reason
|
|
85201
|
+
},
|
|
85202
|
+
[DeviceType.Dryer]: {
|
|
85203
|
+
exported: false,
|
|
85204
|
+
reason
|
|
85205
|
+
},
|
|
85206
|
+
[DeviceType.Dishwasher]: {
|
|
85207
|
+
exported: false,
|
|
85208
|
+
reason
|
|
85209
|
+
}
|
|
85210
|
+
};
|
|
85211
|
+
}
|
|
85212
|
+
function offered(received) {
|
|
85213
|
+
return {
|
|
85214
|
+
[DeviceType.Washer]: {
|
|
85215
|
+
exported: true,
|
|
85216
|
+
as: received
|
|
85217
|
+
},
|
|
85218
|
+
[DeviceType.Dryer]: {
|
|
85219
|
+
exported: true,
|
|
85220
|
+
as: received
|
|
85221
|
+
},
|
|
85222
|
+
[DeviceType.Dishwasher]: {
|
|
85223
|
+
exported: true,
|
|
85224
|
+
as: received
|
|
85225
|
+
}
|
|
85226
|
+
};
|
|
85227
|
+
}
|
|
85228
|
+
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
84182
85229
|
Object.freeze({
|
|
84183
85230
|
"accessories.getStatus": [{
|
|
84184
85231
|
name: "deviceId",
|
|
@@ -85145,6 +86192,21 @@ Object.freeze({
|
|
|
85145
86192
|
form: "single",
|
|
85146
86193
|
optional: false
|
|
85147
86194
|
}],
|
|
86195
|
+
"groupActive.getStatus": [{
|
|
86196
|
+
name: "deviceId",
|
|
86197
|
+
form: "single",
|
|
86198
|
+
optional: false
|
|
86199
|
+
}],
|
|
86200
|
+
"groupCommands.getStatus": [{
|
|
86201
|
+
name: "deviceId",
|
|
86202
|
+
form: "single",
|
|
86203
|
+
optional: false
|
|
86204
|
+
}],
|
|
86205
|
+
"groupStatus.getStatus": [{
|
|
86206
|
+
name: "deviceId",
|
|
86207
|
+
form: "single",
|
|
86208
|
+
optional: false
|
|
86209
|
+
}],
|
|
85148
86210
|
"heatingValve.getStatus": [{
|
|
85149
86211
|
name: "deviceId",
|
|
85150
86212
|
form: "single",
|
|
@@ -85345,6 +86407,11 @@ Object.freeze({
|
|
|
85345
86407
|
form: "single",
|
|
85346
86408
|
optional: false
|
|
85347
86409
|
}],
|
|
86410
|
+
"meter.getStatus": [{
|
|
86411
|
+
name: "deviceId",
|
|
86412
|
+
form: "single",
|
|
86413
|
+
optional: false
|
|
86414
|
+
}],
|
|
85348
86415
|
"motion.getStatus": [{
|
|
85349
86416
|
name: "deviceId",
|
|
85350
86417
|
form: "single",
|
|
@@ -86952,7 +88019,6 @@ Object.freeze({
|
|
|
86952
88019
|
"network-access": "ingress",
|
|
86953
88020
|
"smtp-provider": "email"
|
|
86954
88021
|
});
|
|
86955
|
-
DeviceType.Camera;
|
|
86956
88022
|
/**
|
|
86957
88023
|
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
86958
88024
|
* `z.infer`, they do not restate the shape.
|
|
@@ -87129,6 +88195,8 @@ new Map(Object.entries({
|
|
|
87129
88195
|
"detection-blind": "device-events",
|
|
87130
88196
|
"motion-noisy": "device-events",
|
|
87131
88197
|
"motion-calm": "device-events",
|
|
88198
|
+
"package-llm-silent": "device-events",
|
|
88199
|
+
"package-llm-answered": "device-events",
|
|
87132
88200
|
"recording-stalled": "device-events",
|
|
87133
88201
|
"recording-healthy": "device-events",
|
|
87134
88202
|
"recording-degraded": "device-events",
|
|
@@ -87212,6 +88280,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
87212
88280
|
label: "Motion without a subject",
|
|
87213
88281
|
kinds: ["motion-noisy", "motion-calm"]
|
|
87214
88282
|
},
|
|
88283
|
+
{
|
|
88284
|
+
id: "package-llm",
|
|
88285
|
+
family: "device-events",
|
|
88286
|
+
label: "Package confirm",
|
|
88287
|
+
kinds: ["package-llm-silent", "package-llm-answered"]
|
|
88288
|
+
},
|
|
87215
88289
|
{
|
|
87216
88290
|
id: "recording",
|
|
87217
88291
|
family: "device-events",
|
|
@@ -87393,6 +88467,8 @@ var DEVICE_EVENTS = [
|
|
|
87393
88467
|
"detection-blind",
|
|
87394
88468
|
"motion-noisy",
|
|
87395
88469
|
"motion-calm",
|
|
88470
|
+
"package-llm-silent",
|
|
88471
|
+
"package-llm-answered",
|
|
87396
88472
|
"recording-stalled",
|
|
87397
88473
|
"recording-healthy",
|
|
87398
88474
|
"recording-degraded",
|
|
@@ -87661,6 +88737,15 @@ new Map([
|
|
|
87661
88737
|
snapshotLabel: "Cameras with constant motion now",
|
|
87662
88738
|
snapshotSection: null
|
|
87663
88739
|
},
|
|
88740
|
+
{
|
|
88741
|
+
id: "package-llm-confirm",
|
|
88742
|
+
family: "device-events",
|
|
88743
|
+
label: "Package confirm (not answering / answering again)",
|
|
88744
|
+
bad: ["package-llm-silent"],
|
|
88745
|
+
recovery: ["package-llm-answered"],
|
|
88746
|
+
snapshotLabel: "Cameras whose package confirm is not answering",
|
|
88747
|
+
snapshotSection: null
|
|
88748
|
+
},
|
|
87664
88749
|
{
|
|
87665
88750
|
id: "recording-health",
|
|
87666
88751
|
family: "device-events",
|