@camstack/addon-provider-rtsp 1.2.146 → 1.2.148
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +42 -0
- package/dist/addon.js +1607 -522
- package/dist/addon.mjs +1607 -522
- package/package.json +4 -2
package/dist/addon.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import net from "node:net";
|
|
2
|
-
//#region ../types/dist/event-category-
|
|
2
|
+
//#region ../types/dist/event-category-CT-hz5Rb.mjs
|
|
3
3
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4
4
|
EventCategory["SystemBoot"] = "system.boot";
|
|
5
5
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -325,6 +325,14 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
325
325
|
/** The recovery edge of {@link PipelineMotionNoisy}: the motion fell back, or a detection arrived. */
|
|
326
326
|
EventCategory["PipelineMotionCalm"] = "pipeline.motion-calm";
|
|
327
327
|
/**
|
|
328
|
+
* A camera's package LLM confirm has failed closed three times in a row
|
|
329
|
+
* (D931, 2026-10-26). One edge per camera until
|
|
330
|
+
* {@link PipelinePackageLlmAnswered}.
|
|
331
|
+
*/
|
|
332
|
+
EventCategory["PipelinePackageLlmSilent"] = "pipeline.package-llm-silent";
|
|
333
|
+
/** The recovery edge of {@link PipelinePackageLlmSilent}: a confirm returned yes or no. */
|
|
334
|
+
EventCategory["PipelinePackageLlmAnswered"] = "pipeline.package-llm-answered";
|
|
335
|
+
/**
|
|
328
336
|
* Per-camera pipeline config was mutated by the orchestrator
|
|
329
337
|
* (a settings change via `setAgentInferenceDevices` /
|
|
330
338
|
* `setCameraStepToggle` / `setCameraStepSettings`). Orchestrator
|
|
@@ -5434,7 +5442,7 @@ var ZodIssueCode = {
|
|
|
5434
5442
|
var ZodFirstPartyTypeKind;
|
|
5435
5443
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5436
5444
|
//#endregion
|
|
5437
|
-
//#region ../types/dist/sleep
|
|
5445
|
+
//#region ../types/dist/sleep--jXS9hWo.mjs
|
|
5438
5446
|
/**
|
|
5439
5447
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5440
5448
|
* window to float samples (D455).
|
|
@@ -7988,6 +7996,99 @@ var RecordKindDeclarationSchema = object({
|
|
|
7988
7996
|
}).strict();
|
|
7989
7997
|
/** The kind names: lower-case, dash separated, short enough for an index name. */
|
|
7990
7998
|
var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
|
|
7999
|
+
var READING_QUANTITIES = [
|
|
8000
|
+
"temperature",
|
|
8001
|
+
"pressure",
|
|
8002
|
+
"speed",
|
|
8003
|
+
"precipitation",
|
|
8004
|
+
"precipitation-rate",
|
|
8005
|
+
"length",
|
|
8006
|
+
"area",
|
|
8007
|
+
"illuminance",
|
|
8008
|
+
"irradiance",
|
|
8009
|
+
"power",
|
|
8010
|
+
"energy",
|
|
8011
|
+
"voltage",
|
|
8012
|
+
"current",
|
|
8013
|
+
"apparent-power",
|
|
8014
|
+
"humidity",
|
|
8015
|
+
"percentage",
|
|
8016
|
+
"concentration-volume",
|
|
8017
|
+
"concentration-mass",
|
|
8018
|
+
"concentration-ppb",
|
|
8019
|
+
"volume",
|
|
8020
|
+
"duration",
|
|
8021
|
+
"duration-ms",
|
|
8022
|
+
"information",
|
|
8023
|
+
"sound-level",
|
|
8024
|
+
"signal-level",
|
|
8025
|
+
"colour-temperature",
|
|
8026
|
+
"angle",
|
|
8027
|
+
"bitrate",
|
|
8028
|
+
"rate",
|
|
8029
|
+
"mass",
|
|
8030
|
+
"index",
|
|
8031
|
+
"ratio",
|
|
8032
|
+
"count",
|
|
8033
|
+
"none",
|
|
8034
|
+
"device"
|
|
8035
|
+
];
|
|
8036
|
+
var ReadingQuantitySchema = _enum(READING_QUANTITIES);
|
|
8037
|
+
new Set(READING_QUANTITIES);
|
|
8038
|
+
/**
|
|
8039
|
+
* Stored unit symbol for a quantity. `none` means the number is dimensionless
|
|
8040
|
+
* (or an index / ratio / count). `device` means the cap has no single unit.
|
|
8041
|
+
*/
|
|
8042
|
+
var QUANTITY_BASE = {
|
|
8043
|
+
temperature: "°C",
|
|
8044
|
+
pressure: "hPa",
|
|
8045
|
+
speed: "m/s",
|
|
8046
|
+
precipitation: "mm",
|
|
8047
|
+
"precipitation-rate": "mm/h",
|
|
8048
|
+
length: "m",
|
|
8049
|
+
area: "m²",
|
|
8050
|
+
illuminance: "lx",
|
|
8051
|
+
irradiance: "W/m²",
|
|
8052
|
+
power: "W",
|
|
8053
|
+
energy: "kWh",
|
|
8054
|
+
voltage: "V",
|
|
8055
|
+
current: "A",
|
|
8056
|
+
"apparent-power": "VA",
|
|
8057
|
+
humidity: "%",
|
|
8058
|
+
percentage: "%",
|
|
8059
|
+
"concentration-volume": "ppm",
|
|
8060
|
+
"concentration-mass": "µg/m³",
|
|
8061
|
+
"concentration-ppb": "ppb",
|
|
8062
|
+
volume: "L",
|
|
8063
|
+
duration: "s",
|
|
8064
|
+
"duration-ms": "ms",
|
|
8065
|
+
information: "B",
|
|
8066
|
+
"sound-level": "dBFS",
|
|
8067
|
+
"signal-level": "dBm",
|
|
8068
|
+
"colour-temperature": "mired",
|
|
8069
|
+
angle: "°",
|
|
8070
|
+
bitrate: "kbps",
|
|
8071
|
+
rate: "fps",
|
|
8072
|
+
mass: "kg",
|
|
8073
|
+
index: "none",
|
|
8074
|
+
ratio: "none",
|
|
8075
|
+
count: "none",
|
|
8076
|
+
none: "none",
|
|
8077
|
+
device: "device"
|
|
8078
|
+
};
|
|
8079
|
+
/** The stored unit, or `undefined` when the quantity has no unit symbol. */
|
|
8080
|
+
function baseUnitOf(quantity) {
|
|
8081
|
+
const symbol = QUANTITY_BASE[quantity];
|
|
8082
|
+
if (symbol === "none" || symbol === "device") return void 0;
|
|
8083
|
+
return symbol;
|
|
8084
|
+
}
|
|
8085
|
+
function readingMeta(quantity, extras) {
|
|
8086
|
+
return {
|
|
8087
|
+
quantity,
|
|
8088
|
+
...extras?.precision !== void 0 ? { precision: extras.precision } : {},
|
|
8089
|
+
...extras?.label !== void 0 ? { label: extras.label } : {}
|
|
8090
|
+
};
|
|
8091
|
+
}
|
|
7991
8092
|
/** Shared Zod schemas used across streaming capabilities. */
|
|
7992
8093
|
var CamProfileSchema = _enum([
|
|
7993
8094
|
"high",
|
|
@@ -8015,8 +8116,8 @@ var CamStreamKindSchema = _enum([
|
|
|
8015
8116
|
"derived"
|
|
8016
8117
|
]);
|
|
8017
8118
|
var CamStreamResolutionSchema = object({
|
|
8018
|
-
width: number().int().positive(),
|
|
8019
|
-
height: number().int().positive()
|
|
8119
|
+
width: number().int().positive().meta(readingMeta("none")),
|
|
8120
|
+
height: number().int().positive().meta(readingMeta("none"))
|
|
8020
8121
|
});
|
|
8021
8122
|
var CameraStreamSchema = object({
|
|
8022
8123
|
/** Stable, provider-assigned id unique within the (deviceId) scope. */
|
|
@@ -8534,6 +8635,9 @@ var CAP_NAMES = Object.freeze([
|
|
|
8534
8635
|
"filesystem-browse",
|
|
8535
8636
|
"flood",
|
|
8536
8637
|
"gas",
|
|
8638
|
+
"group-active",
|
|
8639
|
+
"group-commands",
|
|
8640
|
+
"group-status",
|
|
8537
8641
|
"heating-valve",
|
|
8538
8642
|
"humidifier",
|
|
8539
8643
|
"humidity-sensor",
|
|
@@ -8554,6 +8658,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8554
8658
|
"login-method",
|
|
8555
8659
|
"media-player",
|
|
8556
8660
|
"mesh-network",
|
|
8661
|
+
"meter",
|
|
8557
8662
|
"metrics-provider",
|
|
8558
8663
|
"model-convert",
|
|
8559
8664
|
"model-distributor",
|
|
@@ -8663,14 +8768,6 @@ Object.freeze({
|
|
|
8663
8768
|
{
|
|
8664
8769
|
"path": "aqi",
|
|
8665
8770
|
"valueType": "number"
|
|
8666
|
-
},
|
|
8667
|
-
{
|
|
8668
|
-
"path": "unit",
|
|
8669
|
-
"valueType": "string"
|
|
8670
|
-
},
|
|
8671
|
-
{
|
|
8672
|
-
"path": "precision",
|
|
8673
|
-
"valueType": "number"
|
|
8674
8771
|
}
|
|
8675
8772
|
],
|
|
8676
8773
|
"alarm-panel": [
|
|
@@ -8706,20 +8803,10 @@ Object.freeze({
|
|
|
8706
8803
|
"valueType": "boolean"
|
|
8707
8804
|
}
|
|
8708
8805
|
],
|
|
8709
|
-
"ambient-light-sensor": [
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8713
|
-
},
|
|
8714
|
-
{
|
|
8715
|
-
"path": "unit",
|
|
8716
|
-
"valueType": "string"
|
|
8717
|
-
},
|
|
8718
|
-
{
|
|
8719
|
-
"path": "precision",
|
|
8720
|
-
"valueType": "number"
|
|
8721
|
-
}
|
|
8722
|
-
],
|
|
8806
|
+
"ambient-light-sensor": [{
|
|
8807
|
+
"path": "lux",
|
|
8808
|
+
"valueType": "number"
|
|
8809
|
+
}],
|
|
8723
8810
|
"audio-metrics": [
|
|
8724
8811
|
{
|
|
8725
8812
|
"path": "ts",
|
|
@@ -8799,6 +8886,10 @@ Object.freeze({
|
|
|
8799
8886
|
"path": "lastUpdated",
|
|
8800
8887
|
"valueType": "number"
|
|
8801
8888
|
},
|
|
8889
|
+
{
|
|
8890
|
+
"path": "volts",
|
|
8891
|
+
"valueType": "number"
|
|
8892
|
+
},
|
|
8802
8893
|
{
|
|
8803
8894
|
"path": "lastContactAt",
|
|
8804
8895
|
"valueType": "number"
|
|
@@ -9041,10 +9132,6 @@ Object.freeze({
|
|
|
9041
9132
|
"datetime"
|
|
9042
9133
|
]
|
|
9043
9134
|
},
|
|
9044
|
-
{
|
|
9045
|
-
"path": "unit",
|
|
9046
|
-
"valueType": "string"
|
|
9047
|
-
},
|
|
9048
9135
|
{
|
|
9049
9136
|
"path": "min",
|
|
9050
9137
|
"valueType": "number"
|
|
@@ -9056,19 +9143,6 @@ Object.freeze({
|
|
|
9056
9143
|
{
|
|
9057
9144
|
"path": "step",
|
|
9058
9145
|
"valueType": "number"
|
|
9059
|
-
},
|
|
9060
|
-
{
|
|
9061
|
-
"path": "precision",
|
|
9062
|
-
"valueType": "number"
|
|
9063
|
-
},
|
|
9064
|
-
{
|
|
9065
|
-
"path": "format",
|
|
9066
|
-
"valueType": "enum",
|
|
9067
|
-
"enumValues": [
|
|
9068
|
-
"date",
|
|
9069
|
-
"time",
|
|
9070
|
-
"datetime"
|
|
9071
|
-
]
|
|
9072
9146
|
}
|
|
9073
9147
|
],
|
|
9074
9148
|
"cover": [
|
|
@@ -9309,13 +9383,8 @@ Object.freeze({
|
|
|
9309
9383
|
"path": "value",
|
|
9310
9384
|
"valueType": "string"
|
|
9311
9385
|
}, {
|
|
9312
|
-
"path": "
|
|
9313
|
-
"valueType": "
|
|
9314
|
-
"enumValues": [
|
|
9315
|
-
"date",
|
|
9316
|
-
"time",
|
|
9317
|
-
"datetime"
|
|
9318
|
-
]
|
|
9386
|
+
"path": "timestamp",
|
|
9387
|
+
"valueType": "boolean"
|
|
9319
9388
|
}],
|
|
9320
9389
|
"event-emitter": [
|
|
9321
9390
|
{
|
|
@@ -9388,6 +9457,24 @@ Object.freeze({
|
|
|
9388
9457
|
"path": "detected",
|
|
9389
9458
|
"valueType": "boolean"
|
|
9390
9459
|
}],
|
|
9460
|
+
"group-active": [{
|
|
9461
|
+
"path": "active",
|
|
9462
|
+
"valueType": "boolean"
|
|
9463
|
+
}],
|
|
9464
|
+
"group-status": [
|
|
9465
|
+
{
|
|
9466
|
+
"path": "included",
|
|
9467
|
+
"valueType": "number"
|
|
9468
|
+
},
|
|
9469
|
+
{
|
|
9470
|
+
"path": "excluded",
|
|
9471
|
+
"valueType": "number"
|
|
9472
|
+
},
|
|
9473
|
+
{
|
|
9474
|
+
"path": "total",
|
|
9475
|
+
"valueType": "number"
|
|
9476
|
+
}
|
|
9477
|
+
],
|
|
9391
9478
|
"heating-valve": [{
|
|
9392
9479
|
"path": "position",
|
|
9393
9480
|
"valueType": "number"
|
|
@@ -9422,20 +9509,10 @@ Object.freeze({
|
|
|
9422
9509
|
"valueType": "number"
|
|
9423
9510
|
}
|
|
9424
9511
|
],
|
|
9425
|
-
"humidity-sensor": [
|
|
9426
|
-
|
|
9427
|
-
|
|
9428
|
-
|
|
9429
|
-
},
|
|
9430
|
-
{
|
|
9431
|
-
"path": "unit",
|
|
9432
|
-
"valueType": "string"
|
|
9433
|
-
},
|
|
9434
|
-
{
|
|
9435
|
-
"path": "precision",
|
|
9436
|
-
"valueType": "number"
|
|
9437
|
-
}
|
|
9438
|
-
],
|
|
9512
|
+
"humidity-sensor": [{
|
|
9513
|
+
"path": "percent",
|
|
9514
|
+
"valueType": "number"
|
|
9515
|
+
}],
|
|
9439
9516
|
"image": [{
|
|
9440
9517
|
"path": "url",
|
|
9441
9518
|
"valueType": "string"
|
|
@@ -9643,6 +9720,10 @@ Object.freeze({
|
|
|
9643
9720
|
]
|
|
9644
9721
|
}
|
|
9645
9722
|
],
|
|
9723
|
+
"meter": [{
|
|
9724
|
+
"path": "value",
|
|
9725
|
+
"valueType": "number"
|
|
9726
|
+
}],
|
|
9646
9727
|
"motion": [
|
|
9647
9728
|
{
|
|
9648
9729
|
"path": "detected",
|
|
@@ -9774,20 +9855,10 @@ Object.freeze({
|
|
|
9774
9855
|
"valueType": "boolean"
|
|
9775
9856
|
}
|
|
9776
9857
|
],
|
|
9777
|
-
"numeric-sensor": [
|
|
9778
|
-
|
|
9779
|
-
|
|
9780
|
-
|
|
9781
|
-
},
|
|
9782
|
-
{
|
|
9783
|
-
"path": "unit",
|
|
9784
|
-
"valueType": "string"
|
|
9785
|
-
},
|
|
9786
|
-
{
|
|
9787
|
-
"path": "precision",
|
|
9788
|
-
"valueType": "number"
|
|
9789
|
-
}
|
|
9790
|
-
],
|
|
9858
|
+
"numeric-sensor": [{
|
|
9859
|
+
"path": "value",
|
|
9860
|
+
"valueType": "number"
|
|
9861
|
+
}],
|
|
9791
9862
|
"pet-feeder": [
|
|
9792
9863
|
{
|
|
9793
9864
|
"path": "foodLevel",
|
|
@@ -9879,14 +9950,6 @@ Object.freeze({
|
|
|
9879
9950
|
{
|
|
9880
9951
|
"path": "powerFactor",
|
|
9881
9952
|
"valueType": "number"
|
|
9882
|
-
},
|
|
9883
|
-
{
|
|
9884
|
-
"path": "unit",
|
|
9885
|
-
"valueType": "string"
|
|
9886
|
-
},
|
|
9887
|
-
{
|
|
9888
|
-
"path": "precision",
|
|
9889
|
-
"valueType": "number"
|
|
9890
9953
|
}
|
|
9891
9954
|
],
|
|
9892
9955
|
"presence": [
|
|
@@ -9915,20 +9978,10 @@ Object.freeze({
|
|
|
9915
9978
|
"valueType": "number"
|
|
9916
9979
|
}
|
|
9917
9980
|
],
|
|
9918
|
-
"pressure-sensor": [
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
},
|
|
9923
|
-
{
|
|
9924
|
-
"path": "unit",
|
|
9925
|
-
"valueType": "string"
|
|
9926
|
-
},
|
|
9927
|
-
{
|
|
9928
|
-
"path": "precision",
|
|
9929
|
-
"valueType": "number"
|
|
9930
|
-
}
|
|
9931
|
-
],
|
|
9981
|
+
"pressure-sensor": [{
|
|
9982
|
+
"path": "hpa",
|
|
9983
|
+
"valueType": "number"
|
|
9984
|
+
}],
|
|
9932
9985
|
"privacy-mask": [{
|
|
9933
9986
|
"path": "enabled",
|
|
9934
9987
|
"valueType": "boolean"
|
|
@@ -10239,20 +10292,10 @@ Object.freeze({
|
|
|
10239
10292
|
"path": "tampered",
|
|
10240
10293
|
"valueType": "boolean"
|
|
10241
10294
|
}],
|
|
10242
|
-
"temperature-sensor": [
|
|
10243
|
-
|
|
10244
|
-
|
|
10245
|
-
|
|
10246
|
-
},
|
|
10247
|
-
{
|
|
10248
|
-
"path": "unit",
|
|
10249
|
-
"valueType": "string"
|
|
10250
|
-
},
|
|
10251
|
-
{
|
|
10252
|
-
"path": "precision",
|
|
10253
|
-
"valueType": "number"
|
|
10254
|
-
}
|
|
10255
|
-
],
|
|
10295
|
+
"temperature-sensor": [{
|
|
10296
|
+
"path": "celsius",
|
|
10297
|
+
"valueType": "number"
|
|
10298
|
+
}],
|
|
10256
10299
|
"update": [
|
|
10257
10300
|
{
|
|
10258
10301
|
"path": "currentVersion",
|
|
@@ -10363,10 +10406,6 @@ Object.freeze({
|
|
|
10363
10406
|
"path": "temperature",
|
|
10364
10407
|
"valueType": "number"
|
|
10365
10408
|
},
|
|
10366
|
-
{
|
|
10367
|
-
"path": "temperatureUnit",
|
|
10368
|
-
"valueType": "string"
|
|
10369
|
-
},
|
|
10370
10409
|
{
|
|
10371
10410
|
"path": "humidity",
|
|
10372
10411
|
"valueType": "number"
|
|
@@ -10375,18 +10414,10 @@ Object.freeze({
|
|
|
10375
10414
|
"path": "pressure",
|
|
10376
10415
|
"valueType": "number"
|
|
10377
10416
|
},
|
|
10378
|
-
{
|
|
10379
|
-
"path": "pressureUnit",
|
|
10380
|
-
"valueType": "string"
|
|
10381
|
-
},
|
|
10382
10417
|
{
|
|
10383
10418
|
"path": "windSpeed",
|
|
10384
10419
|
"valueType": "number"
|
|
10385
10420
|
},
|
|
10386
|
-
{
|
|
10387
|
-
"path": "windSpeedUnit",
|
|
10388
|
-
"valueType": "string"
|
|
10389
|
-
},
|
|
10390
10421
|
{
|
|
10391
10422
|
"path": "windBearing",
|
|
10392
10423
|
"valueType": "number"
|
|
@@ -10525,7 +10556,7 @@ function maskUrlCredentials(rawUrl) {
|
|
|
10525
10556
|
}
|
|
10526
10557
|
}
|
|
10527
10558
|
//#endregion
|
|
10528
|
-
//#region ../types/dist/err-msg-
|
|
10559
|
+
//#region ../types/dist/err-msg-DIorGYeC.mjs
|
|
10529
10560
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
10530
10561
|
DeviceType["Camera"] = "camera";
|
|
10531
10562
|
DeviceType["Hub"] = "hub";
|
|
@@ -10619,8 +10650,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
10619
10650
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
10620
10651
|
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
10621
10652
|
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
10622
|
-
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10653
|
+
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10654
|
+
* A plug-built dishwasher carries running-state and power instead. */
|
|
10623
10655
|
DeviceType["Dishwasher"] = "dishwasher";
|
|
10656
|
+
/** Washing machine. A plug-built one is running while its power stays
|
|
10657
|
+
* above a threshold. No program cap — that surface is the dishwasher's. */
|
|
10658
|
+
DeviceType["Washer"] = "washer";
|
|
10659
|
+
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
10660
|
+
DeviceType["Dryer"] = "dryer";
|
|
10624
10661
|
return DeviceType;
|
|
10625
10662
|
}({});
|
|
10626
10663
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -10878,7 +10915,7 @@ function errMsg(err) {
|
|
|
10878
10915
|
return String(err);
|
|
10879
10916
|
}
|
|
10880
10917
|
//#endregion
|
|
10881
|
-
//#region ../types/dist/composition-
|
|
10918
|
+
//#region ../types/dist/composition-Bb8iwHEJ.mjs
|
|
10882
10919
|
/**
|
|
10883
10920
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
10884
10921
|
*
|
|
@@ -11193,6 +11230,13 @@ var table = {
|
|
|
11193
11230
|
apply: () => {
|
|
11194
11231
|
throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
|
|
11195
11232
|
}
|
|
11233
|
+
},
|
|
11234
|
+
schmitt: {
|
|
11235
|
+
minArgs: 3,
|
|
11236
|
+
maxArgs: 5,
|
|
11237
|
+
apply: () => {
|
|
11238
|
+
throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
|
|
11239
|
+
}
|
|
11196
11240
|
}
|
|
11197
11241
|
};
|
|
11198
11242
|
Object.freeze(Object.assign(Object.create(null), table));
|
|
@@ -11633,15 +11677,232 @@ function validateExpressionSource(src) {
|
|
|
11633
11677
|
}
|
|
11634
11678
|
return null;
|
|
11635
11679
|
}
|
|
11636
|
-
/**
|
|
11637
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11680
|
+
/**
|
|
11681
|
+
* One power reading becomes a washer, dryer or dishwasher (D944 helper).
|
|
11682
|
+
*
|
|
11683
|
+
* Standby of the control board is under 5 W; a motor or a heater is far
|
|
11684
|
+
* above that, so 5 W is the line. A door lock or a short pump spike is not
|
|
11685
|
+
* a cycle, so running starts only after 30 s above the line.
|
|
11686
|
+
*
|
|
11687
|
+
* A wash, dry or dishwasher pause (soak, heat-wait, heater off between
|
|
11688
|
+
* bursts) lasts several minutes. Ending the cycle on the first dip would
|
|
11689
|
+
* report "finished" in the middle of the program, so the stop dwell is
|
|
11690
|
+
* 10 minutes on every type. The number is stored per type so one machine
|
|
11691
|
+
* can move later without moving the others.
|
|
11692
|
+
*/
|
|
11693
|
+
var APPLIANCE_TEMPLATE_ID = "appliance";
|
|
11694
|
+
var APPLIANCE_KINDS = [
|
|
11695
|
+
"washer",
|
|
11696
|
+
"dryer",
|
|
11697
|
+
"dishwasher"
|
|
11698
|
+
];
|
|
11699
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
11700
|
+
/**
|
|
11701
|
+
* Statistics and integrals over a history read (D944).
|
|
11702
|
+
*
|
|
11703
|
+
* Pure: the same points always give the same answer, so a restart recomputes
|
|
11704
|
+
* rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
|
|
11705
|
+
* never the mean of the samples. A hole with no held value is not integrated
|
|
11706
|
+
* as zero. A truncated read is not published.
|
|
11707
|
+
*/
|
|
11708
|
+
var STATISTIC_STATS = [
|
|
11709
|
+
"min",
|
|
11710
|
+
"max",
|
|
11711
|
+
"mean",
|
|
11712
|
+
"sum",
|
|
11713
|
+
"count",
|
|
11714
|
+
"change"
|
|
11715
|
+
];
|
|
11716
|
+
/**
|
|
11717
|
+
* The range a statistics or integral helper asks history for (D944).
|
|
11718
|
+
*
|
|
11719
|
+
* Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
|
|
11720
|
+
* or 25 hours across a DST change. A tier is eligible only when its retention
|
|
11721
|
+
* is strictly longer than the longest that range can be. Forever qualifies.
|
|
11722
|
+
* Equal to a tier's retention is a refusal, not a short answer.
|
|
11723
|
+
*/
|
|
11724
|
+
var MINUTE_MS = 6e4;
|
|
11725
|
+
var HOUR_MS = 60 * MINUTE_MS;
|
|
11726
|
+
var DAY_MS = 24 * HOUR_MS;
|
|
11727
|
+
var ROLLING_SPANS = [
|
|
11728
|
+
"1h",
|
|
11729
|
+
"24h",
|
|
11730
|
+
"2d",
|
|
11731
|
+
"7d",
|
|
11732
|
+
"30d",
|
|
11733
|
+
"custom"
|
|
11734
|
+
];
|
|
11735
|
+
var CALENDAR_PERIODS = [
|
|
11736
|
+
"today",
|
|
11737
|
+
"yesterday",
|
|
11738
|
+
"this-week",
|
|
11739
|
+
"this-month"
|
|
11740
|
+
];
|
|
11741
|
+
var RESET_PERIODS = [
|
|
11742
|
+
"never",
|
|
11743
|
+
"daily",
|
|
11744
|
+
"weekly",
|
|
11745
|
+
"monthly"
|
|
11746
|
+
];
|
|
11747
|
+
5 * MINUTE_MS;
|
|
11748
|
+
24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
|
|
11749
|
+
7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
|
|
11750
|
+
var rollingWindowObject = object({
|
|
11751
|
+
kind: literal("rolling"),
|
|
11752
|
+
span: _enum(ROLLING_SPANS),
|
|
11753
|
+
customMinutes: number().int().min(5).optional()
|
|
11754
|
+
}).check((payload) => {
|
|
11755
|
+
if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
|
|
11756
|
+
code: "custom",
|
|
11757
|
+
message: "a custom range needs a duration in minutes",
|
|
11758
|
+
path: ["customMinutes"],
|
|
11759
|
+
input: payload.value
|
|
11760
|
+
});
|
|
11761
|
+
if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
|
|
11762
|
+
code: "custom",
|
|
11763
|
+
message: "only a custom range carries a duration",
|
|
11764
|
+
path: ["customMinutes"],
|
|
11765
|
+
input: payload.value
|
|
11766
|
+
});
|
|
11767
|
+
});
|
|
11768
|
+
var RollingWindowSchema = rollingWindowObject;
|
|
11769
|
+
var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
|
|
11770
|
+
kind: literal("calendar"),
|
|
11771
|
+
period: _enum(CALENDAR_PERIODS)
|
|
11772
|
+
})]);
|
|
11773
|
+
/**
|
|
11774
|
+
* A composition source identity: `(addonId, stableId)`, never a numeric device
|
|
11775
|
+
* id. Split out so helper schemas can name it without a cycle through
|
|
11776
|
+
* `composition.ts` (D659).
|
|
11777
|
+
*/
|
|
11640
11778
|
var CompositionSourceRefSchema = object({
|
|
11641
11779
|
/** The owning addon; `stableId` is unique only within it. */
|
|
11642
11780
|
addonId: string().min(1),
|
|
11643
11781
|
stableId: string().min(1)
|
|
11644
11782
|
});
|
|
11783
|
+
var GROUP_TEMPLATE_IDS = [
|
|
11784
|
+
"temperature",
|
|
11785
|
+
"humidity",
|
|
11786
|
+
"pressure",
|
|
11787
|
+
"illuminance",
|
|
11788
|
+
"power",
|
|
11789
|
+
"energy",
|
|
11790
|
+
"battery",
|
|
11791
|
+
"numeric",
|
|
11792
|
+
"motion",
|
|
11793
|
+
"contact",
|
|
11794
|
+
"leak",
|
|
11795
|
+
"smoke",
|
|
11796
|
+
"occupancy",
|
|
11797
|
+
"binary",
|
|
11798
|
+
"switch",
|
|
11799
|
+
"light",
|
|
11800
|
+
"fan",
|
|
11801
|
+
"cover",
|
|
11802
|
+
"valve",
|
|
11803
|
+
"lock",
|
|
11804
|
+
"media-player",
|
|
11805
|
+
"climate"
|
|
11806
|
+
];
|
|
11807
|
+
var NUMERIC_AGGREGATES = [
|
|
11808
|
+
"mean",
|
|
11809
|
+
"median",
|
|
11810
|
+
"min",
|
|
11811
|
+
"max",
|
|
11812
|
+
"sum"
|
|
11813
|
+
];
|
|
11814
|
+
var BINARY_AGGREGATES = ["any", "all"];
|
|
11815
|
+
/** How an actuator group decides it is on. Unknown members never count as off. */
|
|
11816
|
+
var ACTIVE_CRITERIA = ["at-least-one", "all"];
|
|
11817
|
+
var AGGREGATE_REDUCERS = [
|
|
11818
|
+
"mean",
|
|
11819
|
+
"median",
|
|
11820
|
+
"min",
|
|
11821
|
+
"max",
|
|
11822
|
+
"sum",
|
|
11823
|
+
"any",
|
|
11824
|
+
"all"
|
|
11825
|
+
];
|
|
11826
|
+
var GroupTemplateIdSchema = _enum(GROUP_TEMPLATE_IDS);
|
|
11827
|
+
var AggregateReducerSchema = _enum(AGGREGATE_REDUCERS);
|
|
11828
|
+
var ActiveCriterionSchema = _enum(ACTIVE_CRITERIA);
|
|
11829
|
+
/** One member. The field is stored so occupancy can name motion or binary. */
|
|
11830
|
+
var HelperMemberSchema = object({
|
|
11831
|
+
source: CompositionSourceRefSchema,
|
|
11832
|
+
cap: string().min(1),
|
|
11833
|
+
fieldPath: string().min(1)
|
|
11834
|
+
});
|
|
11835
|
+
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
11836
|
+
object({
|
|
11837
|
+
kind: literal("group"),
|
|
11838
|
+
templateId: GroupTemplateIdSchema,
|
|
11839
|
+
aggregate: AggregateReducerSchema,
|
|
11840
|
+
members: array(HelperMemberSchema).min(1).max(64),
|
|
11841
|
+
/** Actuator groups only. Readings leave this unset. */
|
|
11842
|
+
active: ActiveCriterionSchema.optional()
|
|
11843
|
+
}),
|
|
11844
|
+
object({
|
|
11845
|
+
kind: literal("appliance"),
|
|
11846
|
+
templateId: literal(APPLIANCE_TEMPLATE_ID),
|
|
11847
|
+
appliance: _enum(APPLIANCE_KINDS),
|
|
11848
|
+
thresholdWatts: number().positive(),
|
|
11849
|
+
startDwellMs: number().int().nonnegative(),
|
|
11850
|
+
stopDwellMs: number().int().nonnegative(),
|
|
11851
|
+
member: HelperMemberSchema
|
|
11852
|
+
}),
|
|
11853
|
+
object({
|
|
11854
|
+
kind: literal("statistic"),
|
|
11855
|
+
stat: _enum(STATISTIC_STATS),
|
|
11856
|
+
window: StatisticWindowSchema,
|
|
11857
|
+
source: HelperMemberSchema
|
|
11858
|
+
}),
|
|
11859
|
+
object({
|
|
11860
|
+
kind: literal("integral"),
|
|
11861
|
+
reset: _enum(RESET_PERIODS),
|
|
11862
|
+
output: _enum(["energy", "meter"]),
|
|
11863
|
+
source: HelperMemberSchema
|
|
11864
|
+
}),
|
|
11865
|
+
object({
|
|
11866
|
+
kind: literal("threshold"),
|
|
11867
|
+
source: HelperMemberSchema,
|
|
11868
|
+
high: number(),
|
|
11869
|
+
low: number(),
|
|
11870
|
+
onForMs: number().int().nonnegative(),
|
|
11871
|
+
offForMs: number().int().nonnegative()
|
|
11872
|
+
}),
|
|
11873
|
+
object({
|
|
11874
|
+
kind: literal("arithmetic"),
|
|
11875
|
+
op: _enum([
|
|
11876
|
+
"sum",
|
|
11877
|
+
"difference",
|
|
11878
|
+
"ratio"
|
|
11879
|
+
]),
|
|
11880
|
+
inputs: array(HelperMemberSchema).min(2).max(64)
|
|
11881
|
+
}),
|
|
11882
|
+
object({
|
|
11883
|
+
kind: literal("expression"),
|
|
11884
|
+
expr: string().min(1),
|
|
11885
|
+
inputs: array(object({
|
|
11886
|
+
name: string().min(1),
|
|
11887
|
+
member: HelperMemberSchema
|
|
11888
|
+
})).min(1).max(64),
|
|
11889
|
+
output: _enum(["number", "boolean"])
|
|
11890
|
+
}),
|
|
11891
|
+
object({
|
|
11892
|
+
kind: literal("derivative"),
|
|
11893
|
+
window: RollingWindowSchema,
|
|
11894
|
+
per: _enum([
|
|
11895
|
+
"second",
|
|
11896
|
+
"minute",
|
|
11897
|
+
"hour"
|
|
11898
|
+
]),
|
|
11899
|
+
source: HelperMemberSchema
|
|
11900
|
+
})
|
|
11901
|
+
]);
|
|
11902
|
+
/** A composed device's stableId is this prefix plus the block id. */
|
|
11903
|
+
var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
|
|
11904
|
+
/** Longest snippet a `code` source may carry (slice 4). */
|
|
11905
|
+
var MAX_COMPOSITION_CODE_LENGTH = 2e4;
|
|
11645
11906
|
/** One field of one capability of one source device. */
|
|
11646
11907
|
var CompositionFieldReadSchema = object({
|
|
11647
11908
|
source: CompositionSourceRefSchema,
|
|
@@ -11685,7 +11946,59 @@ var CompositionCodeSourceSchema = object({
|
|
|
11685
11946
|
var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
11686
11947
|
CompositionFromSourceSchema,
|
|
11687
11948
|
CompositionExpressionSourceSchema,
|
|
11688
|
-
CompositionCodeSourceSchema
|
|
11949
|
+
CompositionCodeSourceSchema,
|
|
11950
|
+
object({
|
|
11951
|
+
kind: literal("aggregate"),
|
|
11952
|
+
reducer: _enum([
|
|
11953
|
+
"mean",
|
|
11954
|
+
"median",
|
|
11955
|
+
"min",
|
|
11956
|
+
"max",
|
|
11957
|
+
"sum",
|
|
11958
|
+
"any",
|
|
11959
|
+
"all"
|
|
11960
|
+
]),
|
|
11961
|
+
reads: array(CompositionFieldReadSchema).min(1).max(64),
|
|
11962
|
+
/**
|
|
11963
|
+
* Set when the reads are an enum state, not a yes/no: the listed states
|
|
11964
|
+
* count as active (a cover is `open`). A state outside the list is inactive;
|
|
11965
|
+
* a read that is not a state at all is left out. Only `any` / `all` use it.
|
|
11966
|
+
*/
|
|
11967
|
+
activeWhen: array(string().min(1)).min(1).optional(),
|
|
11968
|
+
/** `value` is the reading; the others are the member counts. */
|
|
11969
|
+
publish: _enum([
|
|
11970
|
+
"value",
|
|
11971
|
+
"included",
|
|
11972
|
+
"excluded",
|
|
11973
|
+
"total"
|
|
11974
|
+
]),
|
|
11975
|
+
/** What a `value` publish does when nobody is included. Counts ignore this. */
|
|
11976
|
+
empty: _enum([
|
|
11977
|
+
"null",
|
|
11978
|
+
"omit",
|
|
11979
|
+
"offline"
|
|
11980
|
+
])
|
|
11981
|
+
}),
|
|
11982
|
+
object({
|
|
11983
|
+
kind: literal("series"),
|
|
11984
|
+
read: CompositionFieldReadSchema,
|
|
11985
|
+
/** `value` is the statistic or the integral. The others are the gap counts. */
|
|
11986
|
+
publish: _enum([
|
|
11987
|
+
"value",
|
|
11988
|
+
"included",
|
|
11989
|
+
"excluded",
|
|
11990
|
+
"total"
|
|
11991
|
+
]),
|
|
11992
|
+
/**
|
|
11993
|
+
* What an unanswered `value` does. Counts ignore this. `offline` leaves the
|
|
11994
|
+
* device unseeded until history answers, so a restart cannot publish 0.
|
|
11995
|
+
*/
|
|
11996
|
+
empty: _enum([
|
|
11997
|
+
"null",
|
|
11998
|
+
"omit",
|
|
11999
|
+
"offline"
|
|
12000
|
+
])
|
|
12001
|
+
})
|
|
11689
12002
|
]);
|
|
11690
12003
|
var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
|
|
11691
12004
|
kind: literal("forward"),
|
|
@@ -11731,7 +12044,13 @@ var CompositionSchema = object({
|
|
|
11731
12044
|
kind: literal("existing"),
|
|
11732
12045
|
device: CompositionSourceRefSchema
|
|
11733
12046
|
})]),
|
|
11734
|
-
features: array(CompositionFeatureSchema).min(1).max(16)
|
|
12047
|
+
features: array(CompositionFeatureSchema).min(1).max(16),
|
|
12048
|
+
/**
|
|
12049
|
+
* Present when the block is a helper template. Absent on a free
|
|
12050
|
+
* composition (the room example, device 4603). The editor reads this,
|
|
12051
|
+
* never the compiled features.
|
|
12052
|
+
*/
|
|
12053
|
+
helper: HelperSpecSchema.optional()
|
|
11735
12054
|
}).superRefine((composition, ctx) => {
|
|
11736
12055
|
const seen = /* @__PURE__ */ new Set();
|
|
11737
12056
|
composition.features.forEach((feature, index) => {
|
|
@@ -16870,7 +17189,22 @@ var BrokerProviderInfoSchema = object({
|
|
|
16870
17189
|
*/
|
|
16871
17190
|
interactiveConnectPath: string().min(1).optional(),
|
|
16872
17191
|
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
16873
|
-
interactiveDisconnectPath: string().min(1).optional()
|
|
17192
|
+
interactiveDisconnectPath: string().min(1).optional(),
|
|
17193
|
+
/**
|
|
17194
|
+
* The kind signs in through a server-held interactive session instead of
|
|
17195
|
+
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
17196
|
+
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
17197
|
+
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
17198
|
+
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
17199
|
+
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
17200
|
+
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
17201
|
+
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
17202
|
+
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
17203
|
+
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
17204
|
+
* offers it as the default path; the fields the session fills are
|
|
17205
|
+
* marked `filledBySignIn` in the kind's settings schema.
|
|
17206
|
+
*/
|
|
17207
|
+
signIn: object({ basePath: string().min(1) }).optional()
|
|
16874
17208
|
}))
|
|
16875
17209
|
});
|
|
16876
17210
|
var ListInputSchema = object({
|
|
@@ -17971,7 +18305,12 @@ var CompositionProblemSchema = object({
|
|
|
17971
18305
|
"item-field-reserved",
|
|
17972
18306
|
"target-missing",
|
|
17973
18307
|
"target-unreadable",
|
|
17974
|
-
"not-replaceable"
|
|
18308
|
+
"not-replaceable",
|
|
18309
|
+
"native-capability-replace",
|
|
18310
|
+
"mixed-quantity",
|
|
18311
|
+
"helper-mismatch",
|
|
18312
|
+
"member-constraint",
|
|
18313
|
+
"retention"
|
|
17975
18314
|
]),
|
|
17976
18315
|
path: string(),
|
|
17977
18316
|
message: string()
|
|
@@ -17992,7 +18331,8 @@ var CompositionUnavailableModeSchema = _enum([
|
|
|
17992
18331
|
"null",
|
|
17993
18332
|
"offline",
|
|
17994
18333
|
"unknown",
|
|
17995
|
-
"release"
|
|
18334
|
+
"release",
|
|
18335
|
+
"omit"
|
|
17996
18336
|
]);
|
|
17997
18337
|
/**
|
|
17998
18338
|
* `derived` reads a source · `clock` reads only `now`, recomputed when a sibling
|
|
@@ -18029,9 +18369,11 @@ var CompositionFieldPlanSchema = object({
|
|
|
18029
18369
|
reads: array(CompositionFieldReadSchema)
|
|
18030
18370
|
});
|
|
18031
18371
|
/**
|
|
18032
|
-
* What a feature does to its target (D663): `new` builds a capability on a
|
|
18033
|
-
* device; on an existing device it `add`s a capability the device lacks
|
|
18034
|
-
*
|
|
18372
|
+
* What a feature does to its target (D663, D944): `new` builds a capability on a
|
|
18373
|
+
* new device; on an existing device it `add`s a capability the device lacks.
|
|
18374
|
+
* Replacing a capability the device already serves is refused
|
|
18375
|
+
* (`native-capability-replace`), not planned. `replace` remains on the claim
|
|
18376
|
+
* wire for a claim the hub already holds.
|
|
18035
18377
|
*/
|
|
18036
18378
|
var CompositionFeatureModeSchema = _enum([
|
|
18037
18379
|
"new",
|
|
@@ -18202,8 +18544,392 @@ Object.freeze({
|
|
|
18202
18544
|
latchedAt: {
|
|
18203
18545
|
kind: "fixed",
|
|
18204
18546
|
result: ["number", "null"]
|
|
18547
|
+
},
|
|
18548
|
+
schmitt: {
|
|
18549
|
+
kind: "fixed",
|
|
18550
|
+
result: ["boolean", "null"]
|
|
18205
18551
|
}
|
|
18206
18552
|
});
|
|
18553
|
+
/**
|
|
18554
|
+
* One catalog row per base reading a group helper can build (D944 §3).
|
|
18555
|
+
* Data, not a form default written into the block.
|
|
18556
|
+
*/
|
|
18557
|
+
var NOW$2 = {
|
|
18558
|
+
fieldPath: "lastFetchedAt",
|
|
18559
|
+
expr: "now"
|
|
18560
|
+
};
|
|
18561
|
+
var CHANGED = {
|
|
18562
|
+
fieldPath: "lastChangedAt",
|
|
18563
|
+
expr: "now"
|
|
18564
|
+
};
|
|
18565
|
+
function numeric(input) {
|
|
18566
|
+
return {
|
|
18567
|
+
...input,
|
|
18568
|
+
family: "numeric",
|
|
18569
|
+
deviceType: DeviceType.Sensor,
|
|
18570
|
+
memberDeviceType: DeviceType.Sensor,
|
|
18571
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
18572
|
+
activeWhen: void 0,
|
|
18573
|
+
commandCaps: [],
|
|
18574
|
+
baseUnit: baseUnitOf(input.quantity),
|
|
18575
|
+
aggregates: NUMERIC_AGGREGATES,
|
|
18576
|
+
accept: [{
|
|
18577
|
+
cap: input.outputCap,
|
|
18578
|
+
fieldPath: input.outputField
|
|
18579
|
+
}]
|
|
18580
|
+
};
|
|
18581
|
+
}
|
|
18582
|
+
function binary(input) {
|
|
18583
|
+
return {
|
|
18584
|
+
...input,
|
|
18585
|
+
family: "binary",
|
|
18586
|
+
deviceType: DeviceType.Sensor,
|
|
18587
|
+
memberDeviceType: DeviceType.Sensor,
|
|
18588
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
18589
|
+
activeWhen: void 0,
|
|
18590
|
+
commandCaps: [],
|
|
18591
|
+
quantity: "none",
|
|
18592
|
+
baseUnit: void 0,
|
|
18593
|
+
aggregates: BINARY_AGGREGATES,
|
|
18594
|
+
empty: "offline"
|
|
18595
|
+
};
|
|
18596
|
+
}
|
|
18597
|
+
var GROUP_TEMPLATES = [
|
|
18598
|
+
numeric({
|
|
18599
|
+
id: "temperature",
|
|
18600
|
+
summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
|
|
18601
|
+
role: DeviceRole.TemperatureSensor,
|
|
18602
|
+
outputCap: "temperature-sensor",
|
|
18603
|
+
outputField: "celsius",
|
|
18604
|
+
quantity: "temperature",
|
|
18605
|
+
defaultAggregate: "mean",
|
|
18606
|
+
empty: "null",
|
|
18607
|
+
companions: [NOW$2]
|
|
18608
|
+
}),
|
|
18609
|
+
numeric({
|
|
18610
|
+
id: "humidity",
|
|
18611
|
+
summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18612
|
+
role: DeviceRole.HumiditySensor,
|
|
18613
|
+
outputCap: "humidity-sensor",
|
|
18614
|
+
outputField: "percent",
|
|
18615
|
+
quantity: "humidity",
|
|
18616
|
+
defaultAggregate: "mean",
|
|
18617
|
+
empty: "null",
|
|
18618
|
+
companions: [NOW$2]
|
|
18619
|
+
}),
|
|
18620
|
+
numeric({
|
|
18621
|
+
id: "pressure",
|
|
18622
|
+
summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
|
|
18623
|
+
role: DeviceRole.PressureSensor,
|
|
18624
|
+
outputCap: "pressure-sensor",
|
|
18625
|
+
outputField: "hpa",
|
|
18626
|
+
quantity: "pressure",
|
|
18627
|
+
defaultAggregate: "mean",
|
|
18628
|
+
empty: "null",
|
|
18629
|
+
companions: [NOW$2]
|
|
18630
|
+
}),
|
|
18631
|
+
numeric({
|
|
18632
|
+
id: "illuminance",
|
|
18633
|
+
summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
|
|
18634
|
+
role: DeviceRole.AmbientLightSensor,
|
|
18635
|
+
outputCap: "ambient-light-sensor",
|
|
18636
|
+
outputField: "lux",
|
|
18637
|
+
quantity: "illuminance",
|
|
18638
|
+
defaultAggregate: "mean",
|
|
18639
|
+
empty: "null",
|
|
18640
|
+
companions: [NOW$2]
|
|
18641
|
+
}),
|
|
18642
|
+
numeric({
|
|
18643
|
+
id: "power",
|
|
18644
|
+
summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
|
|
18645
|
+
role: DeviceRole.PowerSensor,
|
|
18646
|
+
outputCap: "power-meter",
|
|
18647
|
+
outputField: "watts",
|
|
18648
|
+
quantity: "power",
|
|
18649
|
+
defaultAggregate: "sum",
|
|
18650
|
+
empty: "omit",
|
|
18651
|
+
companions: [NOW$2]
|
|
18652
|
+
}),
|
|
18653
|
+
numeric({
|
|
18654
|
+
id: "energy",
|
|
18655
|
+
summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
|
|
18656
|
+
role: DeviceRole.EnergySensor,
|
|
18657
|
+
outputCap: "power-meter",
|
|
18658
|
+
outputField: "kwhTotal",
|
|
18659
|
+
quantity: "energy",
|
|
18660
|
+
defaultAggregate: "sum",
|
|
18661
|
+
empty: "omit",
|
|
18662
|
+
companions: [NOW$2]
|
|
18663
|
+
}),
|
|
18664
|
+
numeric({
|
|
18665
|
+
id: "battery",
|
|
18666
|
+
summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18667
|
+
role: DeviceRole.BatterySensor,
|
|
18668
|
+
outputCap: "battery",
|
|
18669
|
+
outputField: "percentage",
|
|
18670
|
+
quantity: "percentage",
|
|
18671
|
+
defaultAggregate: "mean",
|
|
18672
|
+
empty: "null",
|
|
18673
|
+
companions: [
|
|
18674
|
+
{
|
|
18675
|
+
fieldPath: "charging",
|
|
18676
|
+
expr: "\"unknown\""
|
|
18677
|
+
},
|
|
18678
|
+
{
|
|
18679
|
+
fieldPath: "sleeping",
|
|
18680
|
+
expr: "false"
|
|
18681
|
+
},
|
|
18682
|
+
{
|
|
18683
|
+
fieldPath: "lastUpdated",
|
|
18684
|
+
expr: "now"
|
|
18685
|
+
}
|
|
18686
|
+
]
|
|
18687
|
+
}),
|
|
18688
|
+
numeric({
|
|
18689
|
+
id: "numeric",
|
|
18690
|
+
summary: "A numeric reading in the members’ shared quantity. Mixed quantities are refused. Nobody reporting publishes null, not 0.",
|
|
18691
|
+
role: DeviceRole.NumericSensor,
|
|
18692
|
+
outputCap: "numeric-sensor",
|
|
18693
|
+
outputField: "value",
|
|
18694
|
+
quantity: "device",
|
|
18695
|
+
defaultAggregate: "mean",
|
|
18696
|
+
empty: "null",
|
|
18697
|
+
companions: [NOW$2]
|
|
18698
|
+
}),
|
|
18699
|
+
binary({
|
|
18700
|
+
id: "motion",
|
|
18701
|
+
summary: "Any or all motion. Offline and unknown members are left out, never read as off. Nobody reporting takes the device offline.",
|
|
18702
|
+
role: DeviceRole.MotionSensor,
|
|
18703
|
+
outputCap: "motion",
|
|
18704
|
+
outputField: "detected",
|
|
18705
|
+
defaultAggregate: "any",
|
|
18706
|
+
accept: [{
|
|
18707
|
+
cap: "motion",
|
|
18708
|
+
fieldPath: "detected"
|
|
18709
|
+
}],
|
|
18710
|
+
companions: [{
|
|
18711
|
+
fieldPath: "lastDetectedAt",
|
|
18712
|
+
expr: "null"
|
|
18713
|
+
}, {
|
|
18714
|
+
fieldPath: "autoClearAfterMs",
|
|
18715
|
+
expr: "null"
|
|
18716
|
+
}]
|
|
18717
|
+
}),
|
|
18718
|
+
binary({
|
|
18719
|
+
id: "contact",
|
|
18720
|
+
summary: "Any or all open contacts. Offline and unknown members are left out, never read as closed.",
|
|
18721
|
+
role: DeviceRole.ContactSensor,
|
|
18722
|
+
outputCap: "contact",
|
|
18723
|
+
outputField: "entryOpen",
|
|
18724
|
+
defaultAggregate: "any",
|
|
18725
|
+
accept: [{
|
|
18726
|
+
cap: "contact",
|
|
18727
|
+
fieldPath: "entryOpen"
|
|
18728
|
+
}],
|
|
18729
|
+
companions: [CHANGED]
|
|
18730
|
+
}),
|
|
18731
|
+
binary({
|
|
18732
|
+
id: "leak",
|
|
18733
|
+
summary: "Any or all leak detections. Offline and unknown members are left out, never read as dry.",
|
|
18734
|
+
role: DeviceRole.LeakSensor,
|
|
18735
|
+
outputCap: "flood",
|
|
18736
|
+
outputField: "flooded",
|
|
18737
|
+
defaultAggregate: "any",
|
|
18738
|
+
accept: [{
|
|
18739
|
+
cap: "flood",
|
|
18740
|
+
fieldPath: "flooded"
|
|
18741
|
+
}],
|
|
18742
|
+
companions: [CHANGED]
|
|
18743
|
+
}),
|
|
18744
|
+
binary({
|
|
18745
|
+
id: "smoke",
|
|
18746
|
+
summary: "Any or all smoke detections. Offline and unknown members are left out, never read as clear.",
|
|
18747
|
+
role: DeviceRole.SmokeSensor,
|
|
18748
|
+
outputCap: "smoke",
|
|
18749
|
+
outputField: "detected",
|
|
18750
|
+
defaultAggregate: "any",
|
|
18751
|
+
accept: [{
|
|
18752
|
+
cap: "smoke",
|
|
18753
|
+
fieldPath: "detected"
|
|
18754
|
+
}],
|
|
18755
|
+
companions: [CHANGED]
|
|
18756
|
+
}),
|
|
18757
|
+
binary({
|
|
18758
|
+
id: "occupancy",
|
|
18759
|
+
summary: "Any or all of motion or binary sensors, published as a binary. Presence zones are not aggregated. Nobody reporting is offline, not off.",
|
|
18760
|
+
role: DeviceRole.BinarySensor,
|
|
18761
|
+
outputCap: "binary",
|
|
18762
|
+
outputField: "on",
|
|
18763
|
+
defaultAggregate: "any",
|
|
18764
|
+
accept: [{
|
|
18765
|
+
cap: "motion",
|
|
18766
|
+
fieldPath: "detected"
|
|
18767
|
+
}, {
|
|
18768
|
+
cap: "binary",
|
|
18769
|
+
fieldPath: "on"
|
|
18770
|
+
}],
|
|
18771
|
+
companions: [CHANGED]
|
|
18772
|
+
}),
|
|
18773
|
+
binary({
|
|
18774
|
+
id: "binary",
|
|
18775
|
+
summary: "Any or all generic binary sensors. Offline and unknown members are left out, never read as off.",
|
|
18776
|
+
role: DeviceRole.BinarySensor,
|
|
18777
|
+
outputCap: "binary",
|
|
18778
|
+
outputField: "on",
|
|
18779
|
+
defaultAggregate: "any",
|
|
18780
|
+
accept: [{
|
|
18781
|
+
cap: "binary",
|
|
18782
|
+
fieldPath: "on"
|
|
18783
|
+
}],
|
|
18784
|
+
companions: [CHANGED]
|
|
18785
|
+
}),
|
|
18786
|
+
actuator({
|
|
18787
|
+
id: "switch",
|
|
18788
|
+
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.",
|
|
18789
|
+
deviceType: DeviceType.Switch,
|
|
18790
|
+
outputCap: "switch",
|
|
18791
|
+
outputField: "on",
|
|
18792
|
+
member: {
|
|
18793
|
+
cap: "switch",
|
|
18794
|
+
fieldPath: "on"
|
|
18795
|
+
},
|
|
18796
|
+
commandCaps: ["switch"]
|
|
18797
|
+
}),
|
|
18798
|
+
actuator({
|
|
18799
|
+
id: "light",
|
|
18800
|
+
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.",
|
|
18801
|
+
deviceType: DeviceType.Light,
|
|
18802
|
+
outputCap: "switch",
|
|
18803
|
+
outputField: "on",
|
|
18804
|
+
member: {
|
|
18805
|
+
cap: "switch",
|
|
18806
|
+
fieldPath: "on"
|
|
18807
|
+
},
|
|
18808
|
+
commandCaps: [
|
|
18809
|
+
"switch",
|
|
18810
|
+
"brightness",
|
|
18811
|
+
"color"
|
|
18812
|
+
]
|
|
18813
|
+
}),
|
|
18814
|
+
actuator({
|
|
18815
|
+
id: "fan",
|
|
18816
|
+
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.",
|
|
18817
|
+
deviceType: DeviceType.Fan,
|
|
18818
|
+
outputCap: "switch",
|
|
18819
|
+
outputField: "on",
|
|
18820
|
+
member: {
|
|
18821
|
+
cap: "switch",
|
|
18822
|
+
fieldPath: "on"
|
|
18823
|
+
},
|
|
18824
|
+
commandCaps: ["switch", "fan-control"]
|
|
18825
|
+
}),
|
|
18826
|
+
actuator({
|
|
18827
|
+
id: "cover",
|
|
18828
|
+
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.",
|
|
18829
|
+
deviceType: DeviceType.Cover,
|
|
18830
|
+
outputCap: "group-active",
|
|
18831
|
+
outputField: "active",
|
|
18832
|
+
member: {
|
|
18833
|
+
cap: "cover",
|
|
18834
|
+
fieldPath: "state"
|
|
18835
|
+
},
|
|
18836
|
+
activeWhen: [
|
|
18837
|
+
"open",
|
|
18838
|
+
"opening",
|
|
18839
|
+
"stopped"
|
|
18840
|
+
],
|
|
18841
|
+
commandCaps: ["cover"]
|
|
18842
|
+
}),
|
|
18843
|
+
actuator({
|
|
18844
|
+
id: "valve",
|
|
18845
|
+
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.",
|
|
18846
|
+
deviceType: DeviceType.Valve,
|
|
18847
|
+
outputCap: "group-active",
|
|
18848
|
+
outputField: "active",
|
|
18849
|
+
member: {
|
|
18850
|
+
cap: "valve",
|
|
18851
|
+
fieldPath: "state"
|
|
18852
|
+
},
|
|
18853
|
+
activeWhen: [
|
|
18854
|
+
"open",
|
|
18855
|
+
"opening",
|
|
18856
|
+
"stopped"
|
|
18857
|
+
],
|
|
18858
|
+
commandCaps: ["valve"]
|
|
18859
|
+
}),
|
|
18860
|
+
actuator({
|
|
18861
|
+
id: "lock",
|
|
18862
|
+
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.",
|
|
18863
|
+
deviceType: DeviceType.Lock,
|
|
18864
|
+
outputCap: "group-active",
|
|
18865
|
+
outputField: "active",
|
|
18866
|
+
member: {
|
|
18867
|
+
cap: "lock-control",
|
|
18868
|
+
fieldPath: "state"
|
|
18869
|
+
},
|
|
18870
|
+
activeWhen: ["locked"],
|
|
18871
|
+
commandCaps: ["lock-control"]
|
|
18872
|
+
}),
|
|
18873
|
+
actuator({
|
|
18874
|
+
id: "media-player",
|
|
18875
|
+
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.",
|
|
18876
|
+
deviceType: DeviceType.MediaPlayer,
|
|
18877
|
+
outputCap: "group-active",
|
|
18878
|
+
outputField: "active",
|
|
18879
|
+
member: {
|
|
18880
|
+
cap: "media-player",
|
|
18881
|
+
fieldPath: "state"
|
|
18882
|
+
},
|
|
18883
|
+
activeWhen: ["playing", "buffering"],
|
|
18884
|
+
commandCaps: ["media-player"]
|
|
18885
|
+
}),
|
|
18886
|
+
actuator({
|
|
18887
|
+
id: "climate",
|
|
18888
|
+
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.",
|
|
18889
|
+
deviceType: DeviceType.Thermostat,
|
|
18890
|
+
memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
|
|
18891
|
+
outputCap: "group-active",
|
|
18892
|
+
outputField: "active",
|
|
18893
|
+
member: {
|
|
18894
|
+
cap: "climate-control",
|
|
18895
|
+
fieldPath: "mode"
|
|
18896
|
+
},
|
|
18897
|
+
activeWhen: [
|
|
18898
|
+
"heat",
|
|
18899
|
+
"cool",
|
|
18900
|
+
"auto",
|
|
18901
|
+
"heat_cool",
|
|
18902
|
+
"fan_only",
|
|
18903
|
+
"dry"
|
|
18904
|
+
],
|
|
18905
|
+
commandCaps: ["climate-control"]
|
|
18906
|
+
})
|
|
18907
|
+
];
|
|
18908
|
+
function actuator(input) {
|
|
18909
|
+
return {
|
|
18910
|
+
id: input.id,
|
|
18911
|
+
summary: input.summary,
|
|
18912
|
+
deviceType: input.deviceType,
|
|
18913
|
+
outputCap: input.outputCap,
|
|
18914
|
+
outputField: input.outputField,
|
|
18915
|
+
commandCaps: input.commandCaps,
|
|
18916
|
+
family: "actuator",
|
|
18917
|
+
role: void 0,
|
|
18918
|
+
memberDeviceType: input.deviceType,
|
|
18919
|
+
memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
|
|
18920
|
+
activeWhen: input.activeWhen,
|
|
18921
|
+
quantity: "none",
|
|
18922
|
+
baseUnit: void 0,
|
|
18923
|
+
aggregates: BINARY_AGGREGATES,
|
|
18924
|
+
defaultAggregate: "any",
|
|
18925
|
+
empty: "offline",
|
|
18926
|
+
accept: [input.member],
|
|
18927
|
+
companions: [CHANGED]
|
|
18928
|
+
};
|
|
18929
|
+
}
|
|
18930
|
+
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
18931
|
+
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
18932
|
+
DeviceType.Camera;
|
|
18207
18933
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
18208
18934
|
/**
|
|
18209
18935
|
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
@@ -19917,12 +20643,12 @@ method(object({ deviceId: number().int().nonnegative() }), DeviceAdminLinkSchema
|
|
|
19917
20643
|
* providers detect renames by matching `uniqueId` and patch `id`
|
|
19918
20644
|
* in-place so dispatch keeps working.
|
|
19919
20645
|
*
|
|
19920
|
-
* **
|
|
19921
|
-
*
|
|
19922
|
-
*
|
|
19923
|
-
*
|
|
19924
|
-
* (`ha-domain-mapping`)
|
|
19925
|
-
* stored after that point.
|
|
20646
|
+
* **Display unit and precision are NOT stored here.** They are a read-time
|
|
20647
|
+
* preference (D944). A polymorphic reading stores its `quantity` here once,
|
|
20648
|
+
* at adopt, and a date/time value stores `dateTimeShape` the same way —
|
|
20649
|
+
* neither is repeated on each sample. The `device_class` tag is an
|
|
20650
|
+
* HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
|
|
20651
|
+
* to derive the CamStack `DeviceRole`; it is never stored after that point.
|
|
19926
20652
|
*
|
|
19927
20653
|
* The `raw` escape hatch carries provider-specific attributes that don't
|
|
19928
20654
|
* fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
|
|
@@ -19936,6 +20662,14 @@ var SourceInfoSchema = object({
|
|
|
19936
20662
|
/** Immutable upstream identifier when available (HA `unique_id`, … ).
|
|
19937
20663
|
* Used to detect rename when `id` changes. */
|
|
19938
20664
|
uniqueId: string().optional(),
|
|
20665
|
+
/** Quantity of a polymorphic reading, written once at adopt (D944). */
|
|
20666
|
+
quantity: ReadingQuantitySchema.optional(),
|
|
20667
|
+
/** Date / time / datetime shape of a string reading, written once at adopt. */
|
|
20668
|
+
dateTimeShape: _enum([
|
|
20669
|
+
"date",
|
|
20670
|
+
"time",
|
|
20671
|
+
"datetime"
|
|
20672
|
+
]).optional(),
|
|
19939
20673
|
/** Provider-specific extras that don't fit the structured slots.
|
|
19940
20674
|
* Whitelist what goes here per provider — do NOT dump entire HA
|
|
19941
20675
|
* attribute blobs (would explode the meta JSON column). */
|
|
@@ -41040,7 +41774,8 @@ method(object({
|
|
|
41040
41774
|
"enum"
|
|
41041
41775
|
]),
|
|
41042
41776
|
enumValues: array(string()).optional(),
|
|
41043
|
-
item: boolean().optional()
|
|
41777
|
+
item: boolean().optional(),
|
|
41778
|
+
quantity: ReadingQuantitySchema.optional()
|
|
41044
41779
|
})).readonly(),
|
|
41045
41780
|
itemArray: object({
|
|
41046
41781
|
path: string(),
|
|
@@ -43401,7 +44136,7 @@ var NetworkAccessStatusSchema = object({
|
|
|
43401
44136
|
});
|
|
43402
44137
|
/**
|
|
43403
44138
|
* Optional, richer endpoint shape returned by providers that expose
|
|
43404
|
-
* MORE than one ingress concurrently (Tailscale
|
|
44139
|
+
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
43405
44140
|
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
43406
44141
|
* the originating provider config (mode + sourcePort) so the
|
|
43407
44142
|
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
@@ -44884,8 +45619,8 @@ var MaskPolygonShapeSchema = object({
|
|
|
44884
45619
|
/** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
|
|
44885
45620
|
var MaskGridShapeSchema = object({
|
|
44886
45621
|
kind: literal("grid"),
|
|
44887
|
-
gridWidth: number(),
|
|
44888
|
-
gridHeight: number(),
|
|
45622
|
+
gridWidth: number().meta(readingMeta("none")),
|
|
45623
|
+
gridHeight: number().meta(readingMeta("none")),
|
|
44889
45624
|
cells: array(boolean())
|
|
44890
45625
|
});
|
|
44891
45626
|
discriminatedUnion("kind", [
|
|
@@ -44997,6 +45732,8 @@ var NcSystemEventKindSchema = _enum([
|
|
|
44997
45732
|
"detection-blind",
|
|
44998
45733
|
"motion-noisy",
|
|
44999
45734
|
"motion-calm",
|
|
45735
|
+
"package-llm-silent",
|
|
45736
|
+
"package-llm-answered",
|
|
45000
45737
|
"recording-stalled",
|
|
45001
45738
|
"recording-healthy",
|
|
45002
45739
|
"recording-degraded",
|
|
@@ -46086,7 +46823,20 @@ var NcRuleInputSchema = object({
|
|
|
46086
46823
|
* save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
|
|
46087
46824
|
* path does not run Zod defaults.
|
|
46088
46825
|
*/
|
|
46089
|
-
groupingSec: number().int().min(5).max(60).optional()
|
|
46826
|
+
groupingSec: number().int().min(5).max(60).optional(),
|
|
46827
|
+
/**
|
|
46828
|
+
* ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
|
|
46829
|
+
* carries "Correct" / "Wrong" buttons (and, where the channel has room, a
|
|
46830
|
+
* "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
|
|
46831
|
+
* same mark the Library writes, and records whose notification it was and
|
|
46832
|
+
* when. A channel with no buttons gets the same two choices as links.
|
|
46833
|
+
*
|
|
46834
|
+
* `.optional()`, never `.default()` (a Zod default does not run on the addon
|
|
46835
|
+
* cap path): absent and `false` both mean off, which is what every rule
|
|
46836
|
+
* authored before this field meant. An editor turns it off by writing
|
|
46837
|
+
* `false`, so it needs no `clear` key.
|
|
46838
|
+
*/
|
|
46839
|
+
askVerdict: boolean().optional()
|
|
46090
46840
|
});
|
|
46091
46841
|
/**
|
|
46092
46842
|
* Partial patch for `updateRule` — any subset of the input fields, plus the
|
|
@@ -56192,6 +56942,28 @@ var PackageVersionInfoSchema = object({
|
|
|
56192
56942
|
deprecated: string().optional(),
|
|
56193
56943
|
distTags: array(string()).readonly()
|
|
56194
56944
|
});
|
|
56945
|
+
/**
|
|
56946
|
+
* One published document. `ok` is markdown from the registry. `missing` means
|
|
56947
|
+
* the registry answered and the file was not there. `error` is a registry or
|
|
56948
|
+
* tarball failure — never an empty string standing in for either.
|
|
56949
|
+
*/
|
|
56950
|
+
var PackageDocBodySchema = discriminatedUnion("status", [
|
|
56951
|
+
object({
|
|
56952
|
+
status: literal("ok"),
|
|
56953
|
+
markdown: string()
|
|
56954
|
+
}),
|
|
56955
|
+
object({ status: literal("missing") }),
|
|
56956
|
+
object({
|
|
56957
|
+
status: literal("error"),
|
|
56958
|
+
message: string()
|
|
56959
|
+
})
|
|
56960
|
+
]);
|
|
56961
|
+
var PackageDocsSchema = object({
|
|
56962
|
+
name: string(),
|
|
56963
|
+
version: string(),
|
|
56964
|
+
readme: PackageDocBodySchema,
|
|
56965
|
+
changelog: PackageDocBodySchema
|
|
56966
|
+
});
|
|
56195
56967
|
var SearchResultSchema = object({
|
|
56196
56968
|
name: string(),
|
|
56197
56969
|
version: string(),
|
|
@@ -56410,7 +57182,10 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
56410
57182
|
}), object({ success: literal(true) }), {
|
|
56411
57183
|
kind: "mutation",
|
|
56412
57184
|
auth: "admin"
|
|
56413
|
-
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
|
|
57185
|
+
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
|
|
57186
|
+
name: string().min(1),
|
|
57187
|
+
version: string().min(1).optional()
|
|
57188
|
+
}), PackageDocsSchema, { auth: "admin" }), method(object({ addonId: string() }), RestartAddonResultSchema, {
|
|
56414
57189
|
kind: "mutation",
|
|
56415
57190
|
auth: "admin"
|
|
56416
57191
|
}), method(object({ packageName: string() }), object({ success: literal(true) }), {
|
|
@@ -56469,27 +57244,17 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
56469
57244
|
*/
|
|
56470
57245
|
var AirQualitySensorStatusSchema = object({
|
|
56471
57246
|
/** Carbon dioxide concentration in ppm. */
|
|
56472
|
-
co2Ppm: number().min(0).optional(),
|
|
57247
|
+
co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
56473
57248
|
/** Total volatile organic compounds in ppb. */
|
|
56474
|
-
vocPpb: number().min(0).optional(),
|
|
57249
|
+
vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
56475
57250
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
56476
|
-
pm25: number().min(0).optional(),
|
|
57251
|
+
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56477
57252
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
56478
|
-
pm10: number().min(0).optional(),
|
|
57253
|
+
pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56479
57254
|
/** Composite AQI value (typically 0..500). */
|
|
56480
|
-
aqi: number().optional(),
|
|
57255
|
+
aqi: number().optional().meta(readingMeta("index")),
|
|
56481
57256
|
/** Ms epoch when the slice was last updated. */
|
|
56482
|
-
lastFetchedAt: number()
|
|
56483
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
56484
|
-
* `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
|
|
56485
|
-
* upstream `sensor.*` entity surfaces ONE device_class, so one unit
|
|
56486
|
-
* per slice is unambiguous. */
|
|
56487
|
-
unit: string().optional(),
|
|
56488
|
-
/** Suggested decimal places for numeric display.
|
|
56489
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
56490
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
56491
|
-
* auto-formatting when absent. */
|
|
56492
|
-
precision: number().int().min(0).max(10).optional()
|
|
57257
|
+
lastFetchedAt: number()
|
|
56493
57258
|
});
|
|
56494
57259
|
var airQualitySensorCapability = {
|
|
56495
57260
|
name: "air-quality-sensor",
|
|
@@ -56526,12 +57291,12 @@ var airQualitySensorCapability = {
|
|
|
56526
57291
|
},
|
|
56527
57292
|
pm25: {
|
|
56528
57293
|
kind: "gauge",
|
|
56529
|
-
unit: "
|
|
57294
|
+
unit: "µg/m³",
|
|
56530
57295
|
deadband: 1
|
|
56531
57296
|
},
|
|
56532
57297
|
pm10: {
|
|
56533
57298
|
kind: "gauge",
|
|
56534
|
-
unit: "
|
|
57299
|
+
unit: "µg/m³",
|
|
56535
57300
|
deadband: 1
|
|
56536
57301
|
},
|
|
56537
57302
|
aqi: {
|
|
@@ -56549,22 +57314,13 @@ var airQualitySensorCapability = {
|
|
|
56549
57314
|
* entries with `device_class: illuminance`.
|
|
56550
57315
|
*/
|
|
56551
57316
|
var AmbientLightSensorStatusSchema = object({
|
|
56552
|
-
/** Current illuminance in lux (lx). */
|
|
56553
|
-
lux: number().min(0).meta({
|
|
56554
|
-
|
|
57317
|
+
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
57318
|
+
lux: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
57319
|
+
precision: 0,
|
|
56555
57320
|
label: "Illuminance"
|
|
56556
|
-
}),
|
|
57321
|
+
})),
|
|
56557
57322
|
/** Ms epoch when the slice was last updated. */
|
|
56558
|
-
lastFetchedAt: number()
|
|
56559
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
56560
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
56561
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
56562
|
-
unit: string().optional(),
|
|
56563
|
-
/** Suggested decimal places for numeric display.
|
|
56564
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
56565
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
56566
|
-
* auto-formatting when absent. */
|
|
56567
|
-
precision: number().int().min(0).max(10).optional()
|
|
57323
|
+
lastFetchedAt: number()
|
|
56568
57324
|
});
|
|
56569
57325
|
var ambientLightSensorCapability = {
|
|
56570
57326
|
name: "ambient-light-sensor",
|
|
@@ -56596,12 +57352,7 @@ var ambientLightSensorCapability = {
|
|
|
56596
57352
|
} } },
|
|
56597
57353
|
/** Clock fields: written, but excluded from the compare that decides
|
|
56598
57354
|
* whether persisting is worth a SQLite commit. */
|
|
56599
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
56600
|
-
customize: {
|
|
56601
|
-
version: 1,
|
|
56602
|
-
advanced: "derive",
|
|
56603
|
-
targets: []
|
|
56604
|
-
}
|
|
57355
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
56605
57356
|
};
|
|
56606
57357
|
/**
|
|
56607
57358
|
* Per-class audio metrics aggregated over a sliding window.
|
|
@@ -56828,10 +57579,7 @@ var BatteryStatusSchema = object({
|
|
|
56828
57579
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
56829
57580
|
* declines to band a non-finite reading, which is the correct shape.
|
|
56830
57581
|
*/
|
|
56831
|
-
percentage: number().min(0).max(100).nullable().meta({
|
|
56832
|
-
unit: "%",
|
|
56833
|
-
label: "Charge"
|
|
56834
|
-
}),
|
|
57582
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
56835
57583
|
/**
|
|
56836
57584
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
56837
57585
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -56858,7 +57606,14 @@ var BatteryStatusSchema = object({
|
|
|
56858
57606
|
*/
|
|
56859
57607
|
sleeping: boolean().meta({ label: "Sleeping" }),
|
|
56860
57608
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
56861
|
-
lastUpdated: number(),
|
|
57609
|
+
lastUpdated: number().meta(readingMeta("none")),
|
|
57610
|
+
/**
|
|
57611
|
+
* Battery voltage in volts, for a cell the firmware reports as voltage
|
|
57612
|
+
* rather than a percentage (Ecowitt capacitor packs). Absent on cameras
|
|
57613
|
+
* that only report charge. A second reading on this cap, not a second
|
|
57614
|
+
* `numeric-sensor` on the same device.
|
|
57615
|
+
*/
|
|
57616
|
+
volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
56862
57617
|
/**
|
|
56863
57618
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
56864
57619
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -56905,12 +57660,12 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
56905
57660
|
DeviceType.Switch,
|
|
56906
57661
|
DeviceType.PetFeeder,
|
|
56907
57662
|
DeviceType.Container,
|
|
56908
|
-
DeviceType.Thermostat
|
|
57663
|
+
DeviceType.Thermostat,
|
|
57664
|
+
DeviceType.EventEmitter
|
|
56909
57665
|
];
|
|
56910
57666
|
/** Generic Customize target for the percentage reading (D936). */
|
|
56911
57667
|
var batteryCustomize = {
|
|
56912
57668
|
version: 1,
|
|
56913
|
-
advanced: "derive",
|
|
56914
57669
|
targets: [{
|
|
56915
57670
|
id: "percentage",
|
|
56916
57671
|
cardinality: "scalar",
|
|
@@ -56919,7 +57674,7 @@ var batteryCustomize = {
|
|
|
56919
57674
|
stateField: "percentage"
|
|
56920
57675
|
},
|
|
56921
57676
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
56922
|
-
apply: "add
|
|
57677
|
+
apply: "add",
|
|
56923
57678
|
path: "percentage",
|
|
56924
57679
|
sources: [{
|
|
56925
57680
|
kind: "field",
|
|
@@ -57040,10 +57795,16 @@ onStatusChanged: { data: object({
|
|
|
57040
57795
|
/**
|
|
57041
57796
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
57042
57797
|
*/
|
|
57043
|
-
history: { fields: {
|
|
57044
|
-
|
|
57045
|
-
|
|
57046
|
-
|
|
57798
|
+
history: { fields: {
|
|
57799
|
+
percentage: {
|
|
57800
|
+
kind: "gauge",
|
|
57801
|
+
unit: "%"
|
|
57802
|
+
},
|
|
57803
|
+
volts: {
|
|
57804
|
+
kind: "gauge",
|
|
57805
|
+
unit: "V"
|
|
57806
|
+
}
|
|
57807
|
+
} },
|
|
57047
57808
|
customize: batteryCustomize,
|
|
57048
57809
|
/** Clock fields: written, but excluded from the compare that decides
|
|
57049
57810
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -57090,12 +57851,7 @@ var binaryCapability = {
|
|
|
57090
57851
|
history: { fields: { on: { kind: "boolean" } } },
|
|
57091
57852
|
/** Clock fields: written, but excluded from the compare that decides
|
|
57092
57853
|
* whether persisting is worth a SQLite commit. */
|
|
57093
|
-
volatileStateFields: ["lastChangedAt"]
|
|
57094
|
-
customize: {
|
|
57095
|
-
version: 1,
|
|
57096
|
-
advanced: "derive",
|
|
57097
|
-
targets: []
|
|
57098
|
-
}
|
|
57854
|
+
volatileStateFields: ["lastChangedAt"]
|
|
57099
57855
|
};
|
|
57100
57856
|
/**
|
|
57101
57857
|
* Dimmable-light brightness control. Co-exists with `switch` on the
|
|
@@ -57111,7 +57867,7 @@ var binaryCapability = {
|
|
|
57111
57867
|
*/
|
|
57112
57868
|
var BrightnessStatusSchema = object({
|
|
57113
57869
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
57114
|
-
percentage: number().min(0).max(100),
|
|
57870
|
+
percentage: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
57115
57871
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
57116
57872
|
lastChangedAt: number()
|
|
57117
57873
|
});
|
|
@@ -57565,21 +58321,21 @@ var ClimateControlStatusSchema = object({
|
|
|
57565
58321
|
/** Single setpoint in Celsius. Used by single-target modes
|
|
57566
58322
|
* (heat / cool / dry). Null when the device is in a range mode
|
|
57567
58323
|
* (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
|
|
57568
|
-
target: number().nullable(),
|
|
58324
|
+
target: number().nullable().meta(readingMeta("temperature")),
|
|
57569
58325
|
/** Upper bound of the dual setpoint in Celsius. Populated only
|
|
57570
58326
|
* in `heat_cool` mode. */
|
|
57571
|
-
targetHigh: number().nullable(),
|
|
58327
|
+
targetHigh: number().nullable().meta(readingMeta("temperature")),
|
|
57572
58328
|
/** Lower bound of the dual setpoint in Celsius. Populated only
|
|
57573
58329
|
* in `heat_cool` mode. */
|
|
57574
|
-
targetLow: number().nullable(),
|
|
58330
|
+
targetLow: number().nullable().meta(readingMeta("temperature")),
|
|
57575
58331
|
/** Target relative humidity (0..100). Null when the device has no
|
|
57576
58332
|
* humidity control. */
|
|
57577
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
58333
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
57578
58334
|
/** Current measured temperature in Celsius. Read-only — pushed by
|
|
57579
58335
|
* the device's internal sensor. */
|
|
57580
|
-
currentTemp: number().nullable(),
|
|
58336
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
57581
58337
|
/** Current measured relative humidity (0..100). Read-only. */
|
|
57582
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
58338
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
57583
58339
|
/** Vertical louver swing on/off. Null when the device has no vertical
|
|
57584
58340
|
* swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
|
|
57585
58341
|
swingVertical: boolean().nullable(),
|
|
@@ -57587,11 +58343,11 @@ var ClimateControlStatusSchema = object({
|
|
|
57587
58343
|
* horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
|
|
57588
58344
|
swingHorizontal: boolean().nullable(),
|
|
57589
58345
|
/** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
57590
|
-
minTemp: number().nullable().optional(),
|
|
58346
|
+
minTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
57591
58347
|
/** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
57592
|
-
maxTemp: number().nullable().optional(),
|
|
58348
|
+
maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
57593
58349
|
/** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
|
|
57594
|
-
tempStep: number().positive().nullable().optional(),
|
|
58350
|
+
tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
|
|
57595
58351
|
/** Ms epoch when the slice was last updated (push or command). */
|
|
57596
58352
|
lastFetchedAt: number()
|
|
57597
58353
|
});
|
|
@@ -57733,17 +58489,17 @@ var climateControlCapability = {
|
|
|
57733
58489
|
* modes to expose in the picker without a slice fetch.
|
|
57734
58490
|
*/
|
|
57735
58491
|
var RgbTripletSchema = object({
|
|
57736
|
-
r: number().int().min(0).max(255),
|
|
57737
|
-
g: number().int().min(0).max(255),
|
|
57738
|
-
b: number().int().min(0).max(255)
|
|
58492
|
+
r: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
58493
|
+
g: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
58494
|
+
b: number().int().min(0).max(255).meta(readingMeta("none"))
|
|
57739
58495
|
});
|
|
57740
58496
|
var HsvTripletSchema = object({
|
|
57741
58497
|
/** Hue in degrees, 0..360. */
|
|
57742
|
-
h: number().min(0).max(360),
|
|
58498
|
+
h: number().min(0).max(360).meta(readingMeta("none")),
|
|
57743
58499
|
/** Saturation as 0..100 inclusive. */
|
|
57744
|
-
s: number().min(0).max(100),
|
|
58500
|
+
s: number().min(0).max(100).meta(readingMeta("none")),
|
|
57745
58501
|
/** Value/brightness as 0..100 inclusive. */
|
|
57746
|
-
v: number().min(0).max(100)
|
|
58502
|
+
v: number().min(0).max(100).meta(readingMeta("none"))
|
|
57747
58503
|
});
|
|
57748
58504
|
var ColorInputSchema = discriminatedUnion("mode", [
|
|
57749
58505
|
object({
|
|
@@ -57772,7 +58528,7 @@ var ColorStatusSchema = object({
|
|
|
57772
58528
|
/** Populated when `mode === 'hsv'`. */
|
|
57773
58529
|
hsv: HsvTripletSchema.optional(),
|
|
57774
58530
|
/** Populated when `mode === 'mired'`. */
|
|
57775
|
-
mireds: number().int().optional(),
|
|
58531
|
+
mireds: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
57776
58532
|
/** Ms epoch of the last operator-driven change. */
|
|
57777
58533
|
lastChangedAt: number()
|
|
57778
58534
|
});
|
|
@@ -57975,18 +58731,12 @@ _enum([
|
|
|
57975
58731
|
"empty",
|
|
57976
58732
|
"full"
|
|
57977
58733
|
]);
|
|
57978
|
-
/**
|
|
57979
|
-
|
|
57980
|
-
|
|
57981
|
-
"days",
|
|
57982
|
-
"hours",
|
|
57983
|
-
"cycles"
|
|
57984
|
-
]);
|
|
57985
|
-
/** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
|
|
57986
|
-
* days). `value` null = unknown (D393). */
|
|
58734
|
+
/** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
|
|
58735
|
+
var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
|
|
58736
|
+
/** Remaining life. `value` null = unknown (D393). Duration is seconds. */
|
|
57987
58737
|
var ConsumableRemainingSchema = object({
|
|
57988
|
-
value: number().min(0).nullable(),
|
|
57989
|
-
|
|
58738
|
+
value: number().min(0).nullable().meta(readingMeta("device")),
|
|
58739
|
+
kind: ConsumableRemainingKindSchema
|
|
57990
58740
|
});
|
|
57991
58741
|
/** A single consumable item. Either a continuous `level` (remaining
|
|
57992
58742
|
* life %, or fill % for a `supply` / `waste` item) or a discrete `status`
|
|
@@ -57999,7 +58749,7 @@ var ConsumableItemSchema = object({
|
|
|
57999
58749
|
/** Display name. */
|
|
58000
58750
|
label: string().min(1),
|
|
58001
58751
|
/** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
|
|
58002
|
-
level: number().min(0).max(100).nullable(),
|
|
58752
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58003
58753
|
/** The provider's discrete verdict when known (binary mode). */
|
|
58004
58754
|
status: ConsumableStatusSchema.nullable(),
|
|
58005
58755
|
/** What the item is — decides whether low or full is bad. Absent = `wear`. */
|
|
@@ -58081,8 +58831,8 @@ var consumablesCustomize = {
|
|
|
58081
58831
|
transforms: [{ kind: "identity" }]
|
|
58082
58832
|
}],
|
|
58083
58833
|
literals: [{
|
|
58084
|
-
path: "remaining.
|
|
58085
|
-
values: [...
|
|
58834
|
+
path: "remaining.kind",
|
|
58835
|
+
values: [...ConsumableRemainingKindSchema.options]
|
|
58086
58836
|
}]
|
|
58087
58837
|
},
|
|
58088
58838
|
{
|
|
@@ -58211,7 +58961,6 @@ reset: method(object({
|
|
|
58211
58961
|
},
|
|
58212
58962
|
"remaining.value": {
|
|
58213
58963
|
kind: "gauge",
|
|
58214
|
-
unitFrom: "remaining.unit",
|
|
58215
58964
|
deadband: 1,
|
|
58216
58965
|
displayName: "Remaining"
|
|
58217
58966
|
}
|
|
@@ -58281,10 +59030,9 @@ var contactCapability = {
|
|
|
58281
59030
|
* (empty array for the other kinds). It lives in the slice because
|
|
58282
59031
|
* HA `select.*` entities may update their options at runtime.
|
|
58283
59032
|
*
|
|
58284
|
-
*
|
|
58285
|
-
*
|
|
58286
|
-
*
|
|
58287
|
-
* push — consistent with `options` and the sensor-unit approach.
|
|
59033
|
+
* Numeric `min` / `max` / `step` live in the slice and share the device
|
|
59034
|
+
* quantity (`SourceInfo.quantity`, D944). Display precision is a read-time
|
|
59035
|
+
* preference. A datetime control's shape is `SourceInfo.dateTimeShape`.
|
|
58288
59036
|
*/
|
|
58289
59037
|
var ControlStatusSchema = object({
|
|
58290
59038
|
kind: _enum([
|
|
@@ -58296,39 +59044,17 @@ var ControlStatusSchema = object({
|
|
|
58296
59044
|
/** Current value. Numeric kind → number; select/text/datetime → string.
|
|
58297
59045
|
* Datetime values are ISO 8601 strings (full timestamp, date-only,
|
|
58298
59046
|
* or time-only — HA accepts all three forms). */
|
|
58299
|
-
value: union([number(), string()]),
|
|
59047
|
+
value: union([number().meta(readingMeta("device")), string()]),
|
|
58300
59048
|
/** Acceptable values when `kind === 'select'`. Empty for other kinds. */
|
|
58301
59049
|
options: array(string()),
|
|
58302
59050
|
/** Ms epoch when the slice was last updated. */
|
|
58303
59051
|
lastChangedAt: number(),
|
|
58304
|
-
/**
|
|
58305
|
-
|
|
58306
|
-
|
|
58307
|
-
|
|
58308
|
-
/**
|
|
58309
|
-
|
|
58310
|
-
min: number().optional(),
|
|
58311
|
-
/** Maximum allowed value. Populated live from HA `attributes.max`.
|
|
58312
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58313
|
-
max: number().optional(),
|
|
58314
|
-
/** Step increment. Populated live from HA `attributes.step`.
|
|
58315
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58316
|
-
step: number().optional(),
|
|
58317
|
-
/** Suggested decimal places for numeric display. Populated live from HA
|
|
58318
|
-
* `attributes.suggested_display_precision`, or derived from `step` when the
|
|
58319
|
-
* HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
|
|
58320
|
-
* other kinds. Falls back to auto-formatting when absent. */
|
|
58321
|
-
precision: number().int().min(0).max(10).optional(),
|
|
58322
|
-
/** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
|
|
58323
|
-
* 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
|
|
58324
|
-
* (UI defaults to 'datetime'). Derived live from the device's HA domain
|
|
58325
|
-
* (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
|
|
58326
|
-
* `has_date`/`has_time` attributes on each push. */
|
|
58327
|
-
format: _enum([
|
|
58328
|
-
"date",
|
|
58329
|
-
"time",
|
|
58330
|
-
"datetime"
|
|
58331
|
-
]).optional()
|
|
59052
|
+
/** Minimum allowed value, in the device quantity's base unit. */
|
|
59053
|
+
min: number().optional().meta(readingMeta("device")),
|
|
59054
|
+
/** Maximum allowed value, in the device quantity's base unit. */
|
|
59055
|
+
max: number().optional().meta(readingMeta("device")),
|
|
59056
|
+
/** Step increment, in the device quantity's base unit. */
|
|
59057
|
+
step: number().optional().meta(readingMeta("device"))
|
|
58332
59058
|
});
|
|
58333
59059
|
var ControlSetValueInputSchema = discriminatedUnion("kind", [
|
|
58334
59060
|
object({
|
|
@@ -58394,9 +59120,9 @@ var CoverStatusSchema = object({
|
|
|
58394
59120
|
]),
|
|
58395
59121
|
/** 0 = fully closed, 100 = fully open. Null when the device has
|
|
58396
59122
|
* no intermediate position surface. */
|
|
58397
|
-
position: number().min(0).max(100).nullable(),
|
|
59123
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58398
59124
|
/** 0 = closed slats, 100 = open slats. Null when no tilt support. */
|
|
58399
|
-
tiltPosition: number().min(0).max(100).nullable(),
|
|
59125
|
+
tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58400
59126
|
/** Ms epoch when the slice was last updated. */
|
|
58401
59127
|
lastChangedAt: number()
|
|
58402
59128
|
});
|
|
@@ -58499,9 +59225,9 @@ var NormalizedRangeSchema$1 = object({
|
|
|
58499
59225
|
var DayNightStatusSchema = object({
|
|
58500
59226
|
mode: DayNightModeSchema,
|
|
58501
59227
|
/** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
|
|
58502
|
-
sensitivity: number().optional(),
|
|
59228
|
+
sensitivity: number().optional().meta(readingMeta("none")),
|
|
58503
59229
|
/** Delay before the IR-cut filter flips, in seconds. */
|
|
58504
|
-
switchDelaySec: number().optional(),
|
|
59230
|
+
switchDelaySec: number().optional().meta(readingMeta("duration")),
|
|
58505
59231
|
lastFetchedAt: number()
|
|
58506
59232
|
});
|
|
58507
59233
|
/**
|
|
@@ -58719,11 +59445,11 @@ var DishwasherStatusSchema = object({
|
|
|
58719
59445
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
58720
59446
|
availablePrograms: array(string()),
|
|
58721
59447
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
58722
|
-
remainingSeconds: number().int().nonnegative().nullable(),
|
|
59448
|
+
remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
58723
59449
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
58724
|
-
delayedStartSeconds: number().int().nonnegative().nullable(),
|
|
59450
|
+
delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
58725
59451
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
58726
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
59452
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58727
59453
|
/** True when the door is reported open; null when the door state is not known. */
|
|
58728
59454
|
doorOpen: boolean().nullable(),
|
|
58729
59455
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -58747,7 +59473,7 @@ var DishwasherStatusSchema = object({
|
|
|
58747
59473
|
/** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
|
|
58748
59474
|
machineCareDue: boolean().nullable(),
|
|
58749
59475
|
/** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
|
|
58750
|
-
machineCareRemainingRuns: number().int().nonnegative().nullable(),
|
|
59476
|
+
machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
|
|
58751
59477
|
/** Option states: null when the appliance does not expose the option or has not said. */
|
|
58752
59478
|
halfLoad: boolean().nullable(),
|
|
58753
59479
|
hygienePlus: boolean().nullable(),
|
|
@@ -58762,7 +59488,7 @@ var DishwasherStatusSchema = object({
|
|
|
58762
59488
|
/** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
|
|
58763
59489
|
activeEvents: array(string()),
|
|
58764
59490
|
/** Numeric error code when the appliance reports one; null otherwise. */
|
|
58765
|
-
errorCode: number().nullable(),
|
|
59491
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
58766
59492
|
/** Name of the raised alert-level event while the appliance is in error; null otherwise. */
|
|
58767
59493
|
errorLabel: string().nullable(),
|
|
58768
59494
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
@@ -58993,7 +59719,7 @@ var DoorbellStatusSchema = object({
|
|
|
58993
59719
|
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
58994
59720
|
lastPressedAt: number().nullable(),
|
|
58995
59721
|
/** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
|
|
58996
|
-
pressCountSinceStart: number()
|
|
59722
|
+
pressCountSinceStart: number().meta(readingMeta("count"))
|
|
58997
59723
|
});
|
|
58998
59724
|
var DoorbellPressEventSchema = object({
|
|
58999
59725
|
deviceId: number(),
|
|
@@ -59058,17 +59784,7 @@ onPressed: {
|
|
|
59058
59784
|
history: "none",
|
|
59059
59785
|
customize: doorbellCustomize
|
|
59060
59786
|
};
|
|
59061
|
-
|
|
59062
|
-
* Enum-state sensor — a string value picked from a finite option set.
|
|
59063
|
-
* Drives HA `sensor` entries with `state_class: enum` (HVAC action
|
|
59064
|
-
* states, weather conditions, contact-source channels, mode strings).
|
|
59065
|
-
* The option set is stable across observations; it is persisted in the
|
|
59066
|
-
* device config blob (via the control slice at adoption) and is NOT
|
|
59067
|
-
* stored in `sourceInfo`.
|
|
59068
|
-
*/
|
|
59069
|
-
/** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
|
|
59070
|
-
* enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
|
|
59071
|
-
var EnumSensorDateTimeFormatSchema = _enum([
|
|
59787
|
+
_enum([
|
|
59072
59788
|
"date",
|
|
59073
59789
|
"time",
|
|
59074
59790
|
"datetime"
|
|
@@ -59076,12 +59792,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
|
|
|
59076
59792
|
var EnumSensorStatusSchema = object({
|
|
59077
59793
|
value: string(),
|
|
59078
59794
|
/**
|
|
59079
|
-
* Set
|
|
59080
|
-
* as
|
|
59081
|
-
*
|
|
59082
|
-
* a time-only sensor → `'time'`.
|
|
59795
|
+
* Set when `value` is a timestamp rather than an enum state, so history
|
|
59796
|
+
* does not learn ISO strings as states. The display shape (date / time /
|
|
59797
|
+
* datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
|
|
59083
59798
|
*/
|
|
59084
|
-
|
|
59799
|
+
timestamp: boolean().optional(),
|
|
59085
59800
|
/** Ms epoch when the slice was last updated. */
|
|
59086
59801
|
lastFetchedAt: number()
|
|
59087
59802
|
});
|
|
@@ -59108,11 +59823,11 @@ var enumSensorCapability = {
|
|
|
59108
59823
|
* History (D850): the value as an `enum` series (an appliance's program or
|
|
59109
59824
|
* operation state, an HVAC action). The option set is per device, so the
|
|
59110
59825
|
* value list is LEARNED as values are first seen (bounded). A sensor with a
|
|
59111
|
-
* `
|
|
59826
|
+
* `timestamp` carries a clock, not a state, and is not recorded.
|
|
59112
59827
|
*/
|
|
59113
59828
|
history: { fields: { value: {
|
|
59114
59829
|
kind: "enum",
|
|
59115
|
-
skipWhenSet: "
|
|
59830
|
+
skipWhenSet: "timestamp"
|
|
59116
59831
|
} } },
|
|
59117
59832
|
/** Clock fields: written, but excluded from the compare that decides
|
|
59118
59833
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -59132,13 +59847,13 @@ var EventFireSchema = object({
|
|
|
59132
59847
|
deviceId: number(),
|
|
59133
59848
|
eventType: string(),
|
|
59134
59849
|
data: record(string(), unknown()).nullable(),
|
|
59135
|
-
timestamp: number(),
|
|
59136
|
-
seq: number()
|
|
59850
|
+
timestamp: number().meta(readingMeta("none")),
|
|
59851
|
+
seq: number().meta(readingMeta("count"))
|
|
59137
59852
|
});
|
|
59138
59853
|
var EventEmitterStatusSchema = object({
|
|
59139
59854
|
eventTypes: array(string()),
|
|
59140
59855
|
lastEvent: EventFireSchema.nullable(),
|
|
59141
|
-
eventCountSinceStart: number()
|
|
59856
|
+
eventCountSinceStart: number().meta(readingMeta("count"))
|
|
59142
59857
|
});
|
|
59143
59858
|
var eventEmitterCapability = {
|
|
59144
59859
|
name: "event-emitter",
|
|
@@ -59551,10 +60266,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
59551
60266
|
var FanControlStatusSchema = object({
|
|
59552
60267
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
59553
60268
|
* speed surface (single-speed fan). */
|
|
59554
|
-
percentage: number().min(0).max(100).nullable(),
|
|
60269
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59555
60270
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
59556
60271
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
59557
|
-
percentageStep: number().positive().optional(),
|
|
60272
|
+
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
59558
60273
|
/** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
|
|
59559
60274
|
* — empty string when no preset is active or supported. */
|
|
59560
60275
|
preset: string(),
|
|
@@ -59675,7 +60390,7 @@ var FeatureProbeStatusSchema = object({
|
|
|
59675
60390
|
/** Camera/firmware model string. `null` when the firmware doesn't expose it. */
|
|
59676
60391
|
model: string().nullable(),
|
|
59677
60392
|
/** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
|
|
59678
|
-
channelCount: number().nullable(),
|
|
60393
|
+
channelCount: number().nullable().meta(readingMeta("count")),
|
|
59679
60394
|
/**
|
|
59680
60395
|
* Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
|
|
59681
60396
|
* completes — drivers' `getAccessoryChildren()` should treat zero as
|
|
@@ -59798,9 +60513,151 @@ var gasCapability = {
|
|
|
59798
60513
|
* whether persisting is worth a SQLite commit. */
|
|
59799
60514
|
volatileStateFields: ["lastChangedAt"]
|
|
59800
60515
|
};
|
|
60516
|
+
var GroupCommandsSchema = object({
|
|
60517
|
+
/** One row per member that has been commanded. A later command replaces that member's row. */
|
|
60518
|
+
results: array(object({
|
|
60519
|
+
deviceId: number().int().nonnegative(),
|
|
60520
|
+
cap: string().min(1),
|
|
60521
|
+
method: string().min(1),
|
|
60522
|
+
ok: boolean(),
|
|
60523
|
+
error: string().optional(),
|
|
60524
|
+
/** Ms epoch of the attempt. */
|
|
60525
|
+
at: number()
|
|
60526
|
+
})) });
|
|
60527
|
+
var groupCommandsCapability = {
|
|
60528
|
+
name: "group-commands",
|
|
60529
|
+
scope: "device",
|
|
60530
|
+
deviceNative: true,
|
|
60531
|
+
mode: "singleton",
|
|
60532
|
+
deviceTypes: [
|
|
60533
|
+
DeviceType.Switch,
|
|
60534
|
+
DeviceType.Light,
|
|
60535
|
+
DeviceType.Fan,
|
|
60536
|
+
DeviceType.Cover,
|
|
60537
|
+
DeviceType.Valve,
|
|
60538
|
+
DeviceType.Lock,
|
|
60539
|
+
DeviceType.MediaPlayer,
|
|
60540
|
+
DeviceType.Thermostat,
|
|
60541
|
+
DeviceType.Climate
|
|
60542
|
+
],
|
|
60543
|
+
methods: {},
|
|
60544
|
+
status: {
|
|
60545
|
+
schema: GroupCommandsSchema,
|
|
60546
|
+
kind: "push"
|
|
60547
|
+
},
|
|
60548
|
+
runtimeState: GroupCommandsSchema,
|
|
60549
|
+
/**
|
|
60550
|
+
* Runtime-state durability: **session** — the last attempt. A restored
|
|
60551
|
+
* failure would outlive the command that caused it.
|
|
60552
|
+
*
|
|
60553
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
60554
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
60555
|
+
*/
|
|
60556
|
+
durability: "session",
|
|
60557
|
+
history: "none"
|
|
60558
|
+
};
|
|
60559
|
+
/**
|
|
60560
|
+
* Whether a helper actuator group counts as active (D944): at least one
|
|
60561
|
+
* included member is, or every included member is. What "active" means is the
|
|
60562
|
+
* type's: a cover is open, a lock is locked, a media player is playing, a
|
|
60563
|
+
* climate device is not off. A group of lights, switches and fans reports
|
|
60564
|
+
* `switch.on` instead; this cap is for the types whose own state is not a
|
|
60565
|
+
* yes/no. Unknown members are left out (`group-status` counts them) and an
|
|
60566
|
+
* empty included set takes the device offline, never `active: false`.
|
|
60567
|
+
*/
|
|
60568
|
+
var GroupActiveSchema = object({
|
|
60569
|
+
active: boolean().meta({ label: "Active" }),
|
|
60570
|
+
/** Ms epoch when the slice was last updated. */
|
|
60571
|
+
lastChangedAt: number()
|
|
60572
|
+
});
|
|
60573
|
+
var groupActiveCapability = {
|
|
60574
|
+
name: "group-active",
|
|
60575
|
+
scope: "device",
|
|
60576
|
+
deviceNative: true,
|
|
60577
|
+
mode: "singleton",
|
|
60578
|
+
deviceTypes: [
|
|
60579
|
+
DeviceType.Cover,
|
|
60580
|
+
DeviceType.Valve,
|
|
60581
|
+
DeviceType.Lock,
|
|
60582
|
+
DeviceType.MediaPlayer,
|
|
60583
|
+
DeviceType.Climate,
|
|
60584
|
+
DeviceType.Thermostat
|
|
60585
|
+
],
|
|
60586
|
+
methods: {},
|
|
60587
|
+
status: {
|
|
60588
|
+
schema: GroupActiveSchema,
|
|
60589
|
+
kind: "push"
|
|
60590
|
+
},
|
|
60591
|
+
runtimeState: GroupActiveSchema,
|
|
60592
|
+
/**
|
|
60593
|
+
* Runtime-state durability: **session** — recomputed from the members on the
|
|
60594
|
+
* next source event; a restored value would outlive the members it named.
|
|
60595
|
+
*
|
|
60596
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
60597
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
60598
|
+
*/
|
|
60599
|
+
durability: "session",
|
|
60600
|
+
history: "none"
|
|
60601
|
+
};
|
|
60602
|
+
/**
|
|
60603
|
+
* How many members of a helper group answered (D944). Counts are integers,
|
|
60604
|
+
* not measurements: zero included is a real count, never a stand-in reading.
|
|
60605
|
+
* The reading itself stays on the output capability. History is none — the
|
|
60606
|
+
* series that later statistics read is the output cap's own history block.
|
|
60607
|
+
*/
|
|
60608
|
+
var GroupStatusSchema = object({
|
|
60609
|
+
/** Members whose current value entered the aggregate. */
|
|
60610
|
+
included: number().int().nonnegative().meta(readingMeta("count", {
|
|
60611
|
+
precision: 0,
|
|
60612
|
+
label: "Reporting"
|
|
60613
|
+
})),
|
|
60614
|
+
/** Members left out: offline, unknown, or not a finite reading (D393). */
|
|
60615
|
+
excluded: number().int().nonnegative().meta(readingMeta("count", {
|
|
60616
|
+
precision: 0,
|
|
60617
|
+
label: "Excluded"
|
|
60618
|
+
})),
|
|
60619
|
+
/** `included` + `excluded`. */
|
|
60620
|
+
total: number().int().nonnegative().meta(readingMeta("count", {
|
|
60621
|
+
precision: 0,
|
|
60622
|
+
label: "Members"
|
|
60623
|
+
}))
|
|
60624
|
+
});
|
|
60625
|
+
var groupStatusCapability = {
|
|
60626
|
+
name: "group-status",
|
|
60627
|
+
scope: "device",
|
|
60628
|
+
deviceNative: true,
|
|
60629
|
+
mode: "singleton",
|
|
60630
|
+
deviceTypes: [
|
|
60631
|
+
DeviceType.Sensor,
|
|
60632
|
+
DeviceType.Switch,
|
|
60633
|
+
DeviceType.Light,
|
|
60634
|
+
DeviceType.Fan,
|
|
60635
|
+
DeviceType.Cover,
|
|
60636
|
+
DeviceType.Valve,
|
|
60637
|
+
DeviceType.Lock,
|
|
60638
|
+
DeviceType.MediaPlayer,
|
|
60639
|
+
DeviceType.Thermostat,
|
|
60640
|
+
DeviceType.Climate
|
|
60641
|
+
],
|
|
60642
|
+
methods: {},
|
|
60643
|
+
status: {
|
|
60644
|
+
schema: GroupStatusSchema,
|
|
60645
|
+
kind: "push"
|
|
60646
|
+
},
|
|
60647
|
+
runtimeState: GroupStatusSchema,
|
|
60648
|
+
/**
|
|
60649
|
+
* Runtime-state durability: **session** — recomputed from members on the
|
|
60650
|
+
* next source event; a restored count would outlive the readings it named.
|
|
60651
|
+
*
|
|
60652
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
60653
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
60654
|
+
*/
|
|
60655
|
+
durability: "session",
|
|
60656
|
+
history: "none"
|
|
60657
|
+
};
|
|
59801
60658
|
var HeatingValveStatusSchema = object({
|
|
59802
60659
|
/** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
|
|
59803
|
-
position: number().min(0).max(100).nullable(),
|
|
60660
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59804
60661
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
59805
60662
|
lastFetchedAt: number()
|
|
59806
60663
|
});
|
|
@@ -59843,9 +60700,9 @@ var HumidifierStatusSchema = object({
|
|
|
59843
60700
|
/** Whether the humidifier is currently on. */
|
|
59844
60701
|
on: boolean(),
|
|
59845
60702
|
/** Current measured relative humidity (0..100). Null when not reported. */
|
|
59846
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
60703
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
59847
60704
|
/** Target relative humidity (0..100). Null when no setpoint surface. */
|
|
59848
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
60705
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
59849
60706
|
/** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
|
|
59850
60707
|
* device has no mode surface. */
|
|
59851
60708
|
mode: string().nullable(),
|
|
@@ -59855,9 +60712,9 @@ var HumidifierStatusSchema = object({
|
|
|
59855
60712
|
* `idle` / `off`). Null when the device doesn't report it. */
|
|
59856
60713
|
action: string().nullable(),
|
|
59857
60714
|
/** HA `min_humidity` attribute. Null → UI uses 0. */
|
|
59858
|
-
minHumidity: number().nullable(),
|
|
60715
|
+
minHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
59859
60716
|
/** HA `max_humidity` attribute. Null → UI uses 100. */
|
|
59860
|
-
maxHumidity: number().nullable(),
|
|
60717
|
+
maxHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
59861
60718
|
/** Ms epoch when the slice was last updated. */
|
|
59862
60719
|
lastChangedAt: number()
|
|
59863
60720
|
});
|
|
@@ -59927,22 +60784,13 @@ var humidifierCapability = {
|
|
|
59927
60784
|
* `sourceInfo`.
|
|
59928
60785
|
*/
|
|
59929
60786
|
var HumiditySensorStatusSchema = object({
|
|
59930
|
-
/** Current relative humidity, 0..100. */
|
|
59931
|
-
percent: number().min(0).max(100).meta({
|
|
59932
|
-
|
|
60787
|
+
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
60788
|
+
percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
60789
|
+
precision: 0,
|
|
59933
60790
|
label: "Humidity"
|
|
59934
|
-
}),
|
|
60791
|
+
})),
|
|
59935
60792
|
/** Ms epoch when the slice was last updated. */
|
|
59936
|
-
lastFetchedAt: number()
|
|
59937
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
59938
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
59939
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
59940
|
-
unit: string().optional(),
|
|
59941
|
-
/** Suggested decimal places for numeric display.
|
|
59942
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
59943
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
59944
|
-
* auto-formatting when absent. */
|
|
59945
|
-
precision: number().int().min(0).max(10).optional()
|
|
60793
|
+
lastFetchedAt: number()
|
|
59946
60794
|
});
|
|
59947
60795
|
var humiditySensorCapability = {
|
|
59948
60796
|
name: "humidity-sensor",
|
|
@@ -59973,12 +60821,7 @@ var humiditySensorCapability = {
|
|
|
59973
60821
|
} } },
|
|
59974
60822
|
/** Clock fields: written, but excluded from the compare that decides
|
|
59975
60823
|
* whether persisting is worth a SQLite commit. */
|
|
59976
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
59977
|
-
customize: {
|
|
59978
|
-
version: 1,
|
|
59979
|
-
advanced: "derive",
|
|
59980
|
-
targets: []
|
|
59981
|
-
}
|
|
60824
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
59982
60825
|
};
|
|
59983
60826
|
/**
|
|
59984
60827
|
* Image display cap. Models a single still image exposed by an integration —
|
|
@@ -59999,7 +60842,7 @@ var ImageStatusSchema = object({
|
|
|
59999
60842
|
* entity exposes no `entity_picture` (yet). */
|
|
60000
60843
|
url: string().nullable(),
|
|
60001
60844
|
/** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
|
|
60002
|
-
lastUpdated: number().nullable()
|
|
60845
|
+
lastUpdated: number().nullable().meta(readingMeta("none"))
|
|
60003
60846
|
});
|
|
60004
60847
|
var imageCapability = {
|
|
60005
60848
|
name: "image",
|
|
@@ -60087,19 +60930,19 @@ var NormalizedRangeSchema = object({
|
|
|
60087
60930
|
*/
|
|
60088
60931
|
var ImageSettingsStatusSchema = object({
|
|
60089
60932
|
/** Normalized 0–100. */
|
|
60090
|
-
brightness: number().optional(),
|
|
60933
|
+
brightness: number().optional().meta(readingMeta("none")),
|
|
60091
60934
|
/** Normalized 0–100. */
|
|
60092
|
-
contrast: number().optional(),
|
|
60935
|
+
contrast: number().optional().meta(readingMeta("none")),
|
|
60093
60936
|
/** Normalized 0–100. */
|
|
60094
|
-
saturation: number().optional(),
|
|
60937
|
+
saturation: number().optional().meta(readingMeta("none")),
|
|
60095
60938
|
/** Normalized 0–100. */
|
|
60096
|
-
sharpness: number().optional(),
|
|
60939
|
+
sharpness: number().optional().meta(readingMeta("none")),
|
|
60097
60940
|
mirror: boolean().optional(),
|
|
60098
60941
|
flip: boolean().optional(),
|
|
60099
60942
|
rotate: ImageRotateSchema.optional(),
|
|
60100
60943
|
whiteBalance: WhiteBalanceModeSchema.optional(),
|
|
60101
60944
|
/** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
|
|
60102
|
-
warmth: number().optional(),
|
|
60945
|
+
warmth: number().optional().meta(readingMeta("none")),
|
|
60103
60946
|
exposureMode: ExposureModeSchema.optional(),
|
|
60104
60947
|
backlightMode: BacklightModeSchema.optional(),
|
|
60105
60948
|
lastFetchedAt: number()
|
|
@@ -60378,10 +61221,10 @@ var IntercomAbilitySchema = object({
|
|
|
60378
61221
|
/** Codecs accepted by firmware, in preference order. Driver-authored strings. */
|
|
60379
61222
|
codecs: array(string()),
|
|
60380
61223
|
/** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
|
|
60381
|
-
sampleRate: number(),
|
|
61224
|
+
sampleRate: number().meta(readingMeta("none")),
|
|
60382
61225
|
duplex: _enum(["half", "full"]),
|
|
60383
61226
|
/** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
|
|
60384
|
-
maxBacklogMs: number()
|
|
61227
|
+
maxBacklogMs: number().meta(readingMeta("duration-ms"))
|
|
60385
61228
|
});
|
|
60386
61229
|
var IntercomStatusSchema = object({
|
|
60387
61230
|
/** True while a talk session is open. */
|
|
@@ -60608,13 +61451,13 @@ var LawnMowerControlStatusSchema = object({
|
|
|
60608
61451
|
activity: LawnMowerActivitySchema,
|
|
60609
61452
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
60610
61453
|
* reading. */
|
|
60611
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
61454
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60612
61455
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
60613
61456
|
* task is active / progress is unavailable. */
|
|
60614
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
61457
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60615
61458
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
60616
61459
|
* or null when unknown. */
|
|
60617
|
-
currentCode: number().nullable(),
|
|
61460
|
+
currentCode: number().nullable().meta(readingMeta("none")),
|
|
60618
61461
|
/** Human label for {@link currentCode}, or null when undecodable. */
|
|
60619
61462
|
currentCodeLabel: string().nullable(),
|
|
60620
61463
|
/** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
|
|
@@ -61067,7 +61910,14 @@ var lockControlCapability = {
|
|
|
61067
61910
|
}),
|
|
61068
61911
|
unlock: method(object({
|
|
61069
61912
|
deviceId: number().int().nonnegative(),
|
|
61070
|
-
code: string().min(1).optional()
|
|
61913
|
+
code: string().min(1).optional(),
|
|
61914
|
+
/**
|
|
61915
|
+
* Honoured by a helper lock GROUP only (D944): unlocking several doors
|
|
61916
|
+
* with one call is refused unless the caller says it was confirmed. A
|
|
61917
|
+
* single lock ignores it. The refusal lives in the callee, so no
|
|
61918
|
+
* automation or API caller reaches the doors by skipping a dialog.
|
|
61919
|
+
*/
|
|
61920
|
+
confirm: boolean().optional()
|
|
61071
61921
|
}), _void(), {
|
|
61072
61922
|
kind: "mutation",
|
|
61073
61923
|
auth: "protected"
|
|
@@ -61104,6 +61954,44 @@ var lockControlCapability = {
|
|
|
61104
61954
|
volatileStateFields: ["lastChangedAt"]
|
|
61105
61955
|
};
|
|
61106
61956
|
/**
|
|
61957
|
+
* A monotonic total between resets (D944). Energy uses `power-meter.kwhTotal`
|
|
61958
|
+
* so dashboards keep one series; every other instantaneous reading lands here.
|
|
61959
|
+
* `value` is the time integral in the source quantity's accumulated base
|
|
61960
|
+
* (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
|
|
61961
|
+
*/
|
|
61962
|
+
var MeterStatusSchema = object({
|
|
61963
|
+
value: number().nullable().meta(readingMeta("device", { label: "Total" })),
|
|
61964
|
+
lastFetchedAt: number()
|
|
61965
|
+
});
|
|
61966
|
+
var meterCapability = {
|
|
61967
|
+
name: "meter",
|
|
61968
|
+
scope: "device",
|
|
61969
|
+
deviceNative: true,
|
|
61970
|
+
mode: "singleton",
|
|
61971
|
+
deviceTypes: [DeviceType.Sensor],
|
|
61972
|
+
methods: {},
|
|
61973
|
+
status: {
|
|
61974
|
+
schema: MeterStatusSchema,
|
|
61975
|
+
kind: "push"
|
|
61976
|
+
},
|
|
61977
|
+
runtimeState: MeterStatusSchema,
|
|
61978
|
+
/**
|
|
61979
|
+
* Runtime-state durability: **restored** — the total is a cache of the
|
|
61980
|
+
* history integral. A restart shows it only until the history pass
|
|
61981
|
+
* recomputes the open period; it is not an in-memory buffer.
|
|
61982
|
+
*
|
|
61983
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
61984
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
61985
|
+
*/
|
|
61986
|
+
durability: "restored",
|
|
61987
|
+
history: { fields: { value: {
|
|
61988
|
+
kind: "counter",
|
|
61989
|
+
deadband: .001,
|
|
61990
|
+
minIntervalMs: 6e4
|
|
61991
|
+
} } },
|
|
61992
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
61993
|
+
};
|
|
61994
|
+
/**
|
|
61107
61995
|
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
61108
61996
|
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
61109
61997
|
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
@@ -61147,7 +62035,7 @@ var MediaPlayerStatusSchema = object({
|
|
|
61147
62035
|
state: MediaPlayerStateSchema,
|
|
61148
62036
|
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
61149
62037
|
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
61150
|
-
volumeLevel: number().min(0).max(100).nullable(),
|
|
62038
|
+
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61151
62039
|
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
61152
62040
|
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
61153
62041
|
isMuted: boolean().nullable(),
|
|
@@ -61160,10 +62048,10 @@ var MediaPlayerStatusSchema = object({
|
|
|
61160
62048
|
currentMedia: MediaInfoSchema.nullable(),
|
|
61161
62049
|
/** Current playback position in ms. Null when not seekable or
|
|
61162
62050
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
61163
|
-
positionMs: number().int().nonnegative().nullable(),
|
|
62051
|
+
positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61164
62052
|
/** Total duration of the current media in ms. Null when unknown
|
|
61165
62053
|
* (live stream) or nothing is playing. */
|
|
61166
|
-
durationMs: number().int().nonnegative().nullable(),
|
|
62054
|
+
durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61167
62055
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
61168
62056
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
61169
62057
|
shuffle: boolean().nullable(),
|
|
@@ -61472,7 +62360,7 @@ var motionCustomize = {
|
|
|
61472
62360
|
eventName: "onMotionChanged"
|
|
61473
62361
|
},
|
|
61474
62362
|
deviceTypes: [DeviceType.Camera],
|
|
61475
|
-
apply: "
|
|
62363
|
+
apply: "add",
|
|
61476
62364
|
applyAuthority: {
|
|
61477
62365
|
kind: "camera-sensors",
|
|
61478
62366
|
timeoutField: "motionCooldownMs"
|
|
@@ -61544,7 +62432,7 @@ var MotionStatusSchema = object({
|
|
|
61544
62432
|
* it, every provider seeded it at boot, and every session left it
|
|
61545
62433
|
* `detected: true` after its detach.
|
|
61546
62434
|
*/
|
|
61547
|
-
autoClearAfterMs: number().nullable()
|
|
62435
|
+
autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
|
|
61548
62436
|
});
|
|
61549
62437
|
/**
|
|
61550
62438
|
* Payload of `motion.onMotionChanged` event + the corresponding bus
|
|
@@ -61581,12 +62469,18 @@ var motionCapability = {
|
|
|
61581
62469
|
methods: {
|
|
61582
62470
|
/**
|
|
61583
62471
|
* Pull the current motion state synchronously. Convenience shortcut
|
|
61584
|
-
* for consumers that don't need the full status object
|
|
61585
|
-
*
|
|
61586
|
-
* `getStatus` once the auto-injected status surface lands in every
|
|
61587
|
-
* consumer.
|
|
62472
|
+
* for consumers that don't need the full status object. A composed
|
|
62473
|
+
* group answers from its slice (D944): unknown throws, it is not off.
|
|
61588
62474
|
*/
|
|
61589
62475
|
isDetected: method(object({ deviceId: number() }), boolean()) },
|
|
62476
|
+
/**
|
|
62477
|
+
* A composed motion group serves `isDetected` from `detected` (D944).
|
|
62478
|
+
* Without this the cap stays uncomposable (`has-commands`).
|
|
62479
|
+
*/
|
|
62480
|
+
composedMethods: { isDetected: {
|
|
62481
|
+
kind: "slice",
|
|
62482
|
+
field: "detected"
|
|
62483
|
+
} },
|
|
61590
62484
|
events: {
|
|
61591
62485
|
/**
|
|
61592
62486
|
* Fires every time the runner transitions a camera between
|
|
@@ -61726,7 +62620,7 @@ var MotionZoneRegionSchema = object({
|
|
|
61726
62620
|
* the grid region(s). */
|
|
61727
62621
|
var MotionZoneStatusSchema = object({
|
|
61728
62622
|
enabled: boolean(),
|
|
61729
|
-
sensitivity: number(),
|
|
62623
|
+
sensitivity: number().meta(readingMeta("none")),
|
|
61730
62624
|
/** Grid region(s). Today exactly one `grid` shape. */
|
|
61731
62625
|
regions: array(MotionZoneRegionSchema),
|
|
61732
62626
|
lastFetchedAt: number()
|
|
@@ -62096,7 +62990,7 @@ var NavigationStatusSchema = object({
|
|
|
62096
62990
|
following: boolean(),
|
|
62097
62991
|
flash: boolean(),
|
|
62098
62992
|
lightMode: NavigationLightModeSchema,
|
|
62099
|
-
lightLevel: number().min(40).max(100),
|
|
62993
|
+
lightLevel: number().min(40).max(100).meta(readingMeta("none")),
|
|
62100
62994
|
/** Ms epoch when the slice was last updated. */
|
|
62101
62995
|
lastChangedAt: number()
|
|
62102
62996
|
});
|
|
@@ -62230,13 +63124,13 @@ var NetworkLinkStatusSchema = object({
|
|
|
62230
63124
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
62231
63125
|
* SKIP a null rather than coerce it.
|
|
62232
63126
|
*/
|
|
62233
|
-
signalPercent: number().min(0).max(100).nullable(),
|
|
63127
|
+
signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62234
63128
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
62235
|
-
rssiDbm: number().optional(),
|
|
63129
|
+
rssiDbm: number().optional().meta(readingMeta("signal-level")),
|
|
62236
63130
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
62237
63131
|
ssid: string().optional(),
|
|
62238
63132
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
62239
|
-
lastUpdated: number()
|
|
63133
|
+
lastUpdated: number().meta(readingMeta("none"))
|
|
62240
63134
|
});
|
|
62241
63135
|
var networkLinkCapability = {
|
|
62242
63136
|
name: "network-link",
|
|
@@ -62621,7 +63515,7 @@ var NotifierStatusSchema = object({
|
|
|
62621
63515
|
lastError: string().nullable(),
|
|
62622
63516
|
/** Number of deliveries currently buffered server-side (rate-limited
|
|
62623
63517
|
* notifiers may queue). 0 when the provider sends synchronously. */
|
|
62624
|
-
queueDepth: number().int().nonnegative(),
|
|
63518
|
+
queueDepth: number().int().nonnegative().meta(readingMeta("count")),
|
|
62625
63519
|
/** Per-feature capability matrix — authoritative source for the
|
|
62626
63520
|
* compose-form field gating. */
|
|
62627
63521
|
supports: NotifierSupportsSchema
|
|
@@ -62713,11 +63607,9 @@ onSent: { data: object({
|
|
|
62713
63607
|
* cap fits (HA `sensor` whose `device_class` we don't have a typed cap
|
|
62714
63608
|
* for: water flow, distance, weight, frequency, signal strength, …).
|
|
62715
63609
|
*
|
|
62716
|
-
* The
|
|
62717
|
-
*
|
|
62718
|
-
*
|
|
62719
|
-
* The UI reads them directly from the slice — they are the single source
|
|
62720
|
-
* of truth for generic numeric rendering and do NOT live on `sourceInfo`.
|
|
63610
|
+
* The quantity is not on this slice. It is chosen when the device is
|
|
63611
|
+
* adopted and stored once on `SourceInfo.quantity` (D944). `value` is in
|
|
63612
|
+
* that quantity's base unit. `null` is a refused sample (D393).
|
|
62721
63613
|
*
|
|
62722
63614
|
* Prefer the typed alternatives (`temperature-sensor`, `humidity-sensor`,
|
|
62723
63615
|
* `ambient-light-sensor`, `pressure-sensor`, `power-meter`,
|
|
@@ -62726,15 +63618,8 @@ onSent: { data: object({
|
|
|
62726
63618
|
* numeric values land here.
|
|
62727
63619
|
*/
|
|
62728
63620
|
var NumericSensorStatusSchema = object({
|
|
62729
|
-
|
|
62730
|
-
|
|
62731
|
-
* Populated live from the upstream source on each state push.
|
|
62732
|
-
* Absent for unitless sensors. */
|
|
62733
|
-
unit: string().optional(),
|
|
62734
|
-
/** Suggested decimal places for numeric display.
|
|
62735
|
-
* Populated live from the upstream source when provided.
|
|
62736
|
-
* Falls back to auto-formatting when absent. */
|
|
62737
|
-
precision: number().int().min(0).max(10).optional(),
|
|
63621
|
+
/** Reading in the device quantity's base unit. `null` when refused (D393). */
|
|
63622
|
+
value: number().nullable().meta(readingMeta("device")),
|
|
62738
63623
|
/** Ms epoch when the slice was last updated. */
|
|
62739
63624
|
lastFetchedAt: number()
|
|
62740
63625
|
});
|
|
@@ -62758,11 +63643,11 @@ var numericSensorCapability = {
|
|
|
62758
63643
|
*/
|
|
62759
63644
|
durability: "restored",
|
|
62760
63645
|
/**
|
|
62761
|
-
* History (D850): recorded series. The unit is
|
|
63646
|
+
* History (D850): recorded series. The unit is the device quantity's base,
|
|
63647
|
+
* resolved when the series is read — the cap has no single unit.
|
|
62762
63648
|
*/
|
|
62763
63649
|
history: { fields: { value: {
|
|
62764
63650
|
kind: "gauge",
|
|
62765
|
-
unitFrom: "unit",
|
|
62766
63651
|
minIntervalMs: 3e4
|
|
62767
63652
|
} } },
|
|
62768
63653
|
/** Clock fields: written, but excluded from the compare that decides
|
|
@@ -63054,23 +63939,26 @@ var PetFeederStatusSchema = object({
|
|
|
63054
63939
|
/** Food currently in the bowl (grams). Null when the device has not
|
|
63055
63940
|
* reported a reading yet. On dual-hopper models this is the combined
|
|
63056
63941
|
* bowl reading; per-hopper levels live in `food1`/`food2`. */
|
|
63057
|
-
foodLevel: number().nullable(),
|
|
63942
|
+
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
63058
63943
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
63059
63944
|
* single-hopper models. */
|
|
63060
|
-
food1: number().nullable(),
|
|
63945
|
+
food1: number().nullable().meta(readingMeta("none")),
|
|
63061
63946
|
/** Hopper-2 food level (grams) on dual-hopper feeders; null on
|
|
63062
63947
|
* single-hopper models. */
|
|
63063
|
-
food2: number().nullable(),
|
|
63948
|
+
food2: number().nullable().meta(readingMeta("none")),
|
|
63064
63949
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
63065
63950
|
* (`device_class: problem`, on = low). True when the bowl is empty /
|
|
63066
63951
|
* below the feeder's low threshold. */
|
|
63067
63952
|
lowFood: boolean(),
|
|
63068
63953
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
63069
63954
|
* device has no battery reading. */
|
|
63070
|
-
batteryPower: number().min(0).max(100).nullable(),
|
|
63071
|
-
/**
|
|
63072
|
-
*
|
|
63073
|
-
|
|
63955
|
+
batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63956
|
+
/**
|
|
63957
|
+
* Desiccant life remaining, stored in seconds (duration base, D944).
|
|
63958
|
+
* Providers convert from the days the feeder reports. Null when the
|
|
63959
|
+
* model has no desiccant sensor.
|
|
63960
|
+
*/
|
|
63961
|
+
desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
|
|
63074
63962
|
/** True while a feed is in progress. */
|
|
63075
63963
|
feeding: boolean(),
|
|
63076
63964
|
/** Decoded connectivity / power status (HA petkit device-status enum).
|
|
@@ -63081,7 +63969,7 @@ var PetFeederStatusSchema = object({
|
|
|
63081
63969
|
* with `errorCode` for consumers that want the raw integer. */
|
|
63082
63970
|
error: string().nullable(),
|
|
63083
63971
|
/** Raw device error code (0 / null = no error). */
|
|
63084
|
-
errorCode: number().nullable(),
|
|
63972
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
63085
63973
|
/** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
|
|
63086
63974
|
isDualHopper: boolean(),
|
|
63087
63975
|
/** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
|
|
@@ -63091,7 +63979,7 @@ var PetFeederStatusSchema = object({
|
|
|
63091
63979
|
/** Play a chime when dispensing. */
|
|
63092
63980
|
feedSound: boolean(),
|
|
63093
63981
|
/** Speaker / prompt volume level (device-scaled integer). */
|
|
63094
|
-
volume: number(),
|
|
63982
|
+
volume: number().meta(readingMeta("none")),
|
|
63095
63983
|
/** Ms epoch when the slice was last refreshed from the cloud. */
|
|
63096
63984
|
lastFetchedAt: number()
|
|
63097
63985
|
});
|
|
@@ -63210,7 +64098,7 @@ var petFeederCapability = {
|
|
|
63210
64098
|
},
|
|
63211
64099
|
desiccantLeftDays: {
|
|
63212
64100
|
kind: "gauge",
|
|
63213
|
-
unit: "
|
|
64101
|
+
unit: "s"
|
|
63214
64102
|
}
|
|
63215
64103
|
} }
|
|
63216
64104
|
};
|
|
@@ -63611,43 +64499,88 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
63611
64499
|
*/
|
|
63612
64500
|
var PowerMeterStatusSchema = object({
|
|
63613
64501
|
/** Instantaneous power draw in watts. */
|
|
63614
|
-
watts: number().optional().meta({
|
|
63615
|
-
unit: "W",
|
|
63616
|
-
label: "Power"
|
|
63617
|
-
}),
|
|
64502
|
+
watts: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
63618
64503
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
63619
|
-
kwhTotal: number().optional().meta({
|
|
63620
|
-
unit: "kWh",
|
|
63621
|
-
label: "Energy"
|
|
63622
|
-
}),
|
|
64504
|
+
kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
63623
64505
|
/** Voltage in volts. */
|
|
63624
|
-
volts: number().optional(),
|
|
64506
|
+
volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
63625
64507
|
/** Current in amperes. */
|
|
63626
|
-
amps: number().optional(),
|
|
63627
|
-
/** Apparent power in volt-amperes
|
|
63628
|
-
va: number().optional(),
|
|
64508
|
+
amps: number().optional().meta(readingMeta("current", { label: "Current" })),
|
|
64509
|
+
/** Apparent power in volt-amperes. */
|
|
64510
|
+
va: number().optional().meta(readingMeta("apparent-power")),
|
|
63629
64511
|
/** Power factor (0..1) if reported. */
|
|
63630
|
-
powerFactor: number().min(0).max(1).optional(),
|
|
64512
|
+
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
63631
64513
|
/** Ms epoch when the slice was last updated. */
|
|
63632
|
-
lastFetchedAt: number()
|
|
63633
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
63634
|
-
* `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
|
|
63635
|
-
* Each upstream `sensor.*` entity surfaces ONE device_class, so one
|
|
63636
|
-
* unit per slice is unambiguous. The UI prefers this over the role's
|
|
63637
|
-
* canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
|
|
63638
|
-
unit: string().optional(),
|
|
63639
|
-
/** Suggested decimal places for numeric display.
|
|
63640
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
63641
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
63642
|
-
* auto-formatting when absent. */
|
|
63643
|
-
precision: number().int().min(0).max(10).optional()
|
|
64514
|
+
lastFetchedAt: number()
|
|
63644
64515
|
});
|
|
64516
|
+
/**
|
|
64517
|
+
* Loads that draw metered mains power (D951). The reading is a feature of the
|
|
64518
|
+
* device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
|
|
64519
|
+
* vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
|
|
64520
|
+
* meter or a standalone energy monitor IS the device. Cover, lock, valve,
|
|
64521
|
+
* camera and the like are left out: their draw is not what anyone meters.
|
|
64522
|
+
*/
|
|
64523
|
+
var POWER_METER_DEVICE_TYPES = [
|
|
64524
|
+
DeviceType.Sensor,
|
|
64525
|
+
DeviceType.Switch,
|
|
64526
|
+
DeviceType.Light,
|
|
64527
|
+
DeviceType.Fan,
|
|
64528
|
+
DeviceType.Climate,
|
|
64529
|
+
DeviceType.Thermostat,
|
|
64530
|
+
DeviceType.Humidifier,
|
|
64531
|
+
DeviceType.WaterHeater,
|
|
64532
|
+
DeviceType.MediaPlayer,
|
|
64533
|
+
DeviceType.Washer,
|
|
64534
|
+
DeviceType.Dryer,
|
|
64535
|
+
DeviceType.Dishwasher,
|
|
64536
|
+
DeviceType.Vacuum,
|
|
64537
|
+
DeviceType.LawnMower,
|
|
64538
|
+
DeviceType.PetFeeder
|
|
64539
|
+
];
|
|
64540
|
+
/** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
|
|
64541
|
+
var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
|
|
64542
|
+
/** One scalar target per reading; each is sourced from a device of its own (D951). */
|
|
64543
|
+
function powerTarget(id, unit) {
|
|
64544
|
+
return {
|
|
64545
|
+
id,
|
|
64546
|
+
cardinality: "scalar",
|
|
64547
|
+
vocabulary: {
|
|
64548
|
+
cap: "power-meter",
|
|
64549
|
+
stateField: id
|
|
64550
|
+
},
|
|
64551
|
+
deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
|
|
64552
|
+
apply: "add",
|
|
64553
|
+
path: id,
|
|
64554
|
+
sources: [{
|
|
64555
|
+
kind: "numeric",
|
|
64556
|
+
requiredUnit: unit,
|
|
64557
|
+
transforms: [{ kind: "identity" }]
|
|
64558
|
+
}],
|
|
64559
|
+
companions: [{
|
|
64560
|
+
kind: "clock",
|
|
64561
|
+
path: "lastFetchedAt",
|
|
64562
|
+
when: "always"
|
|
64563
|
+
}],
|
|
64564
|
+
sourceReport: { status: "assignment-status" },
|
|
64565
|
+
advancedExpression: true
|
|
64566
|
+
};
|
|
64567
|
+
}
|
|
64568
|
+
/** "Power and energy" extension: watts, energy, voltage and current from other devices (D951). */
|
|
64569
|
+
var powerMeterCustomize = {
|
|
64570
|
+
version: 1,
|
|
64571
|
+
targets: [
|
|
64572
|
+
powerTarget("watts", "W"),
|
|
64573
|
+
powerTarget("kwhTotal", "kWh"),
|
|
64574
|
+
powerTarget("volts", "V"),
|
|
64575
|
+
powerTarget("amps", "A")
|
|
64576
|
+
]
|
|
64577
|
+
};
|
|
63645
64578
|
var powerMeterCapability = {
|
|
63646
64579
|
name: "power-meter",
|
|
63647
64580
|
scope: "device",
|
|
63648
64581
|
deviceNative: true,
|
|
63649
64582
|
mode: "singleton",
|
|
63650
|
-
deviceTypes:
|
|
64583
|
+
deviceTypes: POWER_METER_DEVICE_TYPES,
|
|
63651
64584
|
methods: {},
|
|
63652
64585
|
status: {
|
|
63653
64586
|
schema: PowerMeterStatusSchema,
|
|
@@ -63705,11 +64638,7 @@ var powerMeterCapability = {
|
|
|
63705
64638
|
/** Clock fields: written, but excluded from the compare that decides
|
|
63706
64639
|
* whether persisting is worth a SQLite commit. */
|
|
63707
64640
|
volatileStateFields: ["lastFetchedAt"],
|
|
63708
|
-
customize:
|
|
63709
|
-
version: 1,
|
|
63710
|
-
advanced: "derive",
|
|
63711
|
-
targets: []
|
|
63712
|
-
}
|
|
64641
|
+
customize: powerMeterCustomize
|
|
63713
64642
|
};
|
|
63714
64643
|
/**
|
|
63715
64644
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
@@ -63729,11 +64658,11 @@ var powerMeterCapability = {
|
|
|
63729
64658
|
*/
|
|
63730
64659
|
var GpsLocationSchema = object({
|
|
63731
64660
|
/** Latitude in decimal degrees, -90..90. */
|
|
63732
|
-
latitude: number().min(-90).max(90),
|
|
64661
|
+
latitude: number().min(-90).max(90).meta(readingMeta("angle")),
|
|
63733
64662
|
/** Longitude in decimal degrees, -180..180. */
|
|
63734
|
-
longitude: number().min(-180).max(180),
|
|
64663
|
+
longitude: number().min(-180).max(180).meta(readingMeta("angle")),
|
|
63735
64664
|
/** Reported accuracy in meters (lower = better). */
|
|
63736
|
-
accuracyMeters: number().nonnegative()
|
|
64665
|
+
accuracyMeters: number().nonnegative().meta(readingMeta("length"))
|
|
63737
64666
|
});
|
|
63738
64667
|
var PresenceStatusSchema = object({
|
|
63739
64668
|
/** `home` / `not_home` / any user-defined zone name. */
|
|
@@ -63745,7 +64674,7 @@ var PresenceStatusSchema = object({
|
|
|
63745
64674
|
gps: GpsLocationSchema.nullable(),
|
|
63746
64675
|
/** Optional battery level of the tracking device (0..100). Null
|
|
63747
64676
|
* when not reported. */
|
|
63748
|
-
batteryPercent: number().min(0).max(100).nullable(),
|
|
64677
|
+
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63749
64678
|
/** Ms epoch when the slice was last updated. */
|
|
63750
64679
|
lastChangedAt: number()
|
|
63751
64680
|
});
|
|
@@ -63795,19 +64724,10 @@ var presenceCapability = {
|
|
|
63795
64724
|
* `sourceInfo`.
|
|
63796
64725
|
*/
|
|
63797
64726
|
var PressureSensorStatusSchema = object({
|
|
63798
|
-
/** Current pressure in hPa. */
|
|
63799
|
-
hpa: number(),
|
|
64727
|
+
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
64728
|
+
hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
63800
64729
|
/** Ms epoch when the slice was last updated. */
|
|
63801
|
-
lastFetchedAt: number()
|
|
63802
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
63803
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
63804
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
63805
|
-
unit: string().optional(),
|
|
63806
|
-
/** Suggested decimal places for numeric display.
|
|
63807
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
63808
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
63809
|
-
* auto-formatting when absent. */
|
|
63810
|
-
precision: number().int().min(0).max(10).optional()
|
|
64730
|
+
lastFetchedAt: number()
|
|
63811
64731
|
});
|
|
63812
64732
|
var pressureSensorCapability = {
|
|
63813
64733
|
name: "pressure-sensor",
|
|
@@ -64221,8 +65141,8 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
|
|
|
64221
65141
|
}), _void(), { kind: "mutation" });
|
|
64222
65142
|
var PtzAutotrackSettingsSchema = object({
|
|
64223
65143
|
targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
|
|
64224
|
-
stopDelaySeconds: number().int().min(0).max(300),
|
|
64225
|
-
disappearDelaySeconds: number().int().min(0).max(300)
|
|
65144
|
+
stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
|
|
65145
|
+
disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
|
|
64226
65146
|
});
|
|
64227
65147
|
/**
|
|
64228
65148
|
* One option the camera advertises as a valid `targetType`. Drivers
|
|
@@ -64376,7 +65296,7 @@ var RunningStateStatusSchema = object({
|
|
|
64376
65296
|
/** null = unknown — never coerced to false (D393). */
|
|
64377
65297
|
running: boolean().nullable(),
|
|
64378
65298
|
/** Epoch ms the current boolean began; null while `running` is null. */
|
|
64379
|
-
since: number().nullable(),
|
|
65299
|
+
since: number().nullable().meta(readingMeta("none")),
|
|
64380
65300
|
source: RunningStateSourceSchema.meta({ label: "Source" }),
|
|
64381
65301
|
phase: string().nullable(),
|
|
64382
65302
|
/** null until a reading has been published (D393). */
|
|
@@ -64391,7 +65311,6 @@ var RUNNING_DEVICE_TYPES = Object.values(DeviceType);
|
|
|
64391
65311
|
/** Generic Customize target for the running boolean (D936). */
|
|
64392
65312
|
var runningCustomize = {
|
|
64393
65313
|
version: 1,
|
|
64394
|
-
advanced: "derive",
|
|
64395
65314
|
targets: [{
|
|
64396
65315
|
id: "running",
|
|
64397
65316
|
cardinality: "scalar",
|
|
@@ -64400,6 +65319,8 @@ var runningCustomize = {
|
|
|
64400
65319
|
stateField: "running"
|
|
64401
65320
|
},
|
|
64402
65321
|
deviceTypes: [
|
|
65322
|
+
DeviceType.Washer,
|
|
65323
|
+
DeviceType.Dryer,
|
|
64403
65324
|
DeviceType.Dishwasher,
|
|
64404
65325
|
DeviceType.Vacuum,
|
|
64405
65326
|
DeviceType.LawnMower,
|
|
@@ -64411,7 +65332,7 @@ var runningCustomize = {
|
|
|
64411
65332
|
DeviceType.Thermostat,
|
|
64412
65333
|
DeviceType.PetFeeder
|
|
64413
65334
|
],
|
|
64414
|
-
apply: "add
|
|
65335
|
+
apply: "add",
|
|
64415
65336
|
path: "running",
|
|
64416
65337
|
sources: [
|
|
64417
65338
|
{
|
|
@@ -65675,9 +66596,9 @@ var RecordTriggerSchema = _enum([
|
|
|
65675
66596
|
*/
|
|
65676
66597
|
var RecordWindowSchema = object({
|
|
65677
66598
|
trigger: RecordTriggerSchema,
|
|
65678
|
-
day: number().int().min(0).max(6),
|
|
65679
|
-
startMinute: number().int().min(0).max(1439),
|
|
65680
|
-
endMinute: number().int().min(1).max(1440)
|
|
66599
|
+
day: number().int().min(0).max(6).meta(readingMeta("none")),
|
|
66600
|
+
startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
|
|
66601
|
+
endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
|
|
65681
66602
|
});
|
|
65682
66603
|
/** Status of one physical volume, as the camera itself describes it. */
|
|
65683
66604
|
var OnboardStorageVolumeSchema = object({
|
|
@@ -65753,10 +66674,10 @@ var RecordingOnboardStatusSchema = object({
|
|
|
65753
66674
|
/** Master "record to the card at all" switch. */
|
|
65754
66675
|
enabled: boolean().nullable(),
|
|
65755
66676
|
overwriteWhenFull: boolean().nullable(),
|
|
65756
|
-
preRecordSec: number().nullable(),
|
|
65757
|
-
postRecordSec: number().nullable(),
|
|
66677
|
+
preRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
66678
|
+
postRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
65758
66679
|
/** Length of one recorded file, in minutes. */
|
|
65759
|
-
segmentMinutes: number().nullable(),
|
|
66680
|
+
segmentMinutes: number().nullable().meta(readingMeta("none")),
|
|
65760
66681
|
/** The primary track's weekly windows, flattened. */
|
|
65761
66682
|
windows: array(RecordWindowSchema),
|
|
65762
66683
|
/**
|
|
@@ -65772,7 +66693,7 @@ var RecordingOnboardStatusSchema = object({
|
|
|
65772
66693
|
* provider reporting a non-zero count also reports the schedule as not
|
|
65773
66694
|
* writable.
|
|
65774
66695
|
*/
|
|
65775
|
-
unreadableWindows: number(),
|
|
66696
|
+
unreadableWindows: number().meta(readingMeta("count")),
|
|
65776
66697
|
/**
|
|
65777
66698
|
* The camera is scheduled to record and has NO usable storage.
|
|
65778
66699
|
*
|
|
@@ -66617,7 +67538,7 @@ var CamStreamDescriptorSchema = object({
|
|
|
66617
67538
|
url: string().optional(),
|
|
66618
67539
|
codec: string().optional(),
|
|
66619
67540
|
resolution: CamStreamResolutionSchema.optional(),
|
|
66620
|
-
fps: number().positive().optional(),
|
|
67541
|
+
fps: number().positive().optional().meta(readingMeta("rate")),
|
|
66621
67542
|
label: string().optional(),
|
|
66622
67543
|
/** Device-level features (e.g. `battery-operated`) — drives broker policy. */
|
|
66623
67544
|
deviceFeatures: array(string()).optional(),
|
|
@@ -66704,18 +67625,18 @@ var StreamProfileSchema = _enum([
|
|
|
66704
67625
|
]);
|
|
66705
67626
|
/** The current encoding config of one stream profile. */
|
|
66706
67627
|
var StreamProfileConfigSchema = object({
|
|
66707
|
-
width: number(),
|
|
66708
|
-
height: number(),
|
|
67628
|
+
width: number().meta(readingMeta("none")),
|
|
67629
|
+
height: number().meta(readingMeta("none")),
|
|
66709
67630
|
codec: _enum(["h264", "h265"]),
|
|
66710
|
-
framerate: number(),
|
|
66711
|
-
bitrate: number(),
|
|
67631
|
+
framerate: number().meta(readingMeta("rate")),
|
|
67632
|
+
bitrate: number().meta(readingMeta("bitrate")),
|
|
66712
67633
|
bitrateMode: _enum(["vbr", "cbr"]).optional(),
|
|
66713
67634
|
encoderProfile: _enum([
|
|
66714
67635
|
"high",
|
|
66715
67636
|
"main",
|
|
66716
67637
|
"baseline"
|
|
66717
67638
|
]).optional(),
|
|
66718
|
-
gop: number().optional(),
|
|
67639
|
+
gop: number().optional().meta(readingMeta("none")),
|
|
66719
67640
|
/**
|
|
66720
67641
|
* Whether THIS profile currently carries an audio track. READ-ONLY here.
|
|
66721
67642
|
*
|
|
@@ -66889,7 +67810,8 @@ var switchCapability = {
|
|
|
66889
67810
|
deviceTypes: [
|
|
66890
67811
|
DeviceType.Switch,
|
|
66891
67812
|
DeviceType.Siren,
|
|
66892
|
-
DeviceType.Light
|
|
67813
|
+
DeviceType.Light,
|
|
67814
|
+
DeviceType.Fan
|
|
66893
67815
|
],
|
|
66894
67816
|
methods: { setState: method(object({
|
|
66895
67817
|
deviceId: number(),
|
|
@@ -66936,12 +67858,7 @@ var switchCapability = {
|
|
|
66936
67858
|
label: "On",
|
|
66937
67859
|
kind: "boolean"
|
|
66938
67860
|
}
|
|
66939
|
-
}] }
|
|
66940
|
-
customize: {
|
|
66941
|
-
version: 1,
|
|
66942
|
-
advanced: "derive",
|
|
66943
|
-
targets: []
|
|
66944
|
-
}
|
|
67861
|
+
}] }
|
|
66945
67862
|
};
|
|
66946
67863
|
/** C0 controls, DEL and the C1 range: none of them belongs in a name. */
|
|
66947
67864
|
function hasControlCharacter(value) {
|
|
@@ -67524,35 +68441,21 @@ var tamperCapability = {
|
|
|
67524
68441
|
* entries with `device_class: temperature` and any future native
|
|
67525
68442
|
* thermometer.
|
|
67526
68443
|
*
|
|
67527
|
-
*
|
|
67528
|
-
*
|
|
67529
|
-
* (climate-control's `currentTemp`, energy analytics) can compose
|
|
67530
|
-
* without per-source unit fixups. The canonical display unit (`°C`) is
|
|
67531
|
-
* a descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
67532
|
-
* `sourceInfo`.
|
|
68444
|
+
* The number is Celsius (D944). Providers convert on ingest. A failed
|
|
68445
|
+
* conversion is `null`, never a guess. Display unit is a read-time preference.
|
|
67533
68446
|
*
|
|
67534
68447
|
* Status `lastFetchedAt` lets staleness-aware consumers detect a
|
|
67535
68448
|
* frozen feed (provider hung) distinct from a "temperature hasn't
|
|
67536
68449
|
* changed" steady state.
|
|
67537
68450
|
*/
|
|
67538
68451
|
var TemperatureSensorStatusSchema = object({
|
|
67539
|
-
/** Current temperature in Celsius. */
|
|
67540
|
-
celsius: number().meta({
|
|
67541
|
-
|
|
68452
|
+
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
68453
|
+
celsius: number().nullable().meta(readingMeta("temperature", {
|
|
68454
|
+
precision: 1,
|
|
67542
68455
|
label: "Temperature"
|
|
67543
|
-
}),
|
|
68456
|
+
})),
|
|
67544
68457
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
67545
|
-
lastFetchedAt: number()
|
|
67546
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
67547
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
67548
|
-
* role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
|
|
67549
|
-
* Absent → the UI falls back to the role's canonical unit. */
|
|
67550
|
-
unit: string().optional(),
|
|
67551
|
-
/** Suggested decimal places for numeric display.
|
|
67552
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
67553
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
67554
|
-
* auto-formatting when absent. */
|
|
67555
|
-
precision: number().int().min(0).max(10).optional()
|
|
68458
|
+
lastFetchedAt: number()
|
|
67556
68459
|
});
|
|
67557
68460
|
var temperatureSensorCapability = {
|
|
67558
68461
|
name: "temperature-sensor",
|
|
@@ -67583,12 +68486,7 @@ var temperatureSensorCapability = {
|
|
|
67583
68486
|
} } },
|
|
67584
68487
|
/** Clock fields: written, but excluded from the compare that decides
|
|
67585
68488
|
* whether persisting is worth a SQLite commit. */
|
|
67586
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
67587
|
-
customize: {
|
|
67588
|
-
version: 1,
|
|
67589
|
-
advanced: "derive",
|
|
67590
|
-
targets: []
|
|
67591
|
-
}
|
|
68489
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
67592
68490
|
};
|
|
67593
68491
|
/**
|
|
67594
68492
|
* toast — system-scoped singleton capability that streams toast
|
|
@@ -67871,16 +68769,16 @@ var VacuumControlStatusSchema = object({
|
|
|
67871
68769
|
]),
|
|
67872
68770
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
67873
68771
|
* reading. */
|
|
67874
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
68772
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
67875
68773
|
/** Current fan-speed token (provider-verbatim). Null when unknown or
|
|
67876
68774
|
* the vacuum has no speed control. */
|
|
67877
68775
|
fanSpeed: string().nullable(),
|
|
67878
68776
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
67879
68777
|
availableFanSpeeds: array(string()),
|
|
67880
68778
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
67881
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
68779
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
67882
68780
|
/** Current error code (0 / null = no error). */
|
|
67883
|
-
errorCode: number().nullable(),
|
|
68781
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
67884
68782
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
67885
68783
|
errorLabel: string().nullable(),
|
|
67886
68784
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -67958,7 +68856,7 @@ var ValveStatusSchema = object({
|
|
|
67958
68856
|
]),
|
|
67959
68857
|
/** 0 = fully closed, 100 = fully open. Null when the device has no
|
|
67960
68858
|
* intermediate position surface. */
|
|
67961
|
-
position: number().min(0).max(100).nullable(),
|
|
68859
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
67962
68860
|
/** Ms epoch when the slice was last updated. */
|
|
67963
68861
|
lastChangedAt: number()
|
|
67964
68862
|
});
|
|
@@ -68066,9 +68964,9 @@ var vibrationCapability = {
|
|
|
68066
68964
|
*/
|
|
68067
68965
|
var WaterHeaterStatusSchema = object({
|
|
68068
68966
|
/** Current measured temperature. Null when not reported. */
|
|
68069
|
-
currentTemp: number().nullable(),
|
|
68967
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68070
68968
|
/** Target temperature setpoint. Null when no setpoint surface. */
|
|
68071
|
-
targetTemp: number().nullable(),
|
|
68969
|
+
targetTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68072
68970
|
/** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
|
|
68073
68971
|
* `heat_pump` / `high_demand` / `performance` / `off`). Null when the
|
|
68074
68972
|
* device reports an unknown state. */
|
|
@@ -68079,9 +68977,9 @@ var WaterHeaterStatusSchema = object({
|
|
|
68079
68977
|
* has no away surface. */
|
|
68080
68978
|
away: boolean().nullable(),
|
|
68081
68979
|
/** HA `min_temp` attribute. Null when not reported. */
|
|
68082
|
-
minTemp: number().nullable(),
|
|
68980
|
+
minTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68083
68981
|
/** HA `max_temp` attribute. Null when not reported. */
|
|
68084
|
-
maxTemp: number().nullable(),
|
|
68982
|
+
maxTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68085
68983
|
/** Ms epoch when the slice was last updated. */
|
|
68086
68984
|
lastChangedAt: number()
|
|
68087
68985
|
});
|
|
@@ -68166,25 +69064,18 @@ var waterHeaterCapability = {
|
|
|
68166
69064
|
* Forecast deferred — current conditions only for v1.
|
|
68167
69065
|
*/
|
|
68168
69066
|
var WeatherStatusSchema = object({
|
|
68169
|
-
/** Verbatim
|
|
68170
|
-
* when no condition has been reported yet. */
|
|
69067
|
+
/** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
|
|
68171
69068
|
condition: string().nullable(),
|
|
68172
|
-
/** Current temperature in
|
|
68173
|
-
temperature: number().nullable(),
|
|
68174
|
-
/** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
|
|
68175
|
-
temperatureUnit: string().nullable(),
|
|
69069
|
+
/** Current temperature in Celsius. Null when not provided or refused. */
|
|
69070
|
+
temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
|
|
68176
69071
|
/** Relative humidity (0..100). Null when not provided. */
|
|
68177
|
-
humidity: number().min(0).max(100).nullable(),
|
|
68178
|
-
/** Barometric pressure in
|
|
68179
|
-
pressure: number().nullable(),
|
|
68180
|
-
/**
|
|
68181
|
-
|
|
68182
|
-
/** Wind speed in the reported unit. Null when not provided. */
|
|
68183
|
-
windSpeed: number().nullable(),
|
|
68184
|
-
/** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
|
|
68185
|
-
windSpeedUnit: string().nullable(),
|
|
69072
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
69073
|
+
/** Barometric pressure in hPa. Null when not provided or refused. */
|
|
69074
|
+
pressure: number().nullable().meta(readingMeta("pressure")),
|
|
69075
|
+
/** Wind speed in metres per second. Null when not provided or refused. */
|
|
69076
|
+
windSpeed: number().nullable().meta(readingMeta("speed")),
|
|
68186
69077
|
/** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
|
|
68187
|
-
windBearing: number().nullable(),
|
|
69078
|
+
windBearing: number().nullable().meta(readingMeta("angle")),
|
|
68188
69079
|
/** Ms epoch when the slice was last updated. */
|
|
68189
69080
|
lastFetchedAt: number()
|
|
68190
69081
|
});
|
|
@@ -68212,12 +69103,12 @@ var weatherCapability = {
|
|
|
68212
69103
|
*/
|
|
68213
69104
|
durability: "session",
|
|
68214
69105
|
/**
|
|
68215
|
-
* History (D850): recorded series.
|
|
69106
|
+
* History (D850): recorded series. Numbers are the quantity base (D944).
|
|
68216
69107
|
*/
|
|
68217
69108
|
history: { fields: {
|
|
68218
69109
|
temperature: {
|
|
68219
69110
|
kind: "gauge",
|
|
68220
|
-
|
|
69111
|
+
unit: "°C",
|
|
68221
69112
|
deadband: .2
|
|
68222
69113
|
},
|
|
68223
69114
|
humidity: {
|
|
@@ -68227,12 +69118,12 @@ var weatherCapability = {
|
|
|
68227
69118
|
},
|
|
68228
69119
|
pressure: {
|
|
68229
69120
|
kind: "gauge",
|
|
68230
|
-
|
|
69121
|
+
unit: "hPa",
|
|
68231
69122
|
deadband: .5
|
|
68232
69123
|
},
|
|
68233
69124
|
windSpeed: {
|
|
68234
69125
|
kind: "gauge",
|
|
68235
|
-
|
|
69126
|
+
unit: "m/s",
|
|
68236
69127
|
deadband: .5
|
|
68237
69128
|
}
|
|
68238
69129
|
} }
|
|
@@ -69133,8 +70024,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
69133
70024
|
kind: "mutation",
|
|
69134
70025
|
auth: "admin"
|
|
69135
70026
|
});
|
|
70027
|
+
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
69136
70028
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
69137
|
-
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;
|
|
70029
|
+
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;
|
|
69138
70030
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
69139
70031
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
69140
70032
|
new Set(CAP_NAMES);
|
|
@@ -69463,6 +70355,9 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
69463
70355
|
featureProbe: featureProbeCapability,
|
|
69464
70356
|
flood: floodCapability,
|
|
69465
70357
|
gas: gasCapability,
|
|
70358
|
+
groupActive: groupActiveCapability,
|
|
70359
|
+
groupCommands: groupCommandsCapability,
|
|
70360
|
+
groupStatus: groupStatusCapability,
|
|
69466
70361
|
heatingValve: heatingValveCapability,
|
|
69467
70362
|
humidifier: humidifierCapability,
|
|
69468
70363
|
humiditySensor: humiditySensorCapability,
|
|
@@ -69472,6 +70367,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
69472
70367
|
lawnMowerControl: lawnMowerControlCapability,
|
|
69473
70368
|
lockControl: lockControlCapability,
|
|
69474
70369
|
mediaPlayer: mediaPlayerCapability,
|
|
70370
|
+
meter: meterCapability,
|
|
69475
70371
|
motion: motionCapability,
|
|
69476
70372
|
motionTrigger: motionTriggerCapability,
|
|
69477
70373
|
motionZones: motionZonesCapability,
|
|
@@ -70731,7 +71627,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
70731
71627
|
return toDeviceSummary(device, this.addonId);
|
|
70732
71628
|
}
|
|
70733
71629
|
};
|
|
70734
|
-
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;
|
|
71630
|
+
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;
|
|
70735
71631
|
new Set(Object.values(DeviceType));
|
|
70736
71632
|
Object.freeze({
|
|
70737
71633
|
"accessories.getStatus": {
|
|
@@ -70839,6 +71735,13 @@ Object.freeze({
|
|
|
70839
71735
|
access: "view",
|
|
70840
71736
|
role: "admin"
|
|
70841
71737
|
},
|
|
71738
|
+
"addons.getPackageDocs": {
|
|
71739
|
+
capName: "addons",
|
|
71740
|
+
capScope: "system",
|
|
71741
|
+
addonId: null,
|
|
71742
|
+
access: "view",
|
|
71743
|
+
role: "admin"
|
|
71744
|
+
},
|
|
70842
71745
|
"addons.getVersions": {
|
|
70843
71746
|
capName: "addons",
|
|
70844
71747
|
capScope: "system",
|
|
@@ -73947,6 +74850,27 @@ Object.freeze({
|
|
|
73947
74850
|
access: "view",
|
|
73948
74851
|
role: "viewer"
|
|
73949
74852
|
},
|
|
74853
|
+
"groupActive.getStatus": {
|
|
74854
|
+
capName: "group-active",
|
|
74855
|
+
capScope: "device",
|
|
74856
|
+
addonId: null,
|
|
74857
|
+
access: "view",
|
|
74858
|
+
role: "viewer"
|
|
74859
|
+
},
|
|
74860
|
+
"groupCommands.getStatus": {
|
|
74861
|
+
capName: "group-commands",
|
|
74862
|
+
capScope: "device",
|
|
74863
|
+
addonId: null,
|
|
74864
|
+
access: "view",
|
|
74865
|
+
role: "viewer"
|
|
74866
|
+
},
|
|
74867
|
+
"groupStatus.getStatus": {
|
|
74868
|
+
capName: "group-status",
|
|
74869
|
+
capScope: "device",
|
|
74870
|
+
addonId: null,
|
|
74871
|
+
access: "view",
|
|
74872
|
+
role: "viewer"
|
|
74873
|
+
},
|
|
73950
74874
|
"heatingValve.getStatus": {
|
|
73951
74875
|
capName: "heating-valve",
|
|
73952
74876
|
capScope: "device",
|
|
@@ -74899,6 +75823,13 @@ Object.freeze({
|
|
|
74899
75823
|
access: "create",
|
|
74900
75824
|
role: "admin"
|
|
74901
75825
|
},
|
|
75826
|
+
"meter.getStatus": {
|
|
75827
|
+
capName: "meter",
|
|
75828
|
+
capScope: "device",
|
|
75829
|
+
addonId: null,
|
|
75830
|
+
access: "view",
|
|
75831
|
+
role: "viewer"
|
|
75832
|
+
},
|
|
74902
75833
|
"metricsProvider.collectSnapshot": {
|
|
74903
75834
|
capName: "metrics-provider",
|
|
74904
75835
|
capScope: "system",
|
|
@@ -82997,7 +83928,39 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
82997
83928
|
phrase: t("When {device} finishes", "Quando {device} finisce"),
|
|
82998
83929
|
icon: "square"
|
|
82999
83930
|
}
|
|
83000
|
-
}
|
|
83931
|
+
},
|
|
83932
|
+
overrides: { byType: {
|
|
83933
|
+
[DeviceType.Washer]: {
|
|
83934
|
+
state: { running: {
|
|
83935
|
+
whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
|
|
83936
|
+
whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
|
|
83937
|
+
} },
|
|
83938
|
+
events: {
|
|
83939
|
+
onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
|
|
83940
|
+
onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
|
|
83941
|
+
}
|
|
83942
|
+
},
|
|
83943
|
+
[DeviceType.Dryer]: {
|
|
83944
|
+
state: { running: {
|
|
83945
|
+
whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
|
|
83946
|
+
whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
|
|
83947
|
+
} },
|
|
83948
|
+
events: {
|
|
83949
|
+
onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
|
|
83950
|
+
onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
|
|
83951
|
+
}
|
|
83952
|
+
},
|
|
83953
|
+
[DeviceType.Dishwasher]: {
|
|
83954
|
+
state: { running: {
|
|
83955
|
+
whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
|
|
83956
|
+
whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
|
|
83957
|
+
} },
|
|
83958
|
+
events: {
|
|
83959
|
+
onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
|
|
83960
|
+
onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
|
|
83961
|
+
}
|
|
83962
|
+
}
|
|
83963
|
+
} }
|
|
83001
83964
|
},
|
|
83002
83965
|
update: {
|
|
83003
83966
|
scope: "device",
|
|
@@ -83769,6 +84732,41 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83769
84732
|
icon: "flame"
|
|
83770
84733
|
}]
|
|
83771
84734
|
},
|
|
84735
|
+
"group-active": {
|
|
84736
|
+
scope: "device",
|
|
84737
|
+
noun: t("Group state", "Stato del gruppo")
|
|
84738
|
+
},
|
|
84739
|
+
"group-commands": {
|
|
84740
|
+
scope: "device",
|
|
84741
|
+
noun: t("Group command", "Comando di gruppo")
|
|
84742
|
+
},
|
|
84743
|
+
"group-status": {
|
|
84744
|
+
scope: "device",
|
|
84745
|
+
noun: t("Group", "Gruppo"),
|
|
84746
|
+
state: [
|
|
84747
|
+
{
|
|
84748
|
+
kind: "numeric",
|
|
84749
|
+
field: "included",
|
|
84750
|
+
quantity: t("the members reporting", "i membri che rispondono"),
|
|
84751
|
+
label: t("Reporting", "Rispondono"),
|
|
84752
|
+
icon: "users"
|
|
84753
|
+
},
|
|
84754
|
+
{
|
|
84755
|
+
kind: "numeric",
|
|
84756
|
+
field: "excluded",
|
|
84757
|
+
quantity: t("the members left out", "i membri esclusi"),
|
|
84758
|
+
label: t("Excluded", "Esclusi"),
|
|
84759
|
+
icon: "users"
|
|
84760
|
+
},
|
|
84761
|
+
{
|
|
84762
|
+
kind: "numeric",
|
|
84763
|
+
field: "total",
|
|
84764
|
+
quantity: t("the member count", "il numero di membri"),
|
|
84765
|
+
label: t("Members", "Membri"),
|
|
84766
|
+
icon: "sigma"
|
|
84767
|
+
}
|
|
84768
|
+
]
|
|
84769
|
+
},
|
|
83772
84770
|
"humidity-sensor": {
|
|
83773
84771
|
scope: "device",
|
|
83774
84772
|
noun: t("Humidity", "Umidità"),
|
|
@@ -83794,6 +84792,17 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83794
84792
|
}],
|
|
83795
84793
|
events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
|
|
83796
84794
|
},
|
|
84795
|
+
meter: {
|
|
84796
|
+
scope: "device",
|
|
84797
|
+
noun: t("Meter", "Contatore"),
|
|
84798
|
+
state: [{
|
|
84799
|
+
kind: "numeric",
|
|
84800
|
+
field: "value",
|
|
84801
|
+
quantity: t("the total", "il totale"),
|
|
84802
|
+
label: t("Total", "Totale"),
|
|
84803
|
+
icon: "gauge"
|
|
84804
|
+
}]
|
|
84805
|
+
},
|
|
83797
84806
|
"numeric-sensor": {
|
|
83798
84807
|
scope: "device",
|
|
83799
84808
|
noun: t("Sensor", "Sensore"),
|
|
@@ -83802,7 +84811,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83802
84811
|
field: "value",
|
|
83803
84812
|
quantity: t("the reading", "il valore"),
|
|
83804
84813
|
label: t("Reading", "Valore"),
|
|
83805
|
-
unitField: "unit",
|
|
83806
84814
|
icon: "gauge"
|
|
83807
84815
|
}]
|
|
83808
84816
|
},
|
|
@@ -83938,7 +84946,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83938
84946
|
field: "temperature",
|
|
83939
84947
|
quantity: t("the outdoor temperature", "la temperatura esterna"),
|
|
83940
84948
|
label: t("Temperature", "Temperatura"),
|
|
83941
|
-
|
|
84949
|
+
unit: "°C",
|
|
83942
84950
|
icon: "thermometer"
|
|
83943
84951
|
},
|
|
83944
84952
|
{
|
|
@@ -83954,7 +84962,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83954
84962
|
field: "windSpeed",
|
|
83955
84963
|
quantity: t("the wind speed", "la velocità del vento"),
|
|
83956
84964
|
label: t("Wind", "Vento"),
|
|
83957
|
-
|
|
84965
|
+
unit: "m/s",
|
|
83958
84966
|
icon: "wind"
|
|
83959
84967
|
},
|
|
83960
84968
|
{
|
|
@@ -83962,7 +84970,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83962
84970
|
field: "pressure",
|
|
83963
84971
|
quantity: t("the air pressure", "la pressione atmosferica"),
|
|
83964
84972
|
label: t("Pressure", "Pressione"),
|
|
83965
|
-
|
|
84973
|
+
unit: "hPa",
|
|
83966
84974
|
icon: "gauge"
|
|
83967
84975
|
}
|
|
83968
84976
|
]
|
|
@@ -84267,8 +85275,47 @@ function projectAction(cap, action) {
|
|
|
84267
85275
|
};
|
|
84268
85276
|
}
|
|
84269
85277
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
84270
|
-
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;
|
|
85278
|
+
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;
|
|
84271
85279
|
DeviceFeature.BatteryOperated;
|
|
85280
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
85281
|
+
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.";
|
|
85282
|
+
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.";
|
|
85283
|
+
var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
|
|
85284
|
+
var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
|
|
85285
|
+
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.";
|
|
85286
|
+
function withheld(reason) {
|
|
85287
|
+
return {
|
|
85288
|
+
[DeviceType.Washer]: {
|
|
85289
|
+
exported: false,
|
|
85290
|
+
reason
|
|
85291
|
+
},
|
|
85292
|
+
[DeviceType.Dryer]: {
|
|
85293
|
+
exported: false,
|
|
85294
|
+
reason
|
|
85295
|
+
},
|
|
85296
|
+
[DeviceType.Dishwasher]: {
|
|
85297
|
+
exported: false,
|
|
85298
|
+
reason
|
|
85299
|
+
}
|
|
85300
|
+
};
|
|
85301
|
+
}
|
|
85302
|
+
function offered(received) {
|
|
85303
|
+
return {
|
|
85304
|
+
[DeviceType.Washer]: {
|
|
85305
|
+
exported: true,
|
|
85306
|
+
as: received
|
|
85307
|
+
},
|
|
85308
|
+
[DeviceType.Dryer]: {
|
|
85309
|
+
exported: true,
|
|
85310
|
+
as: received
|
|
85311
|
+
},
|
|
85312
|
+
[DeviceType.Dishwasher]: {
|
|
85313
|
+
exported: true,
|
|
85314
|
+
as: received
|
|
85315
|
+
}
|
|
85316
|
+
};
|
|
85317
|
+
}
|
|
85318
|
+
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
84272
85319
|
/** Reject after `ms`; always clears its own timer. */
|
|
84273
85320
|
async function withTimeout$1(promise, ms, label) {
|
|
84274
85321
|
let timer;
|
|
@@ -85301,6 +86348,21 @@ Object.freeze({
|
|
|
85301
86348
|
form: "single",
|
|
85302
86349
|
optional: false
|
|
85303
86350
|
}],
|
|
86351
|
+
"groupActive.getStatus": [{
|
|
86352
|
+
name: "deviceId",
|
|
86353
|
+
form: "single",
|
|
86354
|
+
optional: false
|
|
86355
|
+
}],
|
|
86356
|
+
"groupCommands.getStatus": [{
|
|
86357
|
+
name: "deviceId",
|
|
86358
|
+
form: "single",
|
|
86359
|
+
optional: false
|
|
86360
|
+
}],
|
|
86361
|
+
"groupStatus.getStatus": [{
|
|
86362
|
+
name: "deviceId",
|
|
86363
|
+
form: "single",
|
|
86364
|
+
optional: false
|
|
86365
|
+
}],
|
|
85304
86366
|
"heatingValve.getStatus": [{
|
|
85305
86367
|
name: "deviceId",
|
|
85306
86368
|
form: "single",
|
|
@@ -85501,6 +86563,11 @@ Object.freeze({
|
|
|
85501
86563
|
form: "single",
|
|
85502
86564
|
optional: false
|
|
85503
86565
|
}],
|
|
86566
|
+
"meter.getStatus": [{
|
|
86567
|
+
name: "deviceId",
|
|
86568
|
+
form: "single",
|
|
86569
|
+
optional: false
|
|
86570
|
+
}],
|
|
85504
86571
|
"motion.getStatus": [{
|
|
85505
86572
|
name: "deviceId",
|
|
85506
86573
|
form: "single",
|
|
@@ -87108,7 +88175,6 @@ Object.freeze({
|
|
|
87108
88175
|
"network-access": "ingress",
|
|
87109
88176
|
"smtp-provider": "email"
|
|
87110
88177
|
});
|
|
87111
|
-
DeviceType.Camera;
|
|
87112
88178
|
/**
|
|
87113
88179
|
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
87114
88180
|
* `z.infer`, they do not restate the shape.
|
|
@@ -87285,6 +88351,8 @@ new Map(Object.entries({
|
|
|
87285
88351
|
"detection-blind": "device-events",
|
|
87286
88352
|
"motion-noisy": "device-events",
|
|
87287
88353
|
"motion-calm": "device-events",
|
|
88354
|
+
"package-llm-silent": "device-events",
|
|
88355
|
+
"package-llm-answered": "device-events",
|
|
87288
88356
|
"recording-stalled": "device-events",
|
|
87289
88357
|
"recording-healthy": "device-events",
|
|
87290
88358
|
"recording-degraded": "device-events",
|
|
@@ -87368,6 +88436,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
87368
88436
|
label: "Motion without a subject",
|
|
87369
88437
|
kinds: ["motion-noisy", "motion-calm"]
|
|
87370
88438
|
},
|
|
88439
|
+
{
|
|
88440
|
+
id: "package-llm",
|
|
88441
|
+
family: "device-events",
|
|
88442
|
+
label: "Package confirm",
|
|
88443
|
+
kinds: ["package-llm-silent", "package-llm-answered"]
|
|
88444
|
+
},
|
|
87371
88445
|
{
|
|
87372
88446
|
id: "recording",
|
|
87373
88447
|
family: "device-events",
|
|
@@ -87549,6 +88623,8 @@ var DEVICE_EVENTS = [
|
|
|
87549
88623
|
"detection-blind",
|
|
87550
88624
|
"motion-noisy",
|
|
87551
88625
|
"motion-calm",
|
|
88626
|
+
"package-llm-silent",
|
|
88627
|
+
"package-llm-answered",
|
|
87552
88628
|
"recording-stalled",
|
|
87553
88629
|
"recording-healthy",
|
|
87554
88630
|
"recording-degraded",
|
|
@@ -87817,6 +88893,15 @@ new Map([
|
|
|
87817
88893
|
snapshotLabel: "Cameras with constant motion now",
|
|
87818
88894
|
snapshotSection: null
|
|
87819
88895
|
},
|
|
88896
|
+
{
|
|
88897
|
+
id: "package-llm-confirm",
|
|
88898
|
+
family: "device-events",
|
|
88899
|
+
label: "Package confirm (not answering / answering again)",
|
|
88900
|
+
bad: ["package-llm-silent"],
|
|
88901
|
+
recovery: ["package-llm-answered"],
|
|
88902
|
+
snapshotLabel: "Cameras whose package confirm is not answering",
|
|
88903
|
+
snapshotSection: null
|
|
88904
|
+
},
|
|
87820
88905
|
{
|
|
87821
88906
|
id: "recording-health",
|
|
87822
88907
|
family: "device-events",
|