@camstack/addon-provider-tuya 0.2.145 → 0.2.147
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +34 -0
- package/dist/addon.js +1607 -522
- package/dist/addon.mjs +1607 -522
- package/package.json +4 -2
package/dist/addon.mjs
CHANGED
|
@@ -818,7 +818,7 @@ async function discoverDevices(options = {}, binder = createDgramBinder()) {
|
|
|
818
818
|
return [...byId.values()];
|
|
819
819
|
}
|
|
820
820
|
//#endregion
|
|
821
|
-
//#region ../types/dist/event-category-
|
|
821
|
+
//#region ../types/dist/event-category-CT-hz5Rb.mjs
|
|
822
822
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
823
823
|
EventCategory["SystemBoot"] = "system.boot";
|
|
824
824
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -1144,6 +1144,14 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
1144
1144
|
/** The recovery edge of {@link PipelineMotionNoisy}: the motion fell back, or a detection arrived. */
|
|
1145
1145
|
EventCategory["PipelineMotionCalm"] = "pipeline.motion-calm";
|
|
1146
1146
|
/**
|
|
1147
|
+
* A camera's package LLM confirm has failed closed three times in a row
|
|
1148
|
+
* (D931, 2026-10-26). One edge per camera until
|
|
1149
|
+
* {@link PipelinePackageLlmAnswered}.
|
|
1150
|
+
*/
|
|
1151
|
+
EventCategory["PipelinePackageLlmSilent"] = "pipeline.package-llm-silent";
|
|
1152
|
+
/** The recovery edge of {@link PipelinePackageLlmSilent}: a confirm returned yes or no. */
|
|
1153
|
+
EventCategory["PipelinePackageLlmAnswered"] = "pipeline.package-llm-answered";
|
|
1154
|
+
/**
|
|
1147
1155
|
* Per-camera pipeline config was mutated by the orchestrator
|
|
1148
1156
|
* (a settings change via `setAgentInferenceDevices` /
|
|
1149
1157
|
* `setCameraStepToggle` / `setCameraStepSettings`). Orchestrator
|
|
@@ -6253,7 +6261,7 @@ var ZodIssueCode = {
|
|
|
6253
6261
|
var ZodFirstPartyTypeKind;
|
|
6254
6262
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
6255
6263
|
//#endregion
|
|
6256
|
-
//#region ../types/dist/sleep
|
|
6264
|
+
//#region ../types/dist/sleep--jXS9hWo.mjs
|
|
6257
6265
|
/**
|
|
6258
6266
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6259
6267
|
* window to float samples (D455).
|
|
@@ -8807,6 +8815,99 @@ var RecordKindDeclarationSchema = object({
|
|
|
8807
8815
|
}).strict();
|
|
8808
8816
|
/** The kind names: lower-case, dash separated, short enough for an index name. */
|
|
8809
8817
|
var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
|
|
8818
|
+
var READING_QUANTITIES = [
|
|
8819
|
+
"temperature",
|
|
8820
|
+
"pressure",
|
|
8821
|
+
"speed",
|
|
8822
|
+
"precipitation",
|
|
8823
|
+
"precipitation-rate",
|
|
8824
|
+
"length",
|
|
8825
|
+
"area",
|
|
8826
|
+
"illuminance",
|
|
8827
|
+
"irradiance",
|
|
8828
|
+
"power",
|
|
8829
|
+
"energy",
|
|
8830
|
+
"voltage",
|
|
8831
|
+
"current",
|
|
8832
|
+
"apparent-power",
|
|
8833
|
+
"humidity",
|
|
8834
|
+
"percentage",
|
|
8835
|
+
"concentration-volume",
|
|
8836
|
+
"concentration-mass",
|
|
8837
|
+
"concentration-ppb",
|
|
8838
|
+
"volume",
|
|
8839
|
+
"duration",
|
|
8840
|
+
"duration-ms",
|
|
8841
|
+
"information",
|
|
8842
|
+
"sound-level",
|
|
8843
|
+
"signal-level",
|
|
8844
|
+
"colour-temperature",
|
|
8845
|
+
"angle",
|
|
8846
|
+
"bitrate",
|
|
8847
|
+
"rate",
|
|
8848
|
+
"mass",
|
|
8849
|
+
"index",
|
|
8850
|
+
"ratio",
|
|
8851
|
+
"count",
|
|
8852
|
+
"none",
|
|
8853
|
+
"device"
|
|
8854
|
+
];
|
|
8855
|
+
var ReadingQuantitySchema = _enum(READING_QUANTITIES);
|
|
8856
|
+
new Set(READING_QUANTITIES);
|
|
8857
|
+
/**
|
|
8858
|
+
* Stored unit symbol for a quantity. `none` means the number is dimensionless
|
|
8859
|
+
* (or an index / ratio / count). `device` means the cap has no single unit.
|
|
8860
|
+
*/
|
|
8861
|
+
var QUANTITY_BASE = {
|
|
8862
|
+
temperature: "°C",
|
|
8863
|
+
pressure: "hPa",
|
|
8864
|
+
speed: "m/s",
|
|
8865
|
+
precipitation: "mm",
|
|
8866
|
+
"precipitation-rate": "mm/h",
|
|
8867
|
+
length: "m",
|
|
8868
|
+
area: "m²",
|
|
8869
|
+
illuminance: "lx",
|
|
8870
|
+
irradiance: "W/m²",
|
|
8871
|
+
power: "W",
|
|
8872
|
+
energy: "kWh",
|
|
8873
|
+
voltage: "V",
|
|
8874
|
+
current: "A",
|
|
8875
|
+
"apparent-power": "VA",
|
|
8876
|
+
humidity: "%",
|
|
8877
|
+
percentage: "%",
|
|
8878
|
+
"concentration-volume": "ppm",
|
|
8879
|
+
"concentration-mass": "µg/m³",
|
|
8880
|
+
"concentration-ppb": "ppb",
|
|
8881
|
+
volume: "L",
|
|
8882
|
+
duration: "s",
|
|
8883
|
+
"duration-ms": "ms",
|
|
8884
|
+
information: "B",
|
|
8885
|
+
"sound-level": "dBFS",
|
|
8886
|
+
"signal-level": "dBm",
|
|
8887
|
+
"colour-temperature": "mired",
|
|
8888
|
+
angle: "°",
|
|
8889
|
+
bitrate: "kbps",
|
|
8890
|
+
rate: "fps",
|
|
8891
|
+
mass: "kg",
|
|
8892
|
+
index: "none",
|
|
8893
|
+
ratio: "none",
|
|
8894
|
+
count: "none",
|
|
8895
|
+
none: "none",
|
|
8896
|
+
device: "device"
|
|
8897
|
+
};
|
|
8898
|
+
/** The stored unit, or `undefined` when the quantity has no unit symbol. */
|
|
8899
|
+
function baseUnitOf(quantity) {
|
|
8900
|
+
const symbol = QUANTITY_BASE[quantity];
|
|
8901
|
+
if (symbol === "none" || symbol === "device") return void 0;
|
|
8902
|
+
return symbol;
|
|
8903
|
+
}
|
|
8904
|
+
function readingMeta(quantity, extras) {
|
|
8905
|
+
return {
|
|
8906
|
+
quantity,
|
|
8907
|
+
...extras?.precision !== void 0 ? { precision: extras.precision } : {},
|
|
8908
|
+
...extras?.label !== void 0 ? { label: extras.label } : {}
|
|
8909
|
+
};
|
|
8910
|
+
}
|
|
8810
8911
|
/** Shared Zod schemas used across streaming capabilities. */
|
|
8811
8912
|
var CamProfileSchema = _enum([
|
|
8812
8913
|
"high",
|
|
@@ -8834,8 +8935,8 @@ var CamStreamKindSchema = _enum([
|
|
|
8834
8935
|
"derived"
|
|
8835
8936
|
]);
|
|
8836
8937
|
var CamStreamResolutionSchema = object({
|
|
8837
|
-
width: number().int().positive(),
|
|
8838
|
-
height: number().int().positive()
|
|
8938
|
+
width: number().int().positive().meta(readingMeta("none")),
|
|
8939
|
+
height: number().int().positive().meta(readingMeta("none"))
|
|
8839
8940
|
});
|
|
8840
8941
|
var CameraStreamSchema = object({
|
|
8841
8942
|
/** Stable, provider-assigned id unique within the (deviceId) scope. */
|
|
@@ -9353,6 +9454,9 @@ var CAP_NAMES = Object.freeze([
|
|
|
9353
9454
|
"filesystem-browse",
|
|
9354
9455
|
"flood",
|
|
9355
9456
|
"gas",
|
|
9457
|
+
"group-active",
|
|
9458
|
+
"group-commands",
|
|
9459
|
+
"group-status",
|
|
9356
9460
|
"heating-valve",
|
|
9357
9461
|
"humidifier",
|
|
9358
9462
|
"humidity-sensor",
|
|
@@ -9373,6 +9477,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9373
9477
|
"login-method",
|
|
9374
9478
|
"media-player",
|
|
9375
9479
|
"mesh-network",
|
|
9480
|
+
"meter",
|
|
9376
9481
|
"metrics-provider",
|
|
9377
9482
|
"model-convert",
|
|
9378
9483
|
"model-distributor",
|
|
@@ -9482,14 +9587,6 @@ Object.freeze({
|
|
|
9482
9587
|
{
|
|
9483
9588
|
"path": "aqi",
|
|
9484
9589
|
"valueType": "number"
|
|
9485
|
-
},
|
|
9486
|
-
{
|
|
9487
|
-
"path": "unit",
|
|
9488
|
-
"valueType": "string"
|
|
9489
|
-
},
|
|
9490
|
-
{
|
|
9491
|
-
"path": "precision",
|
|
9492
|
-
"valueType": "number"
|
|
9493
9590
|
}
|
|
9494
9591
|
],
|
|
9495
9592
|
"alarm-panel": [
|
|
@@ -9525,20 +9622,10 @@ Object.freeze({
|
|
|
9525
9622
|
"valueType": "boolean"
|
|
9526
9623
|
}
|
|
9527
9624
|
],
|
|
9528
|
-
"ambient-light-sensor": [
|
|
9529
|
-
|
|
9530
|
-
|
|
9531
|
-
|
|
9532
|
-
},
|
|
9533
|
-
{
|
|
9534
|
-
"path": "unit",
|
|
9535
|
-
"valueType": "string"
|
|
9536
|
-
},
|
|
9537
|
-
{
|
|
9538
|
-
"path": "precision",
|
|
9539
|
-
"valueType": "number"
|
|
9540
|
-
}
|
|
9541
|
-
],
|
|
9625
|
+
"ambient-light-sensor": [{
|
|
9626
|
+
"path": "lux",
|
|
9627
|
+
"valueType": "number"
|
|
9628
|
+
}],
|
|
9542
9629
|
"audio-metrics": [
|
|
9543
9630
|
{
|
|
9544
9631
|
"path": "ts",
|
|
@@ -9618,6 +9705,10 @@ Object.freeze({
|
|
|
9618
9705
|
"path": "lastUpdated",
|
|
9619
9706
|
"valueType": "number"
|
|
9620
9707
|
},
|
|
9708
|
+
{
|
|
9709
|
+
"path": "volts",
|
|
9710
|
+
"valueType": "number"
|
|
9711
|
+
},
|
|
9621
9712
|
{
|
|
9622
9713
|
"path": "lastContactAt",
|
|
9623
9714
|
"valueType": "number"
|
|
@@ -9860,10 +9951,6 @@ Object.freeze({
|
|
|
9860
9951
|
"datetime"
|
|
9861
9952
|
]
|
|
9862
9953
|
},
|
|
9863
|
-
{
|
|
9864
|
-
"path": "unit",
|
|
9865
|
-
"valueType": "string"
|
|
9866
|
-
},
|
|
9867
9954
|
{
|
|
9868
9955
|
"path": "min",
|
|
9869
9956
|
"valueType": "number"
|
|
@@ -9875,19 +9962,6 @@ Object.freeze({
|
|
|
9875
9962
|
{
|
|
9876
9963
|
"path": "step",
|
|
9877
9964
|
"valueType": "number"
|
|
9878
|
-
},
|
|
9879
|
-
{
|
|
9880
|
-
"path": "precision",
|
|
9881
|
-
"valueType": "number"
|
|
9882
|
-
},
|
|
9883
|
-
{
|
|
9884
|
-
"path": "format",
|
|
9885
|
-
"valueType": "enum",
|
|
9886
|
-
"enumValues": [
|
|
9887
|
-
"date",
|
|
9888
|
-
"time",
|
|
9889
|
-
"datetime"
|
|
9890
|
-
]
|
|
9891
9965
|
}
|
|
9892
9966
|
],
|
|
9893
9967
|
"cover": [
|
|
@@ -10128,13 +10202,8 @@ Object.freeze({
|
|
|
10128
10202
|
"path": "value",
|
|
10129
10203
|
"valueType": "string"
|
|
10130
10204
|
}, {
|
|
10131
|
-
"path": "
|
|
10132
|
-
"valueType": "
|
|
10133
|
-
"enumValues": [
|
|
10134
|
-
"date",
|
|
10135
|
-
"time",
|
|
10136
|
-
"datetime"
|
|
10137
|
-
]
|
|
10205
|
+
"path": "timestamp",
|
|
10206
|
+
"valueType": "boolean"
|
|
10138
10207
|
}],
|
|
10139
10208
|
"event-emitter": [
|
|
10140
10209
|
{
|
|
@@ -10207,6 +10276,24 @@ Object.freeze({
|
|
|
10207
10276
|
"path": "detected",
|
|
10208
10277
|
"valueType": "boolean"
|
|
10209
10278
|
}],
|
|
10279
|
+
"group-active": [{
|
|
10280
|
+
"path": "active",
|
|
10281
|
+
"valueType": "boolean"
|
|
10282
|
+
}],
|
|
10283
|
+
"group-status": [
|
|
10284
|
+
{
|
|
10285
|
+
"path": "included",
|
|
10286
|
+
"valueType": "number"
|
|
10287
|
+
},
|
|
10288
|
+
{
|
|
10289
|
+
"path": "excluded",
|
|
10290
|
+
"valueType": "number"
|
|
10291
|
+
},
|
|
10292
|
+
{
|
|
10293
|
+
"path": "total",
|
|
10294
|
+
"valueType": "number"
|
|
10295
|
+
}
|
|
10296
|
+
],
|
|
10210
10297
|
"heating-valve": [{
|
|
10211
10298
|
"path": "position",
|
|
10212
10299
|
"valueType": "number"
|
|
@@ -10241,20 +10328,10 @@ Object.freeze({
|
|
|
10241
10328
|
"valueType": "number"
|
|
10242
10329
|
}
|
|
10243
10330
|
],
|
|
10244
|
-
"humidity-sensor": [
|
|
10245
|
-
|
|
10246
|
-
|
|
10247
|
-
|
|
10248
|
-
},
|
|
10249
|
-
{
|
|
10250
|
-
"path": "unit",
|
|
10251
|
-
"valueType": "string"
|
|
10252
|
-
},
|
|
10253
|
-
{
|
|
10254
|
-
"path": "precision",
|
|
10255
|
-
"valueType": "number"
|
|
10256
|
-
}
|
|
10257
|
-
],
|
|
10331
|
+
"humidity-sensor": [{
|
|
10332
|
+
"path": "percent",
|
|
10333
|
+
"valueType": "number"
|
|
10334
|
+
}],
|
|
10258
10335
|
"image": [{
|
|
10259
10336
|
"path": "url",
|
|
10260
10337
|
"valueType": "string"
|
|
@@ -10462,6 +10539,10 @@ Object.freeze({
|
|
|
10462
10539
|
]
|
|
10463
10540
|
}
|
|
10464
10541
|
],
|
|
10542
|
+
"meter": [{
|
|
10543
|
+
"path": "value",
|
|
10544
|
+
"valueType": "number"
|
|
10545
|
+
}],
|
|
10465
10546
|
"motion": [
|
|
10466
10547
|
{
|
|
10467
10548
|
"path": "detected",
|
|
@@ -10593,20 +10674,10 @@ Object.freeze({
|
|
|
10593
10674
|
"valueType": "boolean"
|
|
10594
10675
|
}
|
|
10595
10676
|
],
|
|
10596
|
-
"numeric-sensor": [
|
|
10597
|
-
|
|
10598
|
-
|
|
10599
|
-
|
|
10600
|
-
},
|
|
10601
|
-
{
|
|
10602
|
-
"path": "unit",
|
|
10603
|
-
"valueType": "string"
|
|
10604
|
-
},
|
|
10605
|
-
{
|
|
10606
|
-
"path": "precision",
|
|
10607
|
-
"valueType": "number"
|
|
10608
|
-
}
|
|
10609
|
-
],
|
|
10677
|
+
"numeric-sensor": [{
|
|
10678
|
+
"path": "value",
|
|
10679
|
+
"valueType": "number"
|
|
10680
|
+
}],
|
|
10610
10681
|
"pet-feeder": [
|
|
10611
10682
|
{
|
|
10612
10683
|
"path": "foodLevel",
|
|
@@ -10698,14 +10769,6 @@ Object.freeze({
|
|
|
10698
10769
|
{
|
|
10699
10770
|
"path": "powerFactor",
|
|
10700
10771
|
"valueType": "number"
|
|
10701
|
-
},
|
|
10702
|
-
{
|
|
10703
|
-
"path": "unit",
|
|
10704
|
-
"valueType": "string"
|
|
10705
|
-
},
|
|
10706
|
-
{
|
|
10707
|
-
"path": "precision",
|
|
10708
|
-
"valueType": "number"
|
|
10709
10772
|
}
|
|
10710
10773
|
],
|
|
10711
10774
|
"presence": [
|
|
@@ -10734,20 +10797,10 @@ Object.freeze({
|
|
|
10734
10797
|
"valueType": "number"
|
|
10735
10798
|
}
|
|
10736
10799
|
],
|
|
10737
|
-
"pressure-sensor": [
|
|
10738
|
-
|
|
10739
|
-
|
|
10740
|
-
|
|
10741
|
-
},
|
|
10742
|
-
{
|
|
10743
|
-
"path": "unit",
|
|
10744
|
-
"valueType": "string"
|
|
10745
|
-
},
|
|
10746
|
-
{
|
|
10747
|
-
"path": "precision",
|
|
10748
|
-
"valueType": "number"
|
|
10749
|
-
}
|
|
10750
|
-
],
|
|
10800
|
+
"pressure-sensor": [{
|
|
10801
|
+
"path": "hpa",
|
|
10802
|
+
"valueType": "number"
|
|
10803
|
+
}],
|
|
10751
10804
|
"privacy-mask": [{
|
|
10752
10805
|
"path": "enabled",
|
|
10753
10806
|
"valueType": "boolean"
|
|
@@ -11058,20 +11111,10 @@ Object.freeze({
|
|
|
11058
11111
|
"path": "tampered",
|
|
11059
11112
|
"valueType": "boolean"
|
|
11060
11113
|
}],
|
|
11061
|
-
"temperature-sensor": [
|
|
11062
|
-
|
|
11063
|
-
|
|
11064
|
-
|
|
11065
|
-
},
|
|
11066
|
-
{
|
|
11067
|
-
"path": "unit",
|
|
11068
|
-
"valueType": "string"
|
|
11069
|
-
},
|
|
11070
|
-
{
|
|
11071
|
-
"path": "precision",
|
|
11072
|
-
"valueType": "number"
|
|
11073
|
-
}
|
|
11074
|
-
],
|
|
11114
|
+
"temperature-sensor": [{
|
|
11115
|
+
"path": "celsius",
|
|
11116
|
+
"valueType": "number"
|
|
11117
|
+
}],
|
|
11075
11118
|
"update": [
|
|
11076
11119
|
{
|
|
11077
11120
|
"path": "currentVersion",
|
|
@@ -11182,10 +11225,6 @@ Object.freeze({
|
|
|
11182
11225
|
"path": "temperature",
|
|
11183
11226
|
"valueType": "number"
|
|
11184
11227
|
},
|
|
11185
|
-
{
|
|
11186
|
-
"path": "temperatureUnit",
|
|
11187
|
-
"valueType": "string"
|
|
11188
|
-
},
|
|
11189
11228
|
{
|
|
11190
11229
|
"path": "humidity",
|
|
11191
11230
|
"valueType": "number"
|
|
@@ -11194,18 +11233,10 @@ Object.freeze({
|
|
|
11194
11233
|
"path": "pressure",
|
|
11195
11234
|
"valueType": "number"
|
|
11196
11235
|
},
|
|
11197
|
-
{
|
|
11198
|
-
"path": "pressureUnit",
|
|
11199
|
-
"valueType": "string"
|
|
11200
|
-
},
|
|
11201
11236
|
{
|
|
11202
11237
|
"path": "windSpeed",
|
|
11203
11238
|
"valueType": "number"
|
|
11204
11239
|
},
|
|
11205
|
-
{
|
|
11206
|
-
"path": "windSpeedUnit",
|
|
11207
|
-
"valueType": "string"
|
|
11208
|
-
},
|
|
11209
11240
|
{
|
|
11210
11241
|
"path": "windBearing",
|
|
11211
11242
|
"valueType": "number"
|
|
@@ -11255,7 +11286,7 @@ object({
|
|
|
11255
11286
|
params: record(string(), union([string(), number()])).optional()
|
|
11256
11287
|
});
|
|
11257
11288
|
//#endregion
|
|
11258
|
-
//#region ../types/dist/err-msg-
|
|
11289
|
+
//#region ../types/dist/err-msg-DIorGYeC.mjs
|
|
11259
11290
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
11260
11291
|
DeviceType["Camera"] = "camera";
|
|
11261
11292
|
DeviceType["Hub"] = "hub";
|
|
@@ -11349,8 +11380,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
11349
11380
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
11350
11381
|
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
11351
11382
|
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
11352
|
-
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
11383
|
+
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
11384
|
+
* A plug-built dishwasher carries running-state and power instead. */
|
|
11353
11385
|
DeviceType["Dishwasher"] = "dishwasher";
|
|
11386
|
+
/** Washing machine. A plug-built one is running while its power stays
|
|
11387
|
+
* above a threshold. No program cap — that surface is the dishwasher's. */
|
|
11388
|
+
DeviceType["Washer"] = "washer";
|
|
11389
|
+
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
11390
|
+
DeviceType["Dryer"] = "dryer";
|
|
11354
11391
|
return DeviceType;
|
|
11355
11392
|
}({});
|
|
11356
11393
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -11608,7 +11645,7 @@ function errMsg(err) {
|
|
|
11608
11645
|
return String(err);
|
|
11609
11646
|
}
|
|
11610
11647
|
//#endregion
|
|
11611
|
-
//#region ../types/dist/composition-
|
|
11648
|
+
//#region ../types/dist/composition-Bb8iwHEJ.mjs
|
|
11612
11649
|
/**
|
|
11613
11650
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11614
11651
|
*
|
|
@@ -11923,6 +11960,13 @@ var table = {
|
|
|
11923
11960
|
apply: () => {
|
|
11924
11961
|
throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
|
|
11925
11962
|
}
|
|
11963
|
+
},
|
|
11964
|
+
schmitt: {
|
|
11965
|
+
minArgs: 3,
|
|
11966
|
+
maxArgs: 5,
|
|
11967
|
+
apply: () => {
|
|
11968
|
+
throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
|
|
11969
|
+
}
|
|
11926
11970
|
}
|
|
11927
11971
|
};
|
|
11928
11972
|
Object.freeze(Object.assign(Object.create(null), table));
|
|
@@ -12363,15 +12407,232 @@ function validateExpressionSource(src) {
|
|
|
12363
12407
|
}
|
|
12364
12408
|
return null;
|
|
12365
12409
|
}
|
|
12366
|
-
/**
|
|
12367
|
-
|
|
12368
|
-
|
|
12369
|
-
|
|
12410
|
+
/**
|
|
12411
|
+
* One power reading becomes a washer, dryer or dishwasher (D944 helper).
|
|
12412
|
+
*
|
|
12413
|
+
* Standby of the control board is under 5 W; a motor or a heater is far
|
|
12414
|
+
* above that, so 5 W is the line. A door lock or a short pump spike is not
|
|
12415
|
+
* a cycle, so running starts only after 30 s above the line.
|
|
12416
|
+
*
|
|
12417
|
+
* A wash, dry or dishwasher pause (soak, heat-wait, heater off between
|
|
12418
|
+
* bursts) lasts several minutes. Ending the cycle on the first dip would
|
|
12419
|
+
* report "finished" in the middle of the program, so the stop dwell is
|
|
12420
|
+
* 10 minutes on every type. The number is stored per type so one machine
|
|
12421
|
+
* can move later without moving the others.
|
|
12422
|
+
*/
|
|
12423
|
+
var APPLIANCE_TEMPLATE_ID = "appliance";
|
|
12424
|
+
var APPLIANCE_KINDS = [
|
|
12425
|
+
"washer",
|
|
12426
|
+
"dryer",
|
|
12427
|
+
"dishwasher"
|
|
12428
|
+
];
|
|
12429
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
12430
|
+
/**
|
|
12431
|
+
* Statistics and integrals over a history read (D944).
|
|
12432
|
+
*
|
|
12433
|
+
* Pure: the same points always give the same answer, so a restart recomputes
|
|
12434
|
+
* rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
|
|
12435
|
+
* never the mean of the samples. A hole with no held value is not integrated
|
|
12436
|
+
* as zero. A truncated read is not published.
|
|
12437
|
+
*/
|
|
12438
|
+
var STATISTIC_STATS = [
|
|
12439
|
+
"min",
|
|
12440
|
+
"max",
|
|
12441
|
+
"mean",
|
|
12442
|
+
"sum",
|
|
12443
|
+
"count",
|
|
12444
|
+
"change"
|
|
12445
|
+
];
|
|
12446
|
+
/**
|
|
12447
|
+
* The range a statistics or integral helper asks history for (D944).
|
|
12448
|
+
*
|
|
12449
|
+
* Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
|
|
12450
|
+
* or 25 hours across a DST change. A tier is eligible only when its retention
|
|
12451
|
+
* is strictly longer than the longest that range can be. Forever qualifies.
|
|
12452
|
+
* Equal to a tier's retention is a refusal, not a short answer.
|
|
12453
|
+
*/
|
|
12454
|
+
var MINUTE_MS = 6e4;
|
|
12455
|
+
var HOUR_MS = 60 * MINUTE_MS;
|
|
12456
|
+
var DAY_MS = 24 * HOUR_MS;
|
|
12457
|
+
var ROLLING_SPANS = [
|
|
12458
|
+
"1h",
|
|
12459
|
+
"24h",
|
|
12460
|
+
"2d",
|
|
12461
|
+
"7d",
|
|
12462
|
+
"30d",
|
|
12463
|
+
"custom"
|
|
12464
|
+
];
|
|
12465
|
+
var CALENDAR_PERIODS = [
|
|
12466
|
+
"today",
|
|
12467
|
+
"yesterday",
|
|
12468
|
+
"this-week",
|
|
12469
|
+
"this-month"
|
|
12470
|
+
];
|
|
12471
|
+
var RESET_PERIODS = [
|
|
12472
|
+
"never",
|
|
12473
|
+
"daily",
|
|
12474
|
+
"weekly",
|
|
12475
|
+
"monthly"
|
|
12476
|
+
];
|
|
12477
|
+
5 * MINUTE_MS;
|
|
12478
|
+
24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
|
|
12479
|
+
7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
|
|
12480
|
+
var rollingWindowObject = object({
|
|
12481
|
+
kind: literal("rolling"),
|
|
12482
|
+
span: _enum(ROLLING_SPANS),
|
|
12483
|
+
customMinutes: number().int().min(5).optional()
|
|
12484
|
+
}).check((payload) => {
|
|
12485
|
+
if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
|
|
12486
|
+
code: "custom",
|
|
12487
|
+
message: "a custom range needs a duration in minutes",
|
|
12488
|
+
path: ["customMinutes"],
|
|
12489
|
+
input: payload.value
|
|
12490
|
+
});
|
|
12491
|
+
if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
|
|
12492
|
+
code: "custom",
|
|
12493
|
+
message: "only a custom range carries a duration",
|
|
12494
|
+
path: ["customMinutes"],
|
|
12495
|
+
input: payload.value
|
|
12496
|
+
});
|
|
12497
|
+
});
|
|
12498
|
+
var RollingWindowSchema = rollingWindowObject;
|
|
12499
|
+
var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
|
|
12500
|
+
kind: literal("calendar"),
|
|
12501
|
+
period: _enum(CALENDAR_PERIODS)
|
|
12502
|
+
})]);
|
|
12503
|
+
/**
|
|
12504
|
+
* A composition source identity: `(addonId, stableId)`, never a numeric device
|
|
12505
|
+
* id. Split out so helper schemas can name it without a cycle through
|
|
12506
|
+
* `composition.ts` (D659).
|
|
12507
|
+
*/
|
|
12370
12508
|
var CompositionSourceRefSchema = object({
|
|
12371
12509
|
/** The owning addon; `stableId` is unique only within it. */
|
|
12372
12510
|
addonId: string().min(1),
|
|
12373
12511
|
stableId: string().min(1)
|
|
12374
12512
|
});
|
|
12513
|
+
var GROUP_TEMPLATE_IDS = [
|
|
12514
|
+
"temperature",
|
|
12515
|
+
"humidity",
|
|
12516
|
+
"pressure",
|
|
12517
|
+
"illuminance",
|
|
12518
|
+
"power",
|
|
12519
|
+
"energy",
|
|
12520
|
+
"battery",
|
|
12521
|
+
"numeric",
|
|
12522
|
+
"motion",
|
|
12523
|
+
"contact",
|
|
12524
|
+
"leak",
|
|
12525
|
+
"smoke",
|
|
12526
|
+
"occupancy",
|
|
12527
|
+
"binary",
|
|
12528
|
+
"switch",
|
|
12529
|
+
"light",
|
|
12530
|
+
"fan",
|
|
12531
|
+
"cover",
|
|
12532
|
+
"valve",
|
|
12533
|
+
"lock",
|
|
12534
|
+
"media-player",
|
|
12535
|
+
"climate"
|
|
12536
|
+
];
|
|
12537
|
+
var NUMERIC_AGGREGATES = [
|
|
12538
|
+
"mean",
|
|
12539
|
+
"median",
|
|
12540
|
+
"min",
|
|
12541
|
+
"max",
|
|
12542
|
+
"sum"
|
|
12543
|
+
];
|
|
12544
|
+
var BINARY_AGGREGATES = ["any", "all"];
|
|
12545
|
+
/** How an actuator group decides it is on. Unknown members never count as off. */
|
|
12546
|
+
var ACTIVE_CRITERIA = ["at-least-one", "all"];
|
|
12547
|
+
var AGGREGATE_REDUCERS = [
|
|
12548
|
+
"mean",
|
|
12549
|
+
"median",
|
|
12550
|
+
"min",
|
|
12551
|
+
"max",
|
|
12552
|
+
"sum",
|
|
12553
|
+
"any",
|
|
12554
|
+
"all"
|
|
12555
|
+
];
|
|
12556
|
+
var GroupTemplateIdSchema = _enum(GROUP_TEMPLATE_IDS);
|
|
12557
|
+
var AggregateReducerSchema = _enum(AGGREGATE_REDUCERS);
|
|
12558
|
+
var ActiveCriterionSchema = _enum(ACTIVE_CRITERIA);
|
|
12559
|
+
/** One member. The field is stored so occupancy can name motion or binary. */
|
|
12560
|
+
var HelperMemberSchema = object({
|
|
12561
|
+
source: CompositionSourceRefSchema,
|
|
12562
|
+
cap: string().min(1),
|
|
12563
|
+
fieldPath: string().min(1)
|
|
12564
|
+
});
|
|
12565
|
+
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
12566
|
+
object({
|
|
12567
|
+
kind: literal("group"),
|
|
12568
|
+
templateId: GroupTemplateIdSchema,
|
|
12569
|
+
aggregate: AggregateReducerSchema,
|
|
12570
|
+
members: array(HelperMemberSchema).min(1).max(64),
|
|
12571
|
+
/** Actuator groups only. Readings leave this unset. */
|
|
12572
|
+
active: ActiveCriterionSchema.optional()
|
|
12573
|
+
}),
|
|
12574
|
+
object({
|
|
12575
|
+
kind: literal("appliance"),
|
|
12576
|
+
templateId: literal(APPLIANCE_TEMPLATE_ID),
|
|
12577
|
+
appliance: _enum(APPLIANCE_KINDS),
|
|
12578
|
+
thresholdWatts: number().positive(),
|
|
12579
|
+
startDwellMs: number().int().nonnegative(),
|
|
12580
|
+
stopDwellMs: number().int().nonnegative(),
|
|
12581
|
+
member: HelperMemberSchema
|
|
12582
|
+
}),
|
|
12583
|
+
object({
|
|
12584
|
+
kind: literal("statistic"),
|
|
12585
|
+
stat: _enum(STATISTIC_STATS),
|
|
12586
|
+
window: StatisticWindowSchema,
|
|
12587
|
+
source: HelperMemberSchema
|
|
12588
|
+
}),
|
|
12589
|
+
object({
|
|
12590
|
+
kind: literal("integral"),
|
|
12591
|
+
reset: _enum(RESET_PERIODS),
|
|
12592
|
+
output: _enum(["energy", "meter"]),
|
|
12593
|
+
source: HelperMemberSchema
|
|
12594
|
+
}),
|
|
12595
|
+
object({
|
|
12596
|
+
kind: literal("threshold"),
|
|
12597
|
+
source: HelperMemberSchema,
|
|
12598
|
+
high: number(),
|
|
12599
|
+
low: number(),
|
|
12600
|
+
onForMs: number().int().nonnegative(),
|
|
12601
|
+
offForMs: number().int().nonnegative()
|
|
12602
|
+
}),
|
|
12603
|
+
object({
|
|
12604
|
+
kind: literal("arithmetic"),
|
|
12605
|
+
op: _enum([
|
|
12606
|
+
"sum",
|
|
12607
|
+
"difference",
|
|
12608
|
+
"ratio"
|
|
12609
|
+
]),
|
|
12610
|
+
inputs: array(HelperMemberSchema).min(2).max(64)
|
|
12611
|
+
}),
|
|
12612
|
+
object({
|
|
12613
|
+
kind: literal("expression"),
|
|
12614
|
+
expr: string().min(1),
|
|
12615
|
+
inputs: array(object({
|
|
12616
|
+
name: string().min(1),
|
|
12617
|
+
member: HelperMemberSchema
|
|
12618
|
+
})).min(1).max(64),
|
|
12619
|
+
output: _enum(["number", "boolean"])
|
|
12620
|
+
}),
|
|
12621
|
+
object({
|
|
12622
|
+
kind: literal("derivative"),
|
|
12623
|
+
window: RollingWindowSchema,
|
|
12624
|
+
per: _enum([
|
|
12625
|
+
"second",
|
|
12626
|
+
"minute",
|
|
12627
|
+
"hour"
|
|
12628
|
+
]),
|
|
12629
|
+
source: HelperMemberSchema
|
|
12630
|
+
})
|
|
12631
|
+
]);
|
|
12632
|
+
/** A composed device's stableId is this prefix plus the block id. */
|
|
12633
|
+
var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
|
|
12634
|
+
/** Longest snippet a `code` source may carry (slice 4). */
|
|
12635
|
+
var MAX_COMPOSITION_CODE_LENGTH = 2e4;
|
|
12375
12636
|
/** One field of one capability of one source device. */
|
|
12376
12637
|
var CompositionFieldReadSchema = object({
|
|
12377
12638
|
source: CompositionSourceRefSchema,
|
|
@@ -12415,7 +12676,59 @@ var CompositionCodeSourceSchema = object({
|
|
|
12415
12676
|
var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
12416
12677
|
CompositionFromSourceSchema,
|
|
12417
12678
|
CompositionExpressionSourceSchema,
|
|
12418
|
-
CompositionCodeSourceSchema
|
|
12679
|
+
CompositionCodeSourceSchema,
|
|
12680
|
+
object({
|
|
12681
|
+
kind: literal("aggregate"),
|
|
12682
|
+
reducer: _enum([
|
|
12683
|
+
"mean",
|
|
12684
|
+
"median",
|
|
12685
|
+
"min",
|
|
12686
|
+
"max",
|
|
12687
|
+
"sum",
|
|
12688
|
+
"any",
|
|
12689
|
+
"all"
|
|
12690
|
+
]),
|
|
12691
|
+
reads: array(CompositionFieldReadSchema).min(1).max(64),
|
|
12692
|
+
/**
|
|
12693
|
+
* Set when the reads are an enum state, not a yes/no: the listed states
|
|
12694
|
+
* count as active (a cover is `open`). A state outside the list is inactive;
|
|
12695
|
+
* a read that is not a state at all is left out. Only `any` / `all` use it.
|
|
12696
|
+
*/
|
|
12697
|
+
activeWhen: array(string().min(1)).min(1).optional(),
|
|
12698
|
+
/** `value` is the reading; the others are the member counts. */
|
|
12699
|
+
publish: _enum([
|
|
12700
|
+
"value",
|
|
12701
|
+
"included",
|
|
12702
|
+
"excluded",
|
|
12703
|
+
"total"
|
|
12704
|
+
]),
|
|
12705
|
+
/** What a `value` publish does when nobody is included. Counts ignore this. */
|
|
12706
|
+
empty: _enum([
|
|
12707
|
+
"null",
|
|
12708
|
+
"omit",
|
|
12709
|
+
"offline"
|
|
12710
|
+
])
|
|
12711
|
+
}),
|
|
12712
|
+
object({
|
|
12713
|
+
kind: literal("series"),
|
|
12714
|
+
read: CompositionFieldReadSchema,
|
|
12715
|
+
/** `value` is the statistic or the integral. The others are the gap counts. */
|
|
12716
|
+
publish: _enum([
|
|
12717
|
+
"value",
|
|
12718
|
+
"included",
|
|
12719
|
+
"excluded",
|
|
12720
|
+
"total"
|
|
12721
|
+
]),
|
|
12722
|
+
/**
|
|
12723
|
+
* What an unanswered `value` does. Counts ignore this. `offline` leaves the
|
|
12724
|
+
* device unseeded until history answers, so a restart cannot publish 0.
|
|
12725
|
+
*/
|
|
12726
|
+
empty: _enum([
|
|
12727
|
+
"null",
|
|
12728
|
+
"omit",
|
|
12729
|
+
"offline"
|
|
12730
|
+
])
|
|
12731
|
+
})
|
|
12419
12732
|
]);
|
|
12420
12733
|
var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
|
|
12421
12734
|
kind: literal("forward"),
|
|
@@ -12461,7 +12774,13 @@ var CompositionSchema = object({
|
|
|
12461
12774
|
kind: literal("existing"),
|
|
12462
12775
|
device: CompositionSourceRefSchema
|
|
12463
12776
|
})]),
|
|
12464
|
-
features: array(CompositionFeatureSchema).min(1).max(16)
|
|
12777
|
+
features: array(CompositionFeatureSchema).min(1).max(16),
|
|
12778
|
+
/**
|
|
12779
|
+
* Present when the block is a helper template. Absent on a free
|
|
12780
|
+
* composition (the room example, device 4603). The editor reads this,
|
|
12781
|
+
* never the compiled features.
|
|
12782
|
+
*/
|
|
12783
|
+
helper: HelperSpecSchema.optional()
|
|
12465
12784
|
}).superRefine((composition, ctx) => {
|
|
12466
12785
|
const seen = /* @__PURE__ */ new Set();
|
|
12467
12786
|
composition.features.forEach((feature, index) => {
|
|
@@ -17568,7 +17887,22 @@ var BrokerProviderInfoSchema = object({
|
|
|
17568
17887
|
*/
|
|
17569
17888
|
interactiveConnectPath: string().min(1).optional(),
|
|
17570
17889
|
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
17571
|
-
interactiveDisconnectPath: string().min(1).optional()
|
|
17890
|
+
interactiveDisconnectPath: string().min(1).optional(),
|
|
17891
|
+
/**
|
|
17892
|
+
* The kind signs in through a server-held interactive session instead of
|
|
17893
|
+
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
17894
|
+
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
17895
|
+
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
17896
|
+
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
17897
|
+
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
17898
|
+
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
17899
|
+
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
17900
|
+
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
17901
|
+
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
17902
|
+
* offers it as the default path; the fields the session fills are
|
|
17903
|
+
* marked `filledBySignIn` in the kind's settings schema.
|
|
17904
|
+
*/
|
|
17905
|
+
signIn: object({ basePath: string().min(1) }).optional()
|
|
17572
17906
|
}))
|
|
17573
17907
|
});
|
|
17574
17908
|
var ListInputSchema = object({
|
|
@@ -18669,7 +19003,12 @@ var CompositionProblemSchema = object({
|
|
|
18669
19003
|
"item-field-reserved",
|
|
18670
19004
|
"target-missing",
|
|
18671
19005
|
"target-unreadable",
|
|
18672
|
-
"not-replaceable"
|
|
19006
|
+
"not-replaceable",
|
|
19007
|
+
"native-capability-replace",
|
|
19008
|
+
"mixed-quantity",
|
|
19009
|
+
"helper-mismatch",
|
|
19010
|
+
"member-constraint",
|
|
19011
|
+
"retention"
|
|
18673
19012
|
]),
|
|
18674
19013
|
path: string(),
|
|
18675
19014
|
message: string()
|
|
@@ -18690,7 +19029,8 @@ var CompositionUnavailableModeSchema = _enum([
|
|
|
18690
19029
|
"null",
|
|
18691
19030
|
"offline",
|
|
18692
19031
|
"unknown",
|
|
18693
|
-
"release"
|
|
19032
|
+
"release",
|
|
19033
|
+
"omit"
|
|
18694
19034
|
]);
|
|
18695
19035
|
/**
|
|
18696
19036
|
* `derived` reads a source · `clock` reads only `now`, recomputed when a sibling
|
|
@@ -18727,9 +19067,11 @@ var CompositionFieldPlanSchema = object({
|
|
|
18727
19067
|
reads: array(CompositionFieldReadSchema)
|
|
18728
19068
|
});
|
|
18729
19069
|
/**
|
|
18730
|
-
* What a feature does to its target (D663): `new` builds a capability on a
|
|
18731
|
-
* device; on an existing device it `add`s a capability the device lacks
|
|
18732
|
-
*
|
|
19070
|
+
* What a feature does to its target (D663, D944): `new` builds a capability on a
|
|
19071
|
+
* new device; on an existing device it `add`s a capability the device lacks.
|
|
19072
|
+
* Replacing a capability the device already serves is refused
|
|
19073
|
+
* (`native-capability-replace`), not planned. `replace` remains on the claim
|
|
19074
|
+
* wire for a claim the hub already holds.
|
|
18733
19075
|
*/
|
|
18734
19076
|
var CompositionFeatureModeSchema = _enum([
|
|
18735
19077
|
"new",
|
|
@@ -18900,8 +19242,392 @@ Object.freeze({
|
|
|
18900
19242
|
latchedAt: {
|
|
18901
19243
|
kind: "fixed",
|
|
18902
19244
|
result: ["number", "null"]
|
|
19245
|
+
},
|
|
19246
|
+
schmitt: {
|
|
19247
|
+
kind: "fixed",
|
|
19248
|
+
result: ["boolean", "null"]
|
|
18903
19249
|
}
|
|
18904
19250
|
});
|
|
19251
|
+
/**
|
|
19252
|
+
* One catalog row per base reading a group helper can build (D944 §3).
|
|
19253
|
+
* Data, not a form default written into the block.
|
|
19254
|
+
*/
|
|
19255
|
+
var NOW$2 = {
|
|
19256
|
+
fieldPath: "lastFetchedAt",
|
|
19257
|
+
expr: "now"
|
|
19258
|
+
};
|
|
19259
|
+
var CHANGED = {
|
|
19260
|
+
fieldPath: "lastChangedAt",
|
|
19261
|
+
expr: "now"
|
|
19262
|
+
};
|
|
19263
|
+
function numeric(input) {
|
|
19264
|
+
return {
|
|
19265
|
+
...input,
|
|
19266
|
+
family: "numeric",
|
|
19267
|
+
deviceType: DeviceType.Sensor,
|
|
19268
|
+
memberDeviceType: DeviceType.Sensor,
|
|
19269
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
19270
|
+
activeWhen: void 0,
|
|
19271
|
+
commandCaps: [],
|
|
19272
|
+
baseUnit: baseUnitOf(input.quantity),
|
|
19273
|
+
aggregates: NUMERIC_AGGREGATES,
|
|
19274
|
+
accept: [{
|
|
19275
|
+
cap: input.outputCap,
|
|
19276
|
+
fieldPath: input.outputField
|
|
19277
|
+
}]
|
|
19278
|
+
};
|
|
19279
|
+
}
|
|
19280
|
+
function binary(input) {
|
|
19281
|
+
return {
|
|
19282
|
+
...input,
|
|
19283
|
+
family: "binary",
|
|
19284
|
+
deviceType: DeviceType.Sensor,
|
|
19285
|
+
memberDeviceType: DeviceType.Sensor,
|
|
19286
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
19287
|
+
activeWhen: void 0,
|
|
19288
|
+
commandCaps: [],
|
|
19289
|
+
quantity: "none",
|
|
19290
|
+
baseUnit: void 0,
|
|
19291
|
+
aggregates: BINARY_AGGREGATES,
|
|
19292
|
+
empty: "offline"
|
|
19293
|
+
};
|
|
19294
|
+
}
|
|
19295
|
+
var GROUP_TEMPLATES = [
|
|
19296
|
+
numeric({
|
|
19297
|
+
id: "temperature",
|
|
19298
|
+
summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
|
|
19299
|
+
role: DeviceRole.TemperatureSensor,
|
|
19300
|
+
outputCap: "temperature-sensor",
|
|
19301
|
+
outputField: "celsius",
|
|
19302
|
+
quantity: "temperature",
|
|
19303
|
+
defaultAggregate: "mean",
|
|
19304
|
+
empty: "null",
|
|
19305
|
+
companions: [NOW$2]
|
|
19306
|
+
}),
|
|
19307
|
+
numeric({
|
|
19308
|
+
id: "humidity",
|
|
19309
|
+
summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
19310
|
+
role: DeviceRole.HumiditySensor,
|
|
19311
|
+
outputCap: "humidity-sensor",
|
|
19312
|
+
outputField: "percent",
|
|
19313
|
+
quantity: "humidity",
|
|
19314
|
+
defaultAggregate: "mean",
|
|
19315
|
+
empty: "null",
|
|
19316
|
+
companions: [NOW$2]
|
|
19317
|
+
}),
|
|
19318
|
+
numeric({
|
|
19319
|
+
id: "pressure",
|
|
19320
|
+
summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
|
|
19321
|
+
role: DeviceRole.PressureSensor,
|
|
19322
|
+
outputCap: "pressure-sensor",
|
|
19323
|
+
outputField: "hpa",
|
|
19324
|
+
quantity: "pressure",
|
|
19325
|
+
defaultAggregate: "mean",
|
|
19326
|
+
empty: "null",
|
|
19327
|
+
companions: [NOW$2]
|
|
19328
|
+
}),
|
|
19329
|
+
numeric({
|
|
19330
|
+
id: "illuminance",
|
|
19331
|
+
summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
|
|
19332
|
+
role: DeviceRole.AmbientLightSensor,
|
|
19333
|
+
outputCap: "ambient-light-sensor",
|
|
19334
|
+
outputField: "lux",
|
|
19335
|
+
quantity: "illuminance",
|
|
19336
|
+
defaultAggregate: "mean",
|
|
19337
|
+
empty: "null",
|
|
19338
|
+
companions: [NOW$2]
|
|
19339
|
+
}),
|
|
19340
|
+
numeric({
|
|
19341
|
+
id: "power",
|
|
19342
|
+
summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
|
|
19343
|
+
role: DeviceRole.PowerSensor,
|
|
19344
|
+
outputCap: "power-meter",
|
|
19345
|
+
outputField: "watts",
|
|
19346
|
+
quantity: "power",
|
|
19347
|
+
defaultAggregate: "sum",
|
|
19348
|
+
empty: "omit",
|
|
19349
|
+
companions: [NOW$2]
|
|
19350
|
+
}),
|
|
19351
|
+
numeric({
|
|
19352
|
+
id: "energy",
|
|
19353
|
+
summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
|
|
19354
|
+
role: DeviceRole.EnergySensor,
|
|
19355
|
+
outputCap: "power-meter",
|
|
19356
|
+
outputField: "kwhTotal",
|
|
19357
|
+
quantity: "energy",
|
|
19358
|
+
defaultAggregate: "sum",
|
|
19359
|
+
empty: "omit",
|
|
19360
|
+
companions: [NOW$2]
|
|
19361
|
+
}),
|
|
19362
|
+
numeric({
|
|
19363
|
+
id: "battery",
|
|
19364
|
+
summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
19365
|
+
role: DeviceRole.BatterySensor,
|
|
19366
|
+
outputCap: "battery",
|
|
19367
|
+
outputField: "percentage",
|
|
19368
|
+
quantity: "percentage",
|
|
19369
|
+
defaultAggregate: "mean",
|
|
19370
|
+
empty: "null",
|
|
19371
|
+
companions: [
|
|
19372
|
+
{
|
|
19373
|
+
fieldPath: "charging",
|
|
19374
|
+
expr: "\"unknown\""
|
|
19375
|
+
},
|
|
19376
|
+
{
|
|
19377
|
+
fieldPath: "sleeping",
|
|
19378
|
+
expr: "false"
|
|
19379
|
+
},
|
|
19380
|
+
{
|
|
19381
|
+
fieldPath: "lastUpdated",
|
|
19382
|
+
expr: "now"
|
|
19383
|
+
}
|
|
19384
|
+
]
|
|
19385
|
+
}),
|
|
19386
|
+
numeric({
|
|
19387
|
+
id: "numeric",
|
|
19388
|
+
summary: "A numeric reading in the members’ shared quantity. Mixed quantities are refused. Nobody reporting publishes null, not 0.",
|
|
19389
|
+
role: DeviceRole.NumericSensor,
|
|
19390
|
+
outputCap: "numeric-sensor",
|
|
19391
|
+
outputField: "value",
|
|
19392
|
+
quantity: "device",
|
|
19393
|
+
defaultAggregate: "mean",
|
|
19394
|
+
empty: "null",
|
|
19395
|
+
companions: [NOW$2]
|
|
19396
|
+
}),
|
|
19397
|
+
binary({
|
|
19398
|
+
id: "motion",
|
|
19399
|
+
summary: "Any or all motion. Offline and unknown members are left out, never read as off. Nobody reporting takes the device offline.",
|
|
19400
|
+
role: DeviceRole.MotionSensor,
|
|
19401
|
+
outputCap: "motion",
|
|
19402
|
+
outputField: "detected",
|
|
19403
|
+
defaultAggregate: "any",
|
|
19404
|
+
accept: [{
|
|
19405
|
+
cap: "motion",
|
|
19406
|
+
fieldPath: "detected"
|
|
19407
|
+
}],
|
|
19408
|
+
companions: [{
|
|
19409
|
+
fieldPath: "lastDetectedAt",
|
|
19410
|
+
expr: "null"
|
|
19411
|
+
}, {
|
|
19412
|
+
fieldPath: "autoClearAfterMs",
|
|
19413
|
+
expr: "null"
|
|
19414
|
+
}]
|
|
19415
|
+
}),
|
|
19416
|
+
binary({
|
|
19417
|
+
id: "contact",
|
|
19418
|
+
summary: "Any or all open contacts. Offline and unknown members are left out, never read as closed.",
|
|
19419
|
+
role: DeviceRole.ContactSensor,
|
|
19420
|
+
outputCap: "contact",
|
|
19421
|
+
outputField: "entryOpen",
|
|
19422
|
+
defaultAggregate: "any",
|
|
19423
|
+
accept: [{
|
|
19424
|
+
cap: "contact",
|
|
19425
|
+
fieldPath: "entryOpen"
|
|
19426
|
+
}],
|
|
19427
|
+
companions: [CHANGED]
|
|
19428
|
+
}),
|
|
19429
|
+
binary({
|
|
19430
|
+
id: "leak",
|
|
19431
|
+
summary: "Any or all leak detections. Offline and unknown members are left out, never read as dry.",
|
|
19432
|
+
role: DeviceRole.LeakSensor,
|
|
19433
|
+
outputCap: "flood",
|
|
19434
|
+
outputField: "flooded",
|
|
19435
|
+
defaultAggregate: "any",
|
|
19436
|
+
accept: [{
|
|
19437
|
+
cap: "flood",
|
|
19438
|
+
fieldPath: "flooded"
|
|
19439
|
+
}],
|
|
19440
|
+
companions: [CHANGED]
|
|
19441
|
+
}),
|
|
19442
|
+
binary({
|
|
19443
|
+
id: "smoke",
|
|
19444
|
+
summary: "Any or all smoke detections. Offline and unknown members are left out, never read as clear.",
|
|
19445
|
+
role: DeviceRole.SmokeSensor,
|
|
19446
|
+
outputCap: "smoke",
|
|
19447
|
+
outputField: "detected",
|
|
19448
|
+
defaultAggregate: "any",
|
|
19449
|
+
accept: [{
|
|
19450
|
+
cap: "smoke",
|
|
19451
|
+
fieldPath: "detected"
|
|
19452
|
+
}],
|
|
19453
|
+
companions: [CHANGED]
|
|
19454
|
+
}),
|
|
19455
|
+
binary({
|
|
19456
|
+
id: "occupancy",
|
|
19457
|
+
summary: "Any or all of motion or binary sensors, published as a binary. Presence zones are not aggregated. Nobody reporting is offline, not off.",
|
|
19458
|
+
role: DeviceRole.BinarySensor,
|
|
19459
|
+
outputCap: "binary",
|
|
19460
|
+
outputField: "on",
|
|
19461
|
+
defaultAggregate: "any",
|
|
19462
|
+
accept: [{
|
|
19463
|
+
cap: "motion",
|
|
19464
|
+
fieldPath: "detected"
|
|
19465
|
+
}, {
|
|
19466
|
+
cap: "binary",
|
|
19467
|
+
fieldPath: "on"
|
|
19468
|
+
}],
|
|
19469
|
+
companions: [CHANGED]
|
|
19470
|
+
}),
|
|
19471
|
+
binary({
|
|
19472
|
+
id: "binary",
|
|
19473
|
+
summary: "Any or all generic binary sensors. Offline and unknown members are left out, never read as off.",
|
|
19474
|
+
role: DeviceRole.BinarySensor,
|
|
19475
|
+
outputCap: "binary",
|
|
19476
|
+
outputField: "on",
|
|
19477
|
+
defaultAggregate: "any",
|
|
19478
|
+
accept: [{
|
|
19479
|
+
cap: "binary",
|
|
19480
|
+
fieldPath: "on"
|
|
19481
|
+
}],
|
|
19482
|
+
companions: [CHANGED]
|
|
19483
|
+
}),
|
|
19484
|
+
actuator({
|
|
19485
|
+
id: "switch",
|
|
19486
|
+
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.",
|
|
19487
|
+
deviceType: DeviceType.Switch,
|
|
19488
|
+
outputCap: "switch",
|
|
19489
|
+
outputField: "on",
|
|
19490
|
+
member: {
|
|
19491
|
+
cap: "switch",
|
|
19492
|
+
fieldPath: "on"
|
|
19493
|
+
},
|
|
19494
|
+
commandCaps: ["switch"]
|
|
19495
|
+
}),
|
|
19496
|
+
actuator({
|
|
19497
|
+
id: "light",
|
|
19498
|
+
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.",
|
|
19499
|
+
deviceType: DeviceType.Light,
|
|
19500
|
+
outputCap: "switch",
|
|
19501
|
+
outputField: "on",
|
|
19502
|
+
member: {
|
|
19503
|
+
cap: "switch",
|
|
19504
|
+
fieldPath: "on"
|
|
19505
|
+
},
|
|
19506
|
+
commandCaps: [
|
|
19507
|
+
"switch",
|
|
19508
|
+
"brightness",
|
|
19509
|
+
"color"
|
|
19510
|
+
]
|
|
19511
|
+
}),
|
|
19512
|
+
actuator({
|
|
19513
|
+
id: "fan",
|
|
19514
|
+
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.",
|
|
19515
|
+
deviceType: DeviceType.Fan,
|
|
19516
|
+
outputCap: "switch",
|
|
19517
|
+
outputField: "on",
|
|
19518
|
+
member: {
|
|
19519
|
+
cap: "switch",
|
|
19520
|
+
fieldPath: "on"
|
|
19521
|
+
},
|
|
19522
|
+
commandCaps: ["switch", "fan-control"]
|
|
19523
|
+
}),
|
|
19524
|
+
actuator({
|
|
19525
|
+
id: "cover",
|
|
19526
|
+
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.",
|
|
19527
|
+
deviceType: DeviceType.Cover,
|
|
19528
|
+
outputCap: "group-active",
|
|
19529
|
+
outputField: "active",
|
|
19530
|
+
member: {
|
|
19531
|
+
cap: "cover",
|
|
19532
|
+
fieldPath: "state"
|
|
19533
|
+
},
|
|
19534
|
+
activeWhen: [
|
|
19535
|
+
"open",
|
|
19536
|
+
"opening",
|
|
19537
|
+
"stopped"
|
|
19538
|
+
],
|
|
19539
|
+
commandCaps: ["cover"]
|
|
19540
|
+
}),
|
|
19541
|
+
actuator({
|
|
19542
|
+
id: "valve",
|
|
19543
|
+
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.",
|
|
19544
|
+
deviceType: DeviceType.Valve,
|
|
19545
|
+
outputCap: "group-active",
|
|
19546
|
+
outputField: "active",
|
|
19547
|
+
member: {
|
|
19548
|
+
cap: "valve",
|
|
19549
|
+
fieldPath: "state"
|
|
19550
|
+
},
|
|
19551
|
+
activeWhen: [
|
|
19552
|
+
"open",
|
|
19553
|
+
"opening",
|
|
19554
|
+
"stopped"
|
|
19555
|
+
],
|
|
19556
|
+
commandCaps: ["valve"]
|
|
19557
|
+
}),
|
|
19558
|
+
actuator({
|
|
19559
|
+
id: "lock",
|
|
19560
|
+
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.",
|
|
19561
|
+
deviceType: DeviceType.Lock,
|
|
19562
|
+
outputCap: "group-active",
|
|
19563
|
+
outputField: "active",
|
|
19564
|
+
member: {
|
|
19565
|
+
cap: "lock-control",
|
|
19566
|
+
fieldPath: "state"
|
|
19567
|
+
},
|
|
19568
|
+
activeWhen: ["locked"],
|
|
19569
|
+
commandCaps: ["lock-control"]
|
|
19570
|
+
}),
|
|
19571
|
+
actuator({
|
|
19572
|
+
id: "media-player",
|
|
19573
|
+
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.",
|
|
19574
|
+
deviceType: DeviceType.MediaPlayer,
|
|
19575
|
+
outputCap: "group-active",
|
|
19576
|
+
outputField: "active",
|
|
19577
|
+
member: {
|
|
19578
|
+
cap: "media-player",
|
|
19579
|
+
fieldPath: "state"
|
|
19580
|
+
},
|
|
19581
|
+
activeWhen: ["playing", "buffering"],
|
|
19582
|
+
commandCaps: ["media-player"]
|
|
19583
|
+
}),
|
|
19584
|
+
actuator({
|
|
19585
|
+
id: "climate",
|
|
19586
|
+
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.",
|
|
19587
|
+
deviceType: DeviceType.Thermostat,
|
|
19588
|
+
memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
|
|
19589
|
+
outputCap: "group-active",
|
|
19590
|
+
outputField: "active",
|
|
19591
|
+
member: {
|
|
19592
|
+
cap: "climate-control",
|
|
19593
|
+
fieldPath: "mode"
|
|
19594
|
+
},
|
|
19595
|
+
activeWhen: [
|
|
19596
|
+
"heat",
|
|
19597
|
+
"cool",
|
|
19598
|
+
"auto",
|
|
19599
|
+
"heat_cool",
|
|
19600
|
+
"fan_only",
|
|
19601
|
+
"dry"
|
|
19602
|
+
],
|
|
19603
|
+
commandCaps: ["climate-control"]
|
|
19604
|
+
})
|
|
19605
|
+
];
|
|
19606
|
+
function actuator(input) {
|
|
19607
|
+
return {
|
|
19608
|
+
id: input.id,
|
|
19609
|
+
summary: input.summary,
|
|
19610
|
+
deviceType: input.deviceType,
|
|
19611
|
+
outputCap: input.outputCap,
|
|
19612
|
+
outputField: input.outputField,
|
|
19613
|
+
commandCaps: input.commandCaps,
|
|
19614
|
+
family: "actuator",
|
|
19615
|
+
role: void 0,
|
|
19616
|
+
memberDeviceType: input.deviceType,
|
|
19617
|
+
memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
|
|
19618
|
+
activeWhen: input.activeWhen,
|
|
19619
|
+
quantity: "none",
|
|
19620
|
+
baseUnit: void 0,
|
|
19621
|
+
aggregates: BINARY_AGGREGATES,
|
|
19622
|
+
defaultAggregate: "any",
|
|
19623
|
+
empty: "offline",
|
|
19624
|
+
accept: [input.member],
|
|
19625
|
+
companions: [CHANGED]
|
|
19626
|
+
};
|
|
19627
|
+
}
|
|
19628
|
+
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
19629
|
+
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
19630
|
+
DeviceType.Camera;
|
|
18905
19631
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
18906
19632
|
/**
|
|
18907
19633
|
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
@@ -20615,12 +21341,12 @@ method(object({ deviceId: number().int().nonnegative() }), DeviceAdminLinkSchema
|
|
|
20615
21341
|
* providers detect renames by matching `uniqueId` and patch `id`
|
|
20616
21342
|
* in-place so dispatch keeps working.
|
|
20617
21343
|
*
|
|
20618
|
-
* **
|
|
20619
|
-
*
|
|
20620
|
-
*
|
|
20621
|
-
*
|
|
20622
|
-
* (`ha-domain-mapping`)
|
|
20623
|
-
* stored after that point.
|
|
21344
|
+
* **Display unit and precision are NOT stored here.** They are a read-time
|
|
21345
|
+
* preference (D944). A polymorphic reading stores its `quantity` here once,
|
|
21346
|
+
* at adopt, and a date/time value stores `dateTimeShape` the same way —
|
|
21347
|
+
* neither is repeated on each sample. The `device_class` tag is an
|
|
21348
|
+
* HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
|
|
21349
|
+
* to derive the CamStack `DeviceRole`; it is never stored after that point.
|
|
20624
21350
|
*
|
|
20625
21351
|
* The `raw` escape hatch carries provider-specific attributes that don't
|
|
20626
21352
|
* fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
|
|
@@ -20634,6 +21360,14 @@ var SourceInfoSchema = object({
|
|
|
20634
21360
|
/** Immutable upstream identifier when available (HA `unique_id`, … ).
|
|
20635
21361
|
* Used to detect rename when `id` changes. */
|
|
20636
21362
|
uniqueId: string().optional(),
|
|
21363
|
+
/** Quantity of a polymorphic reading, written once at adopt (D944). */
|
|
21364
|
+
quantity: ReadingQuantitySchema.optional(),
|
|
21365
|
+
/** Date / time / datetime shape of a string reading, written once at adopt. */
|
|
21366
|
+
dateTimeShape: _enum([
|
|
21367
|
+
"date",
|
|
21368
|
+
"time",
|
|
21369
|
+
"datetime"
|
|
21370
|
+
]).optional(),
|
|
20637
21371
|
/** Provider-specific extras that don't fit the structured slots.
|
|
20638
21372
|
* Whitelist what goes here per provider — do NOT dump entire HA
|
|
20639
21373
|
* attribute blobs (would explode the meta JSON column). */
|
|
@@ -41755,7 +42489,8 @@ method(object({
|
|
|
41755
42489
|
"enum"
|
|
41756
42490
|
]),
|
|
41757
42491
|
enumValues: array(string()).optional(),
|
|
41758
|
-
item: boolean().optional()
|
|
42492
|
+
item: boolean().optional(),
|
|
42493
|
+
quantity: ReadingQuantitySchema.optional()
|
|
41759
42494
|
})).readonly(),
|
|
41760
42495
|
itemArray: object({
|
|
41761
42496
|
path: string(),
|
|
@@ -44116,7 +44851,7 @@ var NetworkAccessStatusSchema = object({
|
|
|
44116
44851
|
});
|
|
44117
44852
|
/**
|
|
44118
44853
|
* Optional, richer endpoint shape returned by providers that expose
|
|
44119
|
-
* MORE than one ingress concurrently (Tailscale
|
|
44854
|
+
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
44120
44855
|
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
44121
44856
|
* the originating provider config (mode + sourcePort) so the
|
|
44122
44857
|
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
@@ -45599,8 +46334,8 @@ var MaskPolygonShapeSchema = object({
|
|
|
45599
46334
|
/** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
|
|
45600
46335
|
var MaskGridShapeSchema = object({
|
|
45601
46336
|
kind: literal("grid"),
|
|
45602
|
-
gridWidth: number(),
|
|
45603
|
-
gridHeight: number(),
|
|
46337
|
+
gridWidth: number().meta(readingMeta("none")),
|
|
46338
|
+
gridHeight: number().meta(readingMeta("none")),
|
|
45604
46339
|
cells: array(boolean())
|
|
45605
46340
|
});
|
|
45606
46341
|
discriminatedUnion("kind", [
|
|
@@ -45712,6 +46447,8 @@ var NcSystemEventKindSchema = _enum([
|
|
|
45712
46447
|
"detection-blind",
|
|
45713
46448
|
"motion-noisy",
|
|
45714
46449
|
"motion-calm",
|
|
46450
|
+
"package-llm-silent",
|
|
46451
|
+
"package-llm-answered",
|
|
45715
46452
|
"recording-stalled",
|
|
45716
46453
|
"recording-healthy",
|
|
45717
46454
|
"recording-degraded",
|
|
@@ -46801,7 +47538,20 @@ var NcRuleInputSchema = object({
|
|
|
46801
47538
|
* save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
|
|
46802
47539
|
* path does not run Zod defaults.
|
|
46803
47540
|
*/
|
|
46804
|
-
groupingSec: number().int().min(5).max(60).optional()
|
|
47541
|
+
groupingSec: number().int().min(5).max(60).optional(),
|
|
47542
|
+
/**
|
|
47543
|
+
* ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
|
|
47544
|
+
* carries "Correct" / "Wrong" buttons (and, where the channel has room, a
|
|
47545
|
+
* "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
|
|
47546
|
+
* same mark the Library writes, and records whose notification it was and
|
|
47547
|
+
* when. A channel with no buttons gets the same two choices as links.
|
|
47548
|
+
*
|
|
47549
|
+
* `.optional()`, never `.default()` (a Zod default does not run on the addon
|
|
47550
|
+
* cap path): absent and `false` both mean off, which is what every rule
|
|
47551
|
+
* authored before this field meant. An editor turns it off by writing
|
|
47552
|
+
* `false`, so it needs no `clear` key.
|
|
47553
|
+
*/
|
|
47554
|
+
askVerdict: boolean().optional()
|
|
46805
47555
|
});
|
|
46806
47556
|
/**
|
|
46807
47557
|
* Partial patch for `updateRule` — any subset of the input fields, plus the
|
|
@@ -56803,6 +57553,28 @@ var PackageVersionInfoSchema = object({
|
|
|
56803
57553
|
deprecated: string().optional(),
|
|
56804
57554
|
distTags: array(string()).readonly()
|
|
56805
57555
|
});
|
|
57556
|
+
/**
|
|
57557
|
+
* One published document. `ok` is markdown from the registry. `missing` means
|
|
57558
|
+
* the registry answered and the file was not there. `error` is a registry or
|
|
57559
|
+
* tarball failure — never an empty string standing in for either.
|
|
57560
|
+
*/
|
|
57561
|
+
var PackageDocBodySchema = discriminatedUnion("status", [
|
|
57562
|
+
object({
|
|
57563
|
+
status: literal("ok"),
|
|
57564
|
+
markdown: string()
|
|
57565
|
+
}),
|
|
57566
|
+
object({ status: literal("missing") }),
|
|
57567
|
+
object({
|
|
57568
|
+
status: literal("error"),
|
|
57569
|
+
message: string()
|
|
57570
|
+
})
|
|
57571
|
+
]);
|
|
57572
|
+
var PackageDocsSchema = object({
|
|
57573
|
+
name: string(),
|
|
57574
|
+
version: string(),
|
|
57575
|
+
readme: PackageDocBodySchema,
|
|
57576
|
+
changelog: PackageDocBodySchema
|
|
57577
|
+
});
|
|
56806
57578
|
var SearchResultSchema = object({
|
|
56807
57579
|
name: string(),
|
|
56808
57580
|
version: string(),
|
|
@@ -57021,7 +57793,10 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
57021
57793
|
}), object({ success: literal(true) }), {
|
|
57022
57794
|
kind: "mutation",
|
|
57023
57795
|
auth: "admin"
|
|
57024
|
-
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
|
|
57796
|
+
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
|
|
57797
|
+
name: string().min(1),
|
|
57798
|
+
version: string().min(1).optional()
|
|
57799
|
+
}), PackageDocsSchema, { auth: "admin" }), method(object({ addonId: string() }), RestartAddonResultSchema, {
|
|
57025
57800
|
kind: "mutation",
|
|
57026
57801
|
auth: "admin"
|
|
57027
57802
|
}), method(object({ packageName: string() }), object({ success: literal(true) }), {
|
|
@@ -57080,27 +57855,17 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
57080
57855
|
*/
|
|
57081
57856
|
var AirQualitySensorStatusSchema = object({
|
|
57082
57857
|
/** Carbon dioxide concentration in ppm. */
|
|
57083
|
-
co2Ppm: number().min(0).optional(),
|
|
57858
|
+
co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
57084
57859
|
/** Total volatile organic compounds in ppb. */
|
|
57085
|
-
vocPpb: number().min(0).optional(),
|
|
57860
|
+
vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
57086
57861
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
57087
|
-
pm25: number().min(0).optional(),
|
|
57862
|
+
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
57088
57863
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
57089
|
-
pm10: number().min(0).optional(),
|
|
57864
|
+
pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
57090
57865
|
/** Composite AQI value (typically 0..500). */
|
|
57091
|
-
aqi: number().optional(),
|
|
57866
|
+
aqi: number().optional().meta(readingMeta("index")),
|
|
57092
57867
|
/** Ms epoch when the slice was last updated. */
|
|
57093
|
-
lastFetchedAt: number()
|
|
57094
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
57095
|
-
* `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
|
|
57096
|
-
* upstream `sensor.*` entity surfaces ONE device_class, so one unit
|
|
57097
|
-
* per slice is unambiguous. */
|
|
57098
|
-
unit: string().optional(),
|
|
57099
|
-
/** Suggested decimal places for numeric display.
|
|
57100
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
57101
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
57102
|
-
* auto-formatting when absent. */
|
|
57103
|
-
precision: number().int().min(0).max(10).optional()
|
|
57868
|
+
lastFetchedAt: number()
|
|
57104
57869
|
});
|
|
57105
57870
|
var airQualitySensorCapability = {
|
|
57106
57871
|
name: "air-quality-sensor",
|
|
@@ -57137,12 +57902,12 @@ var airQualitySensorCapability = {
|
|
|
57137
57902
|
},
|
|
57138
57903
|
pm25: {
|
|
57139
57904
|
kind: "gauge",
|
|
57140
|
-
unit: "
|
|
57905
|
+
unit: "µg/m³",
|
|
57141
57906
|
deadband: 1
|
|
57142
57907
|
},
|
|
57143
57908
|
pm10: {
|
|
57144
57909
|
kind: "gauge",
|
|
57145
|
-
unit: "
|
|
57910
|
+
unit: "µg/m³",
|
|
57146
57911
|
deadband: 1
|
|
57147
57912
|
},
|
|
57148
57913
|
aqi: {
|
|
@@ -57160,22 +57925,13 @@ var airQualitySensorCapability = {
|
|
|
57160
57925
|
* entries with `device_class: illuminance`.
|
|
57161
57926
|
*/
|
|
57162
57927
|
var AmbientLightSensorStatusSchema = object({
|
|
57163
|
-
/** Current illuminance in lux (lx). */
|
|
57164
|
-
lux: number().min(0).meta({
|
|
57165
|
-
|
|
57928
|
+
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
57929
|
+
lux: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
57930
|
+
precision: 0,
|
|
57166
57931
|
label: "Illuminance"
|
|
57167
|
-
}),
|
|
57932
|
+
})),
|
|
57168
57933
|
/** Ms epoch when the slice was last updated. */
|
|
57169
|
-
lastFetchedAt: number()
|
|
57170
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
57171
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
57172
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
57173
|
-
unit: string().optional(),
|
|
57174
|
-
/** Suggested decimal places for numeric display.
|
|
57175
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
57176
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
57177
|
-
* auto-formatting when absent. */
|
|
57178
|
-
precision: number().int().min(0).max(10).optional()
|
|
57934
|
+
lastFetchedAt: number()
|
|
57179
57935
|
});
|
|
57180
57936
|
var ambientLightSensorCapability = {
|
|
57181
57937
|
name: "ambient-light-sensor",
|
|
@@ -57207,12 +57963,7 @@ var ambientLightSensorCapability = {
|
|
|
57207
57963
|
} } },
|
|
57208
57964
|
/** Clock fields: written, but excluded from the compare that decides
|
|
57209
57965
|
* whether persisting is worth a SQLite commit. */
|
|
57210
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
57211
|
-
customize: {
|
|
57212
|
-
version: 1,
|
|
57213
|
-
advanced: "derive",
|
|
57214
|
-
targets: []
|
|
57215
|
-
}
|
|
57966
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
57216
57967
|
};
|
|
57217
57968
|
/**
|
|
57218
57969
|
* Per-class audio metrics aggregated over a sliding window.
|
|
@@ -57439,10 +58190,7 @@ var BatteryStatusSchema = object({
|
|
|
57439
58190
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
57440
58191
|
* declines to band a non-finite reading, which is the correct shape.
|
|
57441
58192
|
*/
|
|
57442
|
-
percentage: number().min(0).max(100).nullable().meta({
|
|
57443
|
-
unit: "%",
|
|
57444
|
-
label: "Charge"
|
|
57445
|
-
}),
|
|
58193
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
57446
58194
|
/**
|
|
57447
58195
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
57448
58196
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -57469,7 +58217,14 @@ var BatteryStatusSchema = object({
|
|
|
57469
58217
|
*/
|
|
57470
58218
|
sleeping: boolean().meta({ label: "Sleeping" }),
|
|
57471
58219
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
57472
|
-
lastUpdated: number(),
|
|
58220
|
+
lastUpdated: number().meta(readingMeta("none")),
|
|
58221
|
+
/**
|
|
58222
|
+
* Battery voltage in volts, for a cell the firmware reports as voltage
|
|
58223
|
+
* rather than a percentage (Ecowitt capacitor packs). Absent on cameras
|
|
58224
|
+
* that only report charge. A second reading on this cap, not a second
|
|
58225
|
+
* `numeric-sensor` on the same device.
|
|
58226
|
+
*/
|
|
58227
|
+
volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
57473
58228
|
/**
|
|
57474
58229
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
57475
58230
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -57516,12 +58271,12 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
57516
58271
|
DeviceType.Switch,
|
|
57517
58272
|
DeviceType.PetFeeder,
|
|
57518
58273
|
DeviceType.Container,
|
|
57519
|
-
DeviceType.Thermostat
|
|
58274
|
+
DeviceType.Thermostat,
|
|
58275
|
+
DeviceType.EventEmitter
|
|
57520
58276
|
];
|
|
57521
58277
|
/** Generic Customize target for the percentage reading (D936). */
|
|
57522
58278
|
var batteryCustomize = {
|
|
57523
58279
|
version: 1,
|
|
57524
|
-
advanced: "derive",
|
|
57525
58280
|
targets: [{
|
|
57526
58281
|
id: "percentage",
|
|
57527
58282
|
cardinality: "scalar",
|
|
@@ -57530,7 +58285,7 @@ var batteryCustomize = {
|
|
|
57530
58285
|
stateField: "percentage"
|
|
57531
58286
|
},
|
|
57532
58287
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
57533
|
-
apply: "add
|
|
58288
|
+
apply: "add",
|
|
57534
58289
|
path: "percentage",
|
|
57535
58290
|
sources: [{
|
|
57536
58291
|
kind: "field",
|
|
@@ -57651,10 +58406,16 @@ onStatusChanged: { data: object({
|
|
|
57651
58406
|
/**
|
|
57652
58407
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
57653
58408
|
*/
|
|
57654
|
-
history: { fields: {
|
|
57655
|
-
|
|
57656
|
-
|
|
57657
|
-
|
|
58409
|
+
history: { fields: {
|
|
58410
|
+
percentage: {
|
|
58411
|
+
kind: "gauge",
|
|
58412
|
+
unit: "%"
|
|
58413
|
+
},
|
|
58414
|
+
volts: {
|
|
58415
|
+
kind: "gauge",
|
|
58416
|
+
unit: "V"
|
|
58417
|
+
}
|
|
58418
|
+
} },
|
|
57658
58419
|
customize: batteryCustomize,
|
|
57659
58420
|
/** Clock fields: written, but excluded from the compare that decides
|
|
57660
58421
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -57701,12 +58462,7 @@ var binaryCapability = {
|
|
|
57701
58462
|
history: { fields: { on: { kind: "boolean" } } },
|
|
57702
58463
|
/** Clock fields: written, but excluded from the compare that decides
|
|
57703
58464
|
* whether persisting is worth a SQLite commit. */
|
|
57704
|
-
volatileStateFields: ["lastChangedAt"]
|
|
57705
|
-
customize: {
|
|
57706
|
-
version: 1,
|
|
57707
|
-
advanced: "derive",
|
|
57708
|
-
targets: []
|
|
57709
|
-
}
|
|
58465
|
+
volatileStateFields: ["lastChangedAt"]
|
|
57710
58466
|
};
|
|
57711
58467
|
/**
|
|
57712
58468
|
* Dimmable-light brightness control. Co-exists with `switch` on the
|
|
@@ -57722,7 +58478,7 @@ var binaryCapability = {
|
|
|
57722
58478
|
*/
|
|
57723
58479
|
var BrightnessStatusSchema = object({
|
|
57724
58480
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
57725
|
-
percentage: number().min(0).max(100),
|
|
58481
|
+
percentage: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
57726
58482
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
57727
58483
|
lastChangedAt: number()
|
|
57728
58484
|
});
|
|
@@ -58176,21 +58932,21 @@ var ClimateControlStatusSchema = object({
|
|
|
58176
58932
|
/** Single setpoint in Celsius. Used by single-target modes
|
|
58177
58933
|
* (heat / cool / dry). Null when the device is in a range mode
|
|
58178
58934
|
* (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
|
|
58179
|
-
target: number().nullable(),
|
|
58935
|
+
target: number().nullable().meta(readingMeta("temperature")),
|
|
58180
58936
|
/** Upper bound of the dual setpoint in Celsius. Populated only
|
|
58181
58937
|
* in `heat_cool` mode. */
|
|
58182
|
-
targetHigh: number().nullable(),
|
|
58938
|
+
targetHigh: number().nullable().meta(readingMeta("temperature")),
|
|
58183
58939
|
/** Lower bound of the dual setpoint in Celsius. Populated only
|
|
58184
58940
|
* in `heat_cool` mode. */
|
|
58185
|
-
targetLow: number().nullable(),
|
|
58941
|
+
targetLow: number().nullable().meta(readingMeta("temperature")),
|
|
58186
58942
|
/** Target relative humidity (0..100). Null when the device has no
|
|
58187
58943
|
* humidity control. */
|
|
58188
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
58944
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
58189
58945
|
/** Current measured temperature in Celsius. Read-only — pushed by
|
|
58190
58946
|
* the device's internal sensor. */
|
|
58191
|
-
currentTemp: number().nullable(),
|
|
58947
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
58192
58948
|
/** Current measured relative humidity (0..100). Read-only. */
|
|
58193
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
58949
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
58194
58950
|
/** Vertical louver swing on/off. Null when the device has no vertical
|
|
58195
58951
|
* swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
|
|
58196
58952
|
swingVertical: boolean().nullable(),
|
|
@@ -58198,11 +58954,11 @@ var ClimateControlStatusSchema = object({
|
|
|
58198
58954
|
* horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
|
|
58199
58955
|
swingHorizontal: boolean().nullable(),
|
|
58200
58956
|
/** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
58201
|
-
minTemp: number().nullable().optional(),
|
|
58957
|
+
minTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
58202
58958
|
/** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
58203
|
-
maxTemp: number().nullable().optional(),
|
|
58959
|
+
maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
58204
58960
|
/** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
|
|
58205
|
-
tempStep: number().positive().nullable().optional(),
|
|
58961
|
+
tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
|
|
58206
58962
|
/** Ms epoch when the slice was last updated (push or command). */
|
|
58207
58963
|
lastFetchedAt: number()
|
|
58208
58964
|
});
|
|
@@ -58344,17 +59100,17 @@ var climateControlCapability = {
|
|
|
58344
59100
|
* modes to expose in the picker without a slice fetch.
|
|
58345
59101
|
*/
|
|
58346
59102
|
var RgbTripletSchema = object({
|
|
58347
|
-
r: number().int().min(0).max(255),
|
|
58348
|
-
g: number().int().min(0).max(255),
|
|
58349
|
-
b: number().int().min(0).max(255)
|
|
59103
|
+
r: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
59104
|
+
g: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
59105
|
+
b: number().int().min(0).max(255).meta(readingMeta("none"))
|
|
58350
59106
|
});
|
|
58351
59107
|
var HsvTripletSchema = object({
|
|
58352
59108
|
/** Hue in degrees, 0..360. */
|
|
58353
|
-
h: number().min(0).max(360),
|
|
59109
|
+
h: number().min(0).max(360).meta(readingMeta("none")),
|
|
58354
59110
|
/** Saturation as 0..100 inclusive. */
|
|
58355
|
-
s: number().min(0).max(100),
|
|
59111
|
+
s: number().min(0).max(100).meta(readingMeta("none")),
|
|
58356
59112
|
/** Value/brightness as 0..100 inclusive. */
|
|
58357
|
-
v: number().min(0).max(100)
|
|
59113
|
+
v: number().min(0).max(100).meta(readingMeta("none"))
|
|
58358
59114
|
});
|
|
58359
59115
|
var ColorInputSchema = discriminatedUnion("mode", [
|
|
58360
59116
|
object({
|
|
@@ -58383,7 +59139,7 @@ var ColorStatusSchema = object({
|
|
|
58383
59139
|
/** Populated when `mode === 'hsv'`. */
|
|
58384
59140
|
hsv: HsvTripletSchema.optional(),
|
|
58385
59141
|
/** Populated when `mode === 'mired'`. */
|
|
58386
|
-
mireds: number().int().optional(),
|
|
59142
|
+
mireds: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
58387
59143
|
/** Ms epoch of the last operator-driven change. */
|
|
58388
59144
|
lastChangedAt: number()
|
|
58389
59145
|
});
|
|
@@ -58586,18 +59342,12 @@ _enum([
|
|
|
58586
59342
|
"empty",
|
|
58587
59343
|
"full"
|
|
58588
59344
|
]);
|
|
58589
|
-
/**
|
|
58590
|
-
|
|
58591
|
-
|
|
58592
|
-
"days",
|
|
58593
|
-
"hours",
|
|
58594
|
-
"cycles"
|
|
58595
|
-
]);
|
|
58596
|
-
/** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
|
|
58597
|
-
* days). `value` null = unknown (D393). */
|
|
59345
|
+
/** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
|
|
59346
|
+
var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
|
|
59347
|
+
/** Remaining life. `value` null = unknown (D393). Duration is seconds. */
|
|
58598
59348
|
var ConsumableRemainingSchema = object({
|
|
58599
|
-
value: number().min(0).nullable(),
|
|
58600
|
-
|
|
59349
|
+
value: number().min(0).nullable().meta(readingMeta("device")),
|
|
59350
|
+
kind: ConsumableRemainingKindSchema
|
|
58601
59351
|
});
|
|
58602
59352
|
/** A single consumable item. Either a continuous `level` (remaining
|
|
58603
59353
|
* life %, or fill % for a `supply` / `waste` item) or a discrete `status`
|
|
@@ -58610,7 +59360,7 @@ var ConsumableItemSchema = object({
|
|
|
58610
59360
|
/** Display name. */
|
|
58611
59361
|
label: string().min(1),
|
|
58612
59362
|
/** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
|
|
58613
|
-
level: number().min(0).max(100).nullable(),
|
|
59363
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58614
59364
|
/** The provider's discrete verdict when known (binary mode). */
|
|
58615
59365
|
status: ConsumableStatusSchema.nullable(),
|
|
58616
59366
|
/** What the item is — decides whether low or full is bad. Absent = `wear`. */
|
|
@@ -58692,8 +59442,8 @@ var consumablesCustomize = {
|
|
|
58692
59442
|
transforms: [{ kind: "identity" }]
|
|
58693
59443
|
}],
|
|
58694
59444
|
literals: [{
|
|
58695
|
-
path: "remaining.
|
|
58696
|
-
values: [...
|
|
59445
|
+
path: "remaining.kind",
|
|
59446
|
+
values: [...ConsumableRemainingKindSchema.options]
|
|
58697
59447
|
}]
|
|
58698
59448
|
},
|
|
58699
59449
|
{
|
|
@@ -58822,7 +59572,6 @@ reset: method(object({
|
|
|
58822
59572
|
},
|
|
58823
59573
|
"remaining.value": {
|
|
58824
59574
|
kind: "gauge",
|
|
58825
|
-
unitFrom: "remaining.unit",
|
|
58826
59575
|
deadband: 1,
|
|
58827
59576
|
displayName: "Remaining"
|
|
58828
59577
|
}
|
|
@@ -58892,10 +59641,9 @@ var contactCapability = {
|
|
|
58892
59641
|
* (empty array for the other kinds). It lives in the slice because
|
|
58893
59642
|
* HA `select.*` entities may update their options at runtime.
|
|
58894
59643
|
*
|
|
58895
|
-
*
|
|
58896
|
-
*
|
|
58897
|
-
*
|
|
58898
|
-
* push — consistent with `options` and the sensor-unit approach.
|
|
59644
|
+
* Numeric `min` / `max` / `step` live in the slice and share the device
|
|
59645
|
+
* quantity (`SourceInfo.quantity`, D944). Display precision is a read-time
|
|
59646
|
+
* preference. A datetime control's shape is `SourceInfo.dateTimeShape`.
|
|
58899
59647
|
*/
|
|
58900
59648
|
var ControlStatusSchema = object({
|
|
58901
59649
|
kind: _enum([
|
|
@@ -58907,39 +59655,17 @@ var ControlStatusSchema = object({
|
|
|
58907
59655
|
/** Current value. Numeric kind → number; select/text/datetime → string.
|
|
58908
59656
|
* Datetime values are ISO 8601 strings (full timestamp, date-only,
|
|
58909
59657
|
* or time-only — HA accepts all three forms). */
|
|
58910
|
-
value: union([number(), string()]),
|
|
59658
|
+
value: union([number().meta(readingMeta("device")), string()]),
|
|
58911
59659
|
/** Acceptable values when `kind === 'select'`. Empty for other kinds. */
|
|
58912
59660
|
options: array(string()),
|
|
58913
59661
|
/** Ms epoch when the slice was last updated. */
|
|
58914
59662
|
lastChangedAt: number(),
|
|
58915
|
-
/**
|
|
58916
|
-
|
|
58917
|
-
|
|
58918
|
-
|
|
58919
|
-
/**
|
|
58920
|
-
|
|
58921
|
-
min: number().optional(),
|
|
58922
|
-
/** Maximum allowed value. Populated live from HA `attributes.max`.
|
|
58923
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58924
|
-
max: number().optional(),
|
|
58925
|
-
/** Step increment. Populated live from HA `attributes.step`.
|
|
58926
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58927
|
-
step: number().optional(),
|
|
58928
|
-
/** Suggested decimal places for numeric display. Populated live from HA
|
|
58929
|
-
* `attributes.suggested_display_precision`, or derived from `step` when the
|
|
58930
|
-
* HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
|
|
58931
|
-
* other kinds. Falls back to auto-formatting when absent. */
|
|
58932
|
-
precision: number().int().min(0).max(10).optional(),
|
|
58933
|
-
/** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
|
|
58934
|
-
* 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
|
|
58935
|
-
* (UI defaults to 'datetime'). Derived live from the device's HA domain
|
|
58936
|
-
* (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
|
|
58937
|
-
* `has_date`/`has_time` attributes on each push. */
|
|
58938
|
-
format: _enum([
|
|
58939
|
-
"date",
|
|
58940
|
-
"time",
|
|
58941
|
-
"datetime"
|
|
58942
|
-
]).optional()
|
|
59663
|
+
/** Minimum allowed value, in the device quantity's base unit. */
|
|
59664
|
+
min: number().optional().meta(readingMeta("device")),
|
|
59665
|
+
/** Maximum allowed value, in the device quantity's base unit. */
|
|
59666
|
+
max: number().optional().meta(readingMeta("device")),
|
|
59667
|
+
/** Step increment, in the device quantity's base unit. */
|
|
59668
|
+
step: number().optional().meta(readingMeta("device"))
|
|
58943
59669
|
});
|
|
58944
59670
|
var ControlSetValueInputSchema = discriminatedUnion("kind", [
|
|
58945
59671
|
object({
|
|
@@ -59005,9 +59731,9 @@ var CoverStatusSchema = object({
|
|
|
59005
59731
|
]),
|
|
59006
59732
|
/** 0 = fully closed, 100 = fully open. Null when the device has
|
|
59007
59733
|
* no intermediate position surface. */
|
|
59008
|
-
position: number().min(0).max(100).nullable(),
|
|
59734
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59009
59735
|
/** 0 = closed slats, 100 = open slats. Null when no tilt support. */
|
|
59010
|
-
tiltPosition: number().min(0).max(100).nullable(),
|
|
59736
|
+
tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59011
59737
|
/** Ms epoch when the slice was last updated. */
|
|
59012
59738
|
lastChangedAt: number()
|
|
59013
59739
|
});
|
|
@@ -59110,9 +59836,9 @@ var NormalizedRangeSchema$1 = object({
|
|
|
59110
59836
|
var DayNightStatusSchema = object({
|
|
59111
59837
|
mode: DayNightModeSchema,
|
|
59112
59838
|
/** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
|
|
59113
|
-
sensitivity: number().optional(),
|
|
59839
|
+
sensitivity: number().optional().meta(readingMeta("none")),
|
|
59114
59840
|
/** Delay before the IR-cut filter flips, in seconds. */
|
|
59115
|
-
switchDelaySec: number().optional(),
|
|
59841
|
+
switchDelaySec: number().optional().meta(readingMeta("duration")),
|
|
59116
59842
|
lastFetchedAt: number()
|
|
59117
59843
|
});
|
|
59118
59844
|
/**
|
|
@@ -59330,11 +60056,11 @@ var DishwasherStatusSchema = object({
|
|
|
59330
60056
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
59331
60057
|
availablePrograms: array(string()),
|
|
59332
60058
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
59333
|
-
remainingSeconds: number().int().nonnegative().nullable(),
|
|
60059
|
+
remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
59334
60060
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
59335
|
-
delayedStartSeconds: number().int().nonnegative().nullable(),
|
|
60061
|
+
delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
59336
60062
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
59337
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
60063
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59338
60064
|
/** True when the door is reported open; null when the door state is not known. */
|
|
59339
60065
|
doorOpen: boolean().nullable(),
|
|
59340
60066
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -59358,7 +60084,7 @@ var DishwasherStatusSchema = object({
|
|
|
59358
60084
|
/** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
|
|
59359
60085
|
machineCareDue: boolean().nullable(),
|
|
59360
60086
|
/** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
|
|
59361
|
-
machineCareRemainingRuns: number().int().nonnegative().nullable(),
|
|
60087
|
+
machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
|
|
59362
60088
|
/** Option states: null when the appliance does not expose the option or has not said. */
|
|
59363
60089
|
halfLoad: boolean().nullable(),
|
|
59364
60090
|
hygienePlus: boolean().nullable(),
|
|
@@ -59373,7 +60099,7 @@ var DishwasherStatusSchema = object({
|
|
|
59373
60099
|
/** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
|
|
59374
60100
|
activeEvents: array(string()),
|
|
59375
60101
|
/** Numeric error code when the appliance reports one; null otherwise. */
|
|
59376
|
-
errorCode: number().nullable(),
|
|
60102
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
59377
60103
|
/** Name of the raised alert-level event while the appliance is in error; null otherwise. */
|
|
59378
60104
|
errorLabel: string().nullable(),
|
|
59379
60105
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
@@ -59604,7 +60330,7 @@ var DoorbellStatusSchema = object({
|
|
|
59604
60330
|
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
59605
60331
|
lastPressedAt: number().nullable(),
|
|
59606
60332
|
/** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
|
|
59607
|
-
pressCountSinceStart: number()
|
|
60333
|
+
pressCountSinceStart: number().meta(readingMeta("count"))
|
|
59608
60334
|
});
|
|
59609
60335
|
var DoorbellPressEventSchema = object({
|
|
59610
60336
|
deviceId: number(),
|
|
@@ -59669,17 +60395,7 @@ onPressed: {
|
|
|
59669
60395
|
history: "none",
|
|
59670
60396
|
customize: doorbellCustomize
|
|
59671
60397
|
};
|
|
59672
|
-
|
|
59673
|
-
* Enum-state sensor — a string value picked from a finite option set.
|
|
59674
|
-
* Drives HA `sensor` entries with `state_class: enum` (HVAC action
|
|
59675
|
-
* states, weather conditions, contact-source channels, mode strings).
|
|
59676
|
-
* The option set is stable across observations; it is persisted in the
|
|
59677
|
-
* device config blob (via the control slice at adoption) and is NOT
|
|
59678
|
-
* stored in `sourceInfo`.
|
|
59679
|
-
*/
|
|
59680
|
-
/** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
|
|
59681
|
-
* enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
|
|
59682
|
-
var EnumSensorDateTimeFormatSchema = _enum([
|
|
60398
|
+
_enum([
|
|
59683
60399
|
"date",
|
|
59684
60400
|
"time",
|
|
59685
60401
|
"datetime"
|
|
@@ -59687,12 +60403,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
|
|
|
59687
60403
|
var EnumSensorStatusSchema = object({
|
|
59688
60404
|
value: string(),
|
|
59689
60405
|
/**
|
|
59690
|
-
* Set
|
|
59691
|
-
* as
|
|
59692
|
-
*
|
|
59693
|
-
* a time-only sensor → `'time'`.
|
|
60406
|
+
* Set when `value` is a timestamp rather than an enum state, so history
|
|
60407
|
+
* does not learn ISO strings as states. The display shape (date / time /
|
|
60408
|
+
* datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
|
|
59694
60409
|
*/
|
|
59695
|
-
|
|
60410
|
+
timestamp: boolean().optional(),
|
|
59696
60411
|
/** Ms epoch when the slice was last updated. */
|
|
59697
60412
|
lastFetchedAt: number()
|
|
59698
60413
|
});
|
|
@@ -59719,11 +60434,11 @@ var enumSensorCapability = {
|
|
|
59719
60434
|
* History (D850): the value as an `enum` series (an appliance's program or
|
|
59720
60435
|
* operation state, an HVAC action). The option set is per device, so the
|
|
59721
60436
|
* value list is LEARNED as values are first seen (bounded). A sensor with a
|
|
59722
|
-
* `
|
|
60437
|
+
* `timestamp` carries a clock, not a state, and is not recorded.
|
|
59723
60438
|
*/
|
|
59724
60439
|
history: { fields: { value: {
|
|
59725
60440
|
kind: "enum",
|
|
59726
|
-
skipWhenSet: "
|
|
60441
|
+
skipWhenSet: "timestamp"
|
|
59727
60442
|
} } },
|
|
59728
60443
|
/** Clock fields: written, but excluded from the compare that decides
|
|
59729
60444
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -59743,13 +60458,13 @@ var EventFireSchema = object({
|
|
|
59743
60458
|
deviceId: number(),
|
|
59744
60459
|
eventType: string(),
|
|
59745
60460
|
data: record(string(), unknown()).nullable(),
|
|
59746
|
-
timestamp: number(),
|
|
59747
|
-
seq: number()
|
|
60461
|
+
timestamp: number().meta(readingMeta("none")),
|
|
60462
|
+
seq: number().meta(readingMeta("count"))
|
|
59748
60463
|
});
|
|
59749
60464
|
var EventEmitterStatusSchema = object({
|
|
59750
60465
|
eventTypes: array(string()),
|
|
59751
60466
|
lastEvent: EventFireSchema.nullable(),
|
|
59752
|
-
eventCountSinceStart: number()
|
|
60467
|
+
eventCountSinceStart: number().meta(readingMeta("count"))
|
|
59753
60468
|
});
|
|
59754
60469
|
var eventEmitterCapability = {
|
|
59755
60470
|
name: "event-emitter",
|
|
@@ -60162,10 +60877,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
60162
60877
|
var FanControlStatusSchema = object({
|
|
60163
60878
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
60164
60879
|
* speed surface (single-speed fan). */
|
|
60165
|
-
percentage: number().min(0).max(100).nullable(),
|
|
60880
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60166
60881
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
60167
60882
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
60168
|
-
percentageStep: number().positive().optional(),
|
|
60883
|
+
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
60169
60884
|
/** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
|
|
60170
60885
|
* — empty string when no preset is active or supported. */
|
|
60171
60886
|
preset: string(),
|
|
@@ -60286,7 +61001,7 @@ var FeatureProbeStatusSchema = object({
|
|
|
60286
61001
|
/** Camera/firmware model string. `null` when the firmware doesn't expose it. */
|
|
60287
61002
|
model: string().nullable(),
|
|
60288
61003
|
/** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
|
|
60289
|
-
channelCount: number().nullable(),
|
|
61004
|
+
channelCount: number().nullable().meta(readingMeta("count")),
|
|
60290
61005
|
/**
|
|
60291
61006
|
* Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
|
|
60292
61007
|
* completes — drivers' `getAccessoryChildren()` should treat zero as
|
|
@@ -60409,9 +61124,151 @@ var gasCapability = {
|
|
|
60409
61124
|
* whether persisting is worth a SQLite commit. */
|
|
60410
61125
|
volatileStateFields: ["lastChangedAt"]
|
|
60411
61126
|
};
|
|
61127
|
+
var GroupCommandsSchema = object({
|
|
61128
|
+
/** One row per member that has been commanded. A later command replaces that member's row. */
|
|
61129
|
+
results: array(object({
|
|
61130
|
+
deviceId: number().int().nonnegative(),
|
|
61131
|
+
cap: string().min(1),
|
|
61132
|
+
method: string().min(1),
|
|
61133
|
+
ok: boolean(),
|
|
61134
|
+
error: string().optional(),
|
|
61135
|
+
/** Ms epoch of the attempt. */
|
|
61136
|
+
at: number()
|
|
61137
|
+
})) });
|
|
61138
|
+
var groupCommandsCapability = {
|
|
61139
|
+
name: "group-commands",
|
|
61140
|
+
scope: "device",
|
|
61141
|
+
deviceNative: true,
|
|
61142
|
+
mode: "singleton",
|
|
61143
|
+
deviceTypes: [
|
|
61144
|
+
DeviceType.Switch,
|
|
61145
|
+
DeviceType.Light,
|
|
61146
|
+
DeviceType.Fan,
|
|
61147
|
+
DeviceType.Cover,
|
|
61148
|
+
DeviceType.Valve,
|
|
61149
|
+
DeviceType.Lock,
|
|
61150
|
+
DeviceType.MediaPlayer,
|
|
61151
|
+
DeviceType.Thermostat,
|
|
61152
|
+
DeviceType.Climate
|
|
61153
|
+
],
|
|
61154
|
+
methods: {},
|
|
61155
|
+
status: {
|
|
61156
|
+
schema: GroupCommandsSchema,
|
|
61157
|
+
kind: "push"
|
|
61158
|
+
},
|
|
61159
|
+
runtimeState: GroupCommandsSchema,
|
|
61160
|
+
/**
|
|
61161
|
+
* Runtime-state durability: **session** — the last attempt. A restored
|
|
61162
|
+
* failure would outlive the command that caused it.
|
|
61163
|
+
*
|
|
61164
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
61165
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
61166
|
+
*/
|
|
61167
|
+
durability: "session",
|
|
61168
|
+
history: "none"
|
|
61169
|
+
};
|
|
61170
|
+
/**
|
|
61171
|
+
* Whether a helper actuator group counts as active (D944): at least one
|
|
61172
|
+
* included member is, or every included member is. What "active" means is the
|
|
61173
|
+
* type's: a cover is open, a lock is locked, a media player is playing, a
|
|
61174
|
+
* climate device is not off. A group of lights, switches and fans reports
|
|
61175
|
+
* `switch.on` instead; this cap is for the types whose own state is not a
|
|
61176
|
+
* yes/no. Unknown members are left out (`group-status` counts them) and an
|
|
61177
|
+
* empty included set takes the device offline, never `active: false`.
|
|
61178
|
+
*/
|
|
61179
|
+
var GroupActiveSchema = object({
|
|
61180
|
+
active: boolean().meta({ label: "Active" }),
|
|
61181
|
+
/** Ms epoch when the slice was last updated. */
|
|
61182
|
+
lastChangedAt: number()
|
|
61183
|
+
});
|
|
61184
|
+
var groupActiveCapability = {
|
|
61185
|
+
name: "group-active",
|
|
61186
|
+
scope: "device",
|
|
61187
|
+
deviceNative: true,
|
|
61188
|
+
mode: "singleton",
|
|
61189
|
+
deviceTypes: [
|
|
61190
|
+
DeviceType.Cover,
|
|
61191
|
+
DeviceType.Valve,
|
|
61192
|
+
DeviceType.Lock,
|
|
61193
|
+
DeviceType.MediaPlayer,
|
|
61194
|
+
DeviceType.Climate,
|
|
61195
|
+
DeviceType.Thermostat
|
|
61196
|
+
],
|
|
61197
|
+
methods: {},
|
|
61198
|
+
status: {
|
|
61199
|
+
schema: GroupActiveSchema,
|
|
61200
|
+
kind: "push"
|
|
61201
|
+
},
|
|
61202
|
+
runtimeState: GroupActiveSchema,
|
|
61203
|
+
/**
|
|
61204
|
+
* Runtime-state durability: **session** — recomputed from the members on the
|
|
61205
|
+
* next source event; a restored value would outlive the members it named.
|
|
61206
|
+
*
|
|
61207
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
61208
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
61209
|
+
*/
|
|
61210
|
+
durability: "session",
|
|
61211
|
+
history: "none"
|
|
61212
|
+
};
|
|
61213
|
+
/**
|
|
61214
|
+
* How many members of a helper group answered (D944). Counts are integers,
|
|
61215
|
+
* not measurements: zero included is a real count, never a stand-in reading.
|
|
61216
|
+
* The reading itself stays on the output capability. History is none — the
|
|
61217
|
+
* series that later statistics read is the output cap's own history block.
|
|
61218
|
+
*/
|
|
61219
|
+
var GroupStatusSchema = object({
|
|
61220
|
+
/** Members whose current value entered the aggregate. */
|
|
61221
|
+
included: number().int().nonnegative().meta(readingMeta("count", {
|
|
61222
|
+
precision: 0,
|
|
61223
|
+
label: "Reporting"
|
|
61224
|
+
})),
|
|
61225
|
+
/** Members left out: offline, unknown, or not a finite reading (D393). */
|
|
61226
|
+
excluded: number().int().nonnegative().meta(readingMeta("count", {
|
|
61227
|
+
precision: 0,
|
|
61228
|
+
label: "Excluded"
|
|
61229
|
+
})),
|
|
61230
|
+
/** `included` + `excluded`. */
|
|
61231
|
+
total: number().int().nonnegative().meta(readingMeta("count", {
|
|
61232
|
+
precision: 0,
|
|
61233
|
+
label: "Members"
|
|
61234
|
+
}))
|
|
61235
|
+
});
|
|
61236
|
+
var groupStatusCapability = {
|
|
61237
|
+
name: "group-status",
|
|
61238
|
+
scope: "device",
|
|
61239
|
+
deviceNative: true,
|
|
61240
|
+
mode: "singleton",
|
|
61241
|
+
deviceTypes: [
|
|
61242
|
+
DeviceType.Sensor,
|
|
61243
|
+
DeviceType.Switch,
|
|
61244
|
+
DeviceType.Light,
|
|
61245
|
+
DeviceType.Fan,
|
|
61246
|
+
DeviceType.Cover,
|
|
61247
|
+
DeviceType.Valve,
|
|
61248
|
+
DeviceType.Lock,
|
|
61249
|
+
DeviceType.MediaPlayer,
|
|
61250
|
+
DeviceType.Thermostat,
|
|
61251
|
+
DeviceType.Climate
|
|
61252
|
+
],
|
|
61253
|
+
methods: {},
|
|
61254
|
+
status: {
|
|
61255
|
+
schema: GroupStatusSchema,
|
|
61256
|
+
kind: "push"
|
|
61257
|
+
},
|
|
61258
|
+
runtimeState: GroupStatusSchema,
|
|
61259
|
+
/**
|
|
61260
|
+
* Runtime-state durability: **session** — recomputed from members on the
|
|
61261
|
+
* next source event; a restored count would outlive the readings it named.
|
|
61262
|
+
*
|
|
61263
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
61264
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
61265
|
+
*/
|
|
61266
|
+
durability: "session",
|
|
61267
|
+
history: "none"
|
|
61268
|
+
};
|
|
60412
61269
|
var HeatingValveStatusSchema = object({
|
|
60413
61270
|
/** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
|
|
60414
|
-
position: number().min(0).max(100).nullable(),
|
|
61271
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60415
61272
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
60416
61273
|
lastFetchedAt: number()
|
|
60417
61274
|
});
|
|
@@ -60454,9 +61311,9 @@ var HumidifierStatusSchema = object({
|
|
|
60454
61311
|
/** Whether the humidifier is currently on. */
|
|
60455
61312
|
on: boolean(),
|
|
60456
61313
|
/** Current measured relative humidity (0..100). Null when not reported. */
|
|
60457
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
61314
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
60458
61315
|
/** Target relative humidity (0..100). Null when no setpoint surface. */
|
|
60459
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
61316
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
60460
61317
|
/** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
|
|
60461
61318
|
* device has no mode surface. */
|
|
60462
61319
|
mode: string().nullable(),
|
|
@@ -60466,9 +61323,9 @@ var HumidifierStatusSchema = object({
|
|
|
60466
61323
|
* `idle` / `off`). Null when the device doesn't report it. */
|
|
60467
61324
|
action: string().nullable(),
|
|
60468
61325
|
/** HA `min_humidity` attribute. Null → UI uses 0. */
|
|
60469
|
-
minHumidity: number().nullable(),
|
|
61326
|
+
minHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
60470
61327
|
/** HA `max_humidity` attribute. Null → UI uses 100. */
|
|
60471
|
-
maxHumidity: number().nullable(),
|
|
61328
|
+
maxHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
60472
61329
|
/** Ms epoch when the slice was last updated. */
|
|
60473
61330
|
lastChangedAt: number()
|
|
60474
61331
|
});
|
|
@@ -60538,22 +61395,13 @@ var humidifierCapability = {
|
|
|
60538
61395
|
* `sourceInfo`.
|
|
60539
61396
|
*/
|
|
60540
61397
|
var HumiditySensorStatusSchema = object({
|
|
60541
|
-
/** Current relative humidity, 0..100. */
|
|
60542
|
-
percent: number().min(0).max(100).meta({
|
|
60543
|
-
|
|
61398
|
+
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
61399
|
+
percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
61400
|
+
precision: 0,
|
|
60544
61401
|
label: "Humidity"
|
|
60545
|
-
}),
|
|
61402
|
+
})),
|
|
60546
61403
|
/** Ms epoch when the slice was last updated. */
|
|
60547
|
-
lastFetchedAt: number()
|
|
60548
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
60549
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
60550
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
60551
|
-
unit: string().optional(),
|
|
60552
|
-
/** Suggested decimal places for numeric display.
|
|
60553
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
60554
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
60555
|
-
* auto-formatting when absent. */
|
|
60556
|
-
precision: number().int().min(0).max(10).optional()
|
|
61404
|
+
lastFetchedAt: number()
|
|
60557
61405
|
});
|
|
60558
61406
|
var humiditySensorCapability = {
|
|
60559
61407
|
name: "humidity-sensor",
|
|
@@ -60584,12 +61432,7 @@ var humiditySensorCapability = {
|
|
|
60584
61432
|
} } },
|
|
60585
61433
|
/** Clock fields: written, but excluded from the compare that decides
|
|
60586
61434
|
* whether persisting is worth a SQLite commit. */
|
|
60587
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
60588
|
-
customize: {
|
|
60589
|
-
version: 1,
|
|
60590
|
-
advanced: "derive",
|
|
60591
|
-
targets: []
|
|
60592
|
-
}
|
|
61435
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
60593
61436
|
};
|
|
60594
61437
|
/**
|
|
60595
61438
|
* Image display cap. Models a single still image exposed by an integration —
|
|
@@ -60610,7 +61453,7 @@ var ImageStatusSchema = object({
|
|
|
60610
61453
|
* entity exposes no `entity_picture` (yet). */
|
|
60611
61454
|
url: string().nullable(),
|
|
60612
61455
|
/** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
|
|
60613
|
-
lastUpdated: number().nullable()
|
|
61456
|
+
lastUpdated: number().nullable().meta(readingMeta("none"))
|
|
60614
61457
|
});
|
|
60615
61458
|
var imageCapability = {
|
|
60616
61459
|
name: "image",
|
|
@@ -60698,19 +61541,19 @@ var NormalizedRangeSchema = object({
|
|
|
60698
61541
|
*/
|
|
60699
61542
|
var ImageSettingsStatusSchema = object({
|
|
60700
61543
|
/** Normalized 0–100. */
|
|
60701
|
-
brightness: number().optional(),
|
|
61544
|
+
brightness: number().optional().meta(readingMeta("none")),
|
|
60702
61545
|
/** Normalized 0–100. */
|
|
60703
|
-
contrast: number().optional(),
|
|
61546
|
+
contrast: number().optional().meta(readingMeta("none")),
|
|
60704
61547
|
/** Normalized 0–100. */
|
|
60705
|
-
saturation: number().optional(),
|
|
61548
|
+
saturation: number().optional().meta(readingMeta("none")),
|
|
60706
61549
|
/** Normalized 0–100. */
|
|
60707
|
-
sharpness: number().optional(),
|
|
61550
|
+
sharpness: number().optional().meta(readingMeta("none")),
|
|
60708
61551
|
mirror: boolean().optional(),
|
|
60709
61552
|
flip: boolean().optional(),
|
|
60710
61553
|
rotate: ImageRotateSchema.optional(),
|
|
60711
61554
|
whiteBalance: WhiteBalanceModeSchema.optional(),
|
|
60712
61555
|
/** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
|
|
60713
|
-
warmth: number().optional(),
|
|
61556
|
+
warmth: number().optional().meta(readingMeta("none")),
|
|
60714
61557
|
exposureMode: ExposureModeSchema.optional(),
|
|
60715
61558
|
backlightMode: BacklightModeSchema.optional(),
|
|
60716
61559
|
lastFetchedAt: number()
|
|
@@ -60989,10 +61832,10 @@ var IntercomAbilitySchema = object({
|
|
|
60989
61832
|
/** Codecs accepted by firmware, in preference order. Driver-authored strings. */
|
|
60990
61833
|
codecs: array(string()),
|
|
60991
61834
|
/** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
|
|
60992
|
-
sampleRate: number(),
|
|
61835
|
+
sampleRate: number().meta(readingMeta("none")),
|
|
60993
61836
|
duplex: _enum(["half", "full"]),
|
|
60994
61837
|
/** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
|
|
60995
|
-
maxBacklogMs: number()
|
|
61838
|
+
maxBacklogMs: number().meta(readingMeta("duration-ms"))
|
|
60996
61839
|
});
|
|
60997
61840
|
var IntercomStatusSchema = object({
|
|
60998
61841
|
/** True while a talk session is open. */
|
|
@@ -61219,13 +62062,13 @@ var LawnMowerControlStatusSchema = object({
|
|
|
61219
62062
|
activity: LawnMowerActivitySchema,
|
|
61220
62063
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
61221
62064
|
* reading. */
|
|
61222
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
62065
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61223
62066
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
61224
62067
|
* task is active / progress is unavailable. */
|
|
61225
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
62068
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61226
62069
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
61227
62070
|
* or null when unknown. */
|
|
61228
|
-
currentCode: number().nullable(),
|
|
62071
|
+
currentCode: number().nullable().meta(readingMeta("none")),
|
|
61229
62072
|
/** Human label for {@link currentCode}, or null when undecodable. */
|
|
61230
62073
|
currentCodeLabel: string().nullable(),
|
|
61231
62074
|
/** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
|
|
@@ -61678,7 +62521,14 @@ var lockControlCapability = {
|
|
|
61678
62521
|
}),
|
|
61679
62522
|
unlock: method(object({
|
|
61680
62523
|
deviceId: number().int().nonnegative(),
|
|
61681
|
-
code: string().min(1).optional()
|
|
62524
|
+
code: string().min(1).optional(),
|
|
62525
|
+
/**
|
|
62526
|
+
* Honoured by a helper lock GROUP only (D944): unlocking several doors
|
|
62527
|
+
* with one call is refused unless the caller says it was confirmed. A
|
|
62528
|
+
* single lock ignores it. The refusal lives in the callee, so no
|
|
62529
|
+
* automation or API caller reaches the doors by skipping a dialog.
|
|
62530
|
+
*/
|
|
62531
|
+
confirm: boolean().optional()
|
|
61682
62532
|
}), _void(), {
|
|
61683
62533
|
kind: "mutation",
|
|
61684
62534
|
auth: "protected"
|
|
@@ -61715,6 +62565,44 @@ var lockControlCapability = {
|
|
|
61715
62565
|
volatileStateFields: ["lastChangedAt"]
|
|
61716
62566
|
};
|
|
61717
62567
|
/**
|
|
62568
|
+
* A monotonic total between resets (D944). Energy uses `power-meter.kwhTotal`
|
|
62569
|
+
* so dashboards keep one series; every other instantaneous reading lands here.
|
|
62570
|
+
* `value` is the time integral in the source quantity's accumulated base
|
|
62571
|
+
* (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
|
|
62572
|
+
*/
|
|
62573
|
+
var MeterStatusSchema = object({
|
|
62574
|
+
value: number().nullable().meta(readingMeta("device", { label: "Total" })),
|
|
62575
|
+
lastFetchedAt: number()
|
|
62576
|
+
});
|
|
62577
|
+
var meterCapability = {
|
|
62578
|
+
name: "meter",
|
|
62579
|
+
scope: "device",
|
|
62580
|
+
deviceNative: true,
|
|
62581
|
+
mode: "singleton",
|
|
62582
|
+
deviceTypes: [DeviceType.Sensor],
|
|
62583
|
+
methods: {},
|
|
62584
|
+
status: {
|
|
62585
|
+
schema: MeterStatusSchema,
|
|
62586
|
+
kind: "push"
|
|
62587
|
+
},
|
|
62588
|
+
runtimeState: MeterStatusSchema,
|
|
62589
|
+
/**
|
|
62590
|
+
* Runtime-state durability: **restored** — the total is a cache of the
|
|
62591
|
+
* history integral. A restart shows it only until the history pass
|
|
62592
|
+
* recomputes the open period; it is not an in-memory buffer.
|
|
62593
|
+
*
|
|
62594
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
62595
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
62596
|
+
*/
|
|
62597
|
+
durability: "restored",
|
|
62598
|
+
history: { fields: { value: {
|
|
62599
|
+
kind: "counter",
|
|
62600
|
+
deadband: .001,
|
|
62601
|
+
minIntervalMs: 6e4
|
|
62602
|
+
} } },
|
|
62603
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
62604
|
+
};
|
|
62605
|
+
/**
|
|
61718
62606
|
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
61719
62607
|
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
61720
62608
|
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
@@ -61758,7 +62646,7 @@ var MediaPlayerStatusSchema = object({
|
|
|
61758
62646
|
state: MediaPlayerStateSchema,
|
|
61759
62647
|
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
61760
62648
|
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
61761
|
-
volumeLevel: number().min(0).max(100).nullable(),
|
|
62649
|
+
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61762
62650
|
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
61763
62651
|
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
61764
62652
|
isMuted: boolean().nullable(),
|
|
@@ -61771,10 +62659,10 @@ var MediaPlayerStatusSchema = object({
|
|
|
61771
62659
|
currentMedia: MediaInfoSchema.nullable(),
|
|
61772
62660
|
/** Current playback position in ms. Null when not seekable or
|
|
61773
62661
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
61774
|
-
positionMs: number().int().nonnegative().nullable(),
|
|
62662
|
+
positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61775
62663
|
/** Total duration of the current media in ms. Null when unknown
|
|
61776
62664
|
* (live stream) or nothing is playing. */
|
|
61777
|
-
durationMs: number().int().nonnegative().nullable(),
|
|
62665
|
+
durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61778
62666
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
61779
62667
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
61780
62668
|
shuffle: boolean().nullable(),
|
|
@@ -62083,7 +62971,7 @@ var motionCustomize = {
|
|
|
62083
62971
|
eventName: "onMotionChanged"
|
|
62084
62972
|
},
|
|
62085
62973
|
deviceTypes: [DeviceType.Camera],
|
|
62086
|
-
apply: "
|
|
62974
|
+
apply: "add",
|
|
62087
62975
|
applyAuthority: {
|
|
62088
62976
|
kind: "camera-sensors",
|
|
62089
62977
|
timeoutField: "motionCooldownMs"
|
|
@@ -62155,7 +63043,7 @@ var MotionStatusSchema = object({
|
|
|
62155
63043
|
* it, every provider seeded it at boot, and every session left it
|
|
62156
63044
|
* `detected: true` after its detach.
|
|
62157
63045
|
*/
|
|
62158
|
-
autoClearAfterMs: number().nullable()
|
|
63046
|
+
autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
|
|
62159
63047
|
});
|
|
62160
63048
|
/**
|
|
62161
63049
|
* Payload of `motion.onMotionChanged` event + the corresponding bus
|
|
@@ -62192,12 +63080,18 @@ var motionCapability = {
|
|
|
62192
63080
|
methods: {
|
|
62193
63081
|
/**
|
|
62194
63082
|
* Pull the current motion state synchronously. Convenience shortcut
|
|
62195
|
-
* for consumers that don't need the full status object
|
|
62196
|
-
*
|
|
62197
|
-
* `getStatus` once the auto-injected status surface lands in every
|
|
62198
|
-
* consumer.
|
|
63083
|
+
* for consumers that don't need the full status object. A composed
|
|
63084
|
+
* group answers from its slice (D944): unknown throws, it is not off.
|
|
62199
63085
|
*/
|
|
62200
63086
|
isDetected: method(object({ deviceId: number() }), boolean()) },
|
|
63087
|
+
/**
|
|
63088
|
+
* A composed motion group serves `isDetected` from `detected` (D944).
|
|
63089
|
+
* Without this the cap stays uncomposable (`has-commands`).
|
|
63090
|
+
*/
|
|
63091
|
+
composedMethods: { isDetected: {
|
|
63092
|
+
kind: "slice",
|
|
63093
|
+
field: "detected"
|
|
63094
|
+
} },
|
|
62201
63095
|
events: {
|
|
62202
63096
|
/**
|
|
62203
63097
|
* Fires every time the runner transitions a camera between
|
|
@@ -62337,7 +63231,7 @@ var MotionZoneRegionSchema = object({
|
|
|
62337
63231
|
* the grid region(s). */
|
|
62338
63232
|
var MotionZoneStatusSchema = object({
|
|
62339
63233
|
enabled: boolean(),
|
|
62340
|
-
sensitivity: number(),
|
|
63234
|
+
sensitivity: number().meta(readingMeta("none")),
|
|
62341
63235
|
/** Grid region(s). Today exactly one `grid` shape. */
|
|
62342
63236
|
regions: array(MotionZoneRegionSchema),
|
|
62343
63237
|
lastFetchedAt: number()
|
|
@@ -62707,7 +63601,7 @@ var NavigationStatusSchema = object({
|
|
|
62707
63601
|
following: boolean(),
|
|
62708
63602
|
flash: boolean(),
|
|
62709
63603
|
lightMode: NavigationLightModeSchema,
|
|
62710
|
-
lightLevel: number().min(40).max(100),
|
|
63604
|
+
lightLevel: number().min(40).max(100).meta(readingMeta("none")),
|
|
62711
63605
|
/** Ms epoch when the slice was last updated. */
|
|
62712
63606
|
lastChangedAt: number()
|
|
62713
63607
|
});
|
|
@@ -62841,13 +63735,13 @@ var NetworkLinkStatusSchema = object({
|
|
|
62841
63735
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
62842
63736
|
* SKIP a null rather than coerce it.
|
|
62843
63737
|
*/
|
|
62844
|
-
signalPercent: number().min(0).max(100).nullable(),
|
|
63738
|
+
signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62845
63739
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
62846
|
-
rssiDbm: number().optional(),
|
|
63740
|
+
rssiDbm: number().optional().meta(readingMeta("signal-level")),
|
|
62847
63741
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
62848
63742
|
ssid: string().optional(),
|
|
62849
63743
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
62850
|
-
lastUpdated: number()
|
|
63744
|
+
lastUpdated: number().meta(readingMeta("none"))
|
|
62851
63745
|
});
|
|
62852
63746
|
var networkLinkCapability = {
|
|
62853
63747
|
name: "network-link",
|
|
@@ -63232,7 +64126,7 @@ var NotifierStatusSchema = object({
|
|
|
63232
64126
|
lastError: string().nullable(),
|
|
63233
64127
|
/** Number of deliveries currently buffered server-side (rate-limited
|
|
63234
64128
|
* notifiers may queue). 0 when the provider sends synchronously. */
|
|
63235
|
-
queueDepth: number().int().nonnegative(),
|
|
64129
|
+
queueDepth: number().int().nonnegative().meta(readingMeta("count")),
|
|
63236
64130
|
/** Per-feature capability matrix — authoritative source for the
|
|
63237
64131
|
* compose-form field gating. */
|
|
63238
64132
|
supports: NotifierSupportsSchema
|
|
@@ -63324,11 +64218,9 @@ onSent: { data: object({
|
|
|
63324
64218
|
* cap fits (HA `sensor` whose `device_class` we don't have a typed cap
|
|
63325
64219
|
* for: water flow, distance, weight, frequency, signal strength, …).
|
|
63326
64220
|
*
|
|
63327
|
-
* The
|
|
63328
|
-
*
|
|
63329
|
-
*
|
|
63330
|
-
* The UI reads them directly from the slice — they are the single source
|
|
63331
|
-
* of truth for generic numeric rendering and do NOT live on `sourceInfo`.
|
|
64221
|
+
* The quantity is not on this slice. It is chosen when the device is
|
|
64222
|
+
* adopted and stored once on `SourceInfo.quantity` (D944). `value` is in
|
|
64223
|
+
* that quantity's base unit. `null` is a refused sample (D393).
|
|
63332
64224
|
*
|
|
63333
64225
|
* Prefer the typed alternatives (`temperature-sensor`, `humidity-sensor`,
|
|
63334
64226
|
* `ambient-light-sensor`, `pressure-sensor`, `power-meter`,
|
|
@@ -63337,15 +64229,8 @@ onSent: { data: object({
|
|
|
63337
64229
|
* numeric values land here.
|
|
63338
64230
|
*/
|
|
63339
64231
|
var NumericSensorStatusSchema = object({
|
|
63340
|
-
|
|
63341
|
-
|
|
63342
|
-
* Populated live from the upstream source on each state push.
|
|
63343
|
-
* Absent for unitless sensors. */
|
|
63344
|
-
unit: string().optional(),
|
|
63345
|
-
/** Suggested decimal places for numeric display.
|
|
63346
|
-
* Populated live from the upstream source when provided.
|
|
63347
|
-
* Falls back to auto-formatting when absent. */
|
|
63348
|
-
precision: number().int().min(0).max(10).optional(),
|
|
64232
|
+
/** Reading in the device quantity's base unit. `null` when refused (D393). */
|
|
64233
|
+
value: number().nullable().meta(readingMeta("device")),
|
|
63349
64234
|
/** Ms epoch when the slice was last updated. */
|
|
63350
64235
|
lastFetchedAt: number()
|
|
63351
64236
|
});
|
|
@@ -63369,11 +64254,11 @@ var numericSensorCapability = {
|
|
|
63369
64254
|
*/
|
|
63370
64255
|
durability: "restored",
|
|
63371
64256
|
/**
|
|
63372
|
-
* History (D850): recorded series. The unit is
|
|
64257
|
+
* History (D850): recorded series. The unit is the device quantity's base,
|
|
64258
|
+
* resolved when the series is read — the cap has no single unit.
|
|
63373
64259
|
*/
|
|
63374
64260
|
history: { fields: { value: {
|
|
63375
64261
|
kind: "gauge",
|
|
63376
|
-
unitFrom: "unit",
|
|
63377
64262
|
minIntervalMs: 3e4
|
|
63378
64263
|
} } },
|
|
63379
64264
|
/** Clock fields: written, but excluded from the compare that decides
|
|
@@ -63665,23 +64550,26 @@ var PetFeederStatusSchema = object({
|
|
|
63665
64550
|
/** Food currently in the bowl (grams). Null when the device has not
|
|
63666
64551
|
* reported a reading yet. On dual-hopper models this is the combined
|
|
63667
64552
|
* bowl reading; per-hopper levels live in `food1`/`food2`. */
|
|
63668
|
-
foodLevel: number().nullable(),
|
|
64553
|
+
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
63669
64554
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
63670
64555
|
* single-hopper models. */
|
|
63671
|
-
food1: number().nullable(),
|
|
64556
|
+
food1: number().nullable().meta(readingMeta("none")),
|
|
63672
64557
|
/** Hopper-2 food level (grams) on dual-hopper feeders; null on
|
|
63673
64558
|
* single-hopper models. */
|
|
63674
|
-
food2: number().nullable(),
|
|
64559
|
+
food2: number().nullable().meta(readingMeta("none")),
|
|
63675
64560
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
63676
64561
|
* (`device_class: problem`, on = low). True when the bowl is empty /
|
|
63677
64562
|
* below the feeder's low threshold. */
|
|
63678
64563
|
lowFood: boolean(),
|
|
63679
64564
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
63680
64565
|
* device has no battery reading. */
|
|
63681
|
-
batteryPower: number().min(0).max(100).nullable(),
|
|
63682
|
-
/**
|
|
63683
|
-
*
|
|
63684
|
-
|
|
64566
|
+
batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
64567
|
+
/**
|
|
64568
|
+
* Desiccant life remaining, stored in seconds (duration base, D944).
|
|
64569
|
+
* Providers convert from the days the feeder reports. Null when the
|
|
64570
|
+
* model has no desiccant sensor.
|
|
64571
|
+
*/
|
|
64572
|
+
desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
|
|
63685
64573
|
/** True while a feed is in progress. */
|
|
63686
64574
|
feeding: boolean(),
|
|
63687
64575
|
/** Decoded connectivity / power status (HA petkit device-status enum).
|
|
@@ -63692,7 +64580,7 @@ var PetFeederStatusSchema = object({
|
|
|
63692
64580
|
* with `errorCode` for consumers that want the raw integer. */
|
|
63693
64581
|
error: string().nullable(),
|
|
63694
64582
|
/** Raw device error code (0 / null = no error). */
|
|
63695
|
-
errorCode: number().nullable(),
|
|
64583
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
63696
64584
|
/** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
|
|
63697
64585
|
isDualHopper: boolean(),
|
|
63698
64586
|
/** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
|
|
@@ -63702,7 +64590,7 @@ var PetFeederStatusSchema = object({
|
|
|
63702
64590
|
/** Play a chime when dispensing. */
|
|
63703
64591
|
feedSound: boolean(),
|
|
63704
64592
|
/** Speaker / prompt volume level (device-scaled integer). */
|
|
63705
|
-
volume: number(),
|
|
64593
|
+
volume: number().meta(readingMeta("none")),
|
|
63706
64594
|
/** Ms epoch when the slice was last refreshed from the cloud. */
|
|
63707
64595
|
lastFetchedAt: number()
|
|
63708
64596
|
});
|
|
@@ -63821,7 +64709,7 @@ var petFeederCapability = {
|
|
|
63821
64709
|
},
|
|
63822
64710
|
desiccantLeftDays: {
|
|
63823
64711
|
kind: "gauge",
|
|
63824
|
-
unit: "
|
|
64712
|
+
unit: "s"
|
|
63825
64713
|
}
|
|
63826
64714
|
} }
|
|
63827
64715
|
};
|
|
@@ -64222,43 +65110,88 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
64222
65110
|
*/
|
|
64223
65111
|
var PowerMeterStatusSchema = object({
|
|
64224
65112
|
/** Instantaneous power draw in watts. */
|
|
64225
|
-
watts: number().optional().meta({
|
|
64226
|
-
unit: "W",
|
|
64227
|
-
label: "Power"
|
|
64228
|
-
}),
|
|
65113
|
+
watts: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
64229
65114
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
64230
|
-
kwhTotal: number().optional().meta({
|
|
64231
|
-
unit: "kWh",
|
|
64232
|
-
label: "Energy"
|
|
64233
|
-
}),
|
|
65115
|
+
kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
64234
65116
|
/** Voltage in volts. */
|
|
64235
|
-
volts: number().optional(),
|
|
65117
|
+
volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
64236
65118
|
/** Current in amperes. */
|
|
64237
|
-
amps: number().optional(),
|
|
64238
|
-
/** Apparent power in volt-amperes
|
|
64239
|
-
va: number().optional(),
|
|
65119
|
+
amps: number().optional().meta(readingMeta("current", { label: "Current" })),
|
|
65120
|
+
/** Apparent power in volt-amperes. */
|
|
65121
|
+
va: number().optional().meta(readingMeta("apparent-power")),
|
|
64240
65122
|
/** Power factor (0..1) if reported. */
|
|
64241
|
-
powerFactor: number().min(0).max(1).optional(),
|
|
65123
|
+
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
64242
65124
|
/** Ms epoch when the slice was last updated. */
|
|
64243
|
-
lastFetchedAt: number()
|
|
64244
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
64245
|
-
* `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
|
|
64246
|
-
* Each upstream `sensor.*` entity surfaces ONE device_class, so one
|
|
64247
|
-
* unit per slice is unambiguous. The UI prefers this over the role's
|
|
64248
|
-
* canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
|
|
64249
|
-
unit: string().optional(),
|
|
64250
|
-
/** Suggested decimal places for numeric display.
|
|
64251
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
64252
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
64253
|
-
* auto-formatting when absent. */
|
|
64254
|
-
precision: number().int().min(0).max(10).optional()
|
|
65125
|
+
lastFetchedAt: number()
|
|
64255
65126
|
});
|
|
65127
|
+
/**
|
|
65128
|
+
* Loads that draw metered mains power (D951). The reading is a feature of the
|
|
65129
|
+
* device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
|
|
65130
|
+
* vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
|
|
65131
|
+
* meter or a standalone energy monitor IS the device. Cover, lock, valve,
|
|
65132
|
+
* camera and the like are left out: their draw is not what anyone meters.
|
|
65133
|
+
*/
|
|
65134
|
+
var POWER_METER_DEVICE_TYPES = [
|
|
65135
|
+
DeviceType.Sensor,
|
|
65136
|
+
DeviceType.Switch,
|
|
65137
|
+
DeviceType.Light,
|
|
65138
|
+
DeviceType.Fan,
|
|
65139
|
+
DeviceType.Climate,
|
|
65140
|
+
DeviceType.Thermostat,
|
|
65141
|
+
DeviceType.Humidifier,
|
|
65142
|
+
DeviceType.WaterHeater,
|
|
65143
|
+
DeviceType.MediaPlayer,
|
|
65144
|
+
DeviceType.Washer,
|
|
65145
|
+
DeviceType.Dryer,
|
|
65146
|
+
DeviceType.Dishwasher,
|
|
65147
|
+
DeviceType.Vacuum,
|
|
65148
|
+
DeviceType.LawnMower,
|
|
65149
|
+
DeviceType.PetFeeder
|
|
65150
|
+
];
|
|
65151
|
+
/** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
|
|
65152
|
+
var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
|
|
65153
|
+
/** One scalar target per reading; each is sourced from a device of its own (D951). */
|
|
65154
|
+
function powerTarget(id, unit) {
|
|
65155
|
+
return {
|
|
65156
|
+
id,
|
|
65157
|
+
cardinality: "scalar",
|
|
65158
|
+
vocabulary: {
|
|
65159
|
+
cap: "power-meter",
|
|
65160
|
+
stateField: id
|
|
65161
|
+
},
|
|
65162
|
+
deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
|
|
65163
|
+
apply: "add",
|
|
65164
|
+
path: id,
|
|
65165
|
+
sources: [{
|
|
65166
|
+
kind: "numeric",
|
|
65167
|
+
requiredUnit: unit,
|
|
65168
|
+
transforms: [{ kind: "identity" }]
|
|
65169
|
+
}],
|
|
65170
|
+
companions: [{
|
|
65171
|
+
kind: "clock",
|
|
65172
|
+
path: "lastFetchedAt",
|
|
65173
|
+
when: "always"
|
|
65174
|
+
}],
|
|
65175
|
+
sourceReport: { status: "assignment-status" },
|
|
65176
|
+
advancedExpression: true
|
|
65177
|
+
};
|
|
65178
|
+
}
|
|
65179
|
+
/** "Power and energy" extension: watts, energy, voltage and current from other devices (D951). */
|
|
65180
|
+
var powerMeterCustomize = {
|
|
65181
|
+
version: 1,
|
|
65182
|
+
targets: [
|
|
65183
|
+
powerTarget("watts", "W"),
|
|
65184
|
+
powerTarget("kwhTotal", "kWh"),
|
|
65185
|
+
powerTarget("volts", "V"),
|
|
65186
|
+
powerTarget("amps", "A")
|
|
65187
|
+
]
|
|
65188
|
+
};
|
|
64256
65189
|
var powerMeterCapability = {
|
|
64257
65190
|
name: "power-meter",
|
|
64258
65191
|
scope: "device",
|
|
64259
65192
|
deviceNative: true,
|
|
64260
65193
|
mode: "singleton",
|
|
64261
|
-
deviceTypes:
|
|
65194
|
+
deviceTypes: POWER_METER_DEVICE_TYPES,
|
|
64262
65195
|
methods: {},
|
|
64263
65196
|
status: {
|
|
64264
65197
|
schema: PowerMeterStatusSchema,
|
|
@@ -64316,11 +65249,7 @@ var powerMeterCapability = {
|
|
|
64316
65249
|
/** Clock fields: written, but excluded from the compare that decides
|
|
64317
65250
|
* whether persisting is worth a SQLite commit. */
|
|
64318
65251
|
volatileStateFields: ["lastFetchedAt"],
|
|
64319
|
-
customize:
|
|
64320
|
-
version: 1,
|
|
64321
|
-
advanced: "derive",
|
|
64322
|
-
targets: []
|
|
64323
|
-
}
|
|
65252
|
+
customize: powerMeterCustomize
|
|
64324
65253
|
};
|
|
64325
65254
|
/**
|
|
64326
65255
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
@@ -64340,11 +65269,11 @@ var powerMeterCapability = {
|
|
|
64340
65269
|
*/
|
|
64341
65270
|
var GpsLocationSchema = object({
|
|
64342
65271
|
/** Latitude in decimal degrees, -90..90. */
|
|
64343
|
-
latitude: number().min(-90).max(90),
|
|
65272
|
+
latitude: number().min(-90).max(90).meta(readingMeta("angle")),
|
|
64344
65273
|
/** Longitude in decimal degrees, -180..180. */
|
|
64345
|
-
longitude: number().min(-180).max(180),
|
|
65274
|
+
longitude: number().min(-180).max(180).meta(readingMeta("angle")),
|
|
64346
65275
|
/** Reported accuracy in meters (lower = better). */
|
|
64347
|
-
accuracyMeters: number().nonnegative()
|
|
65276
|
+
accuracyMeters: number().nonnegative().meta(readingMeta("length"))
|
|
64348
65277
|
});
|
|
64349
65278
|
var PresenceStatusSchema = object({
|
|
64350
65279
|
/** `home` / `not_home` / any user-defined zone name. */
|
|
@@ -64356,7 +65285,7 @@ var PresenceStatusSchema = object({
|
|
|
64356
65285
|
gps: GpsLocationSchema.nullable(),
|
|
64357
65286
|
/** Optional battery level of the tracking device (0..100). Null
|
|
64358
65287
|
* when not reported. */
|
|
64359
|
-
batteryPercent: number().min(0).max(100).nullable(),
|
|
65288
|
+
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
64360
65289
|
/** Ms epoch when the slice was last updated. */
|
|
64361
65290
|
lastChangedAt: number()
|
|
64362
65291
|
});
|
|
@@ -64406,19 +65335,10 @@ var presenceCapability = {
|
|
|
64406
65335
|
* `sourceInfo`.
|
|
64407
65336
|
*/
|
|
64408
65337
|
var PressureSensorStatusSchema = object({
|
|
64409
|
-
/** Current pressure in hPa. */
|
|
64410
|
-
hpa: number(),
|
|
65338
|
+
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
65339
|
+
hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
64411
65340
|
/** Ms epoch when the slice was last updated. */
|
|
64412
|
-
lastFetchedAt: number()
|
|
64413
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
64414
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
64415
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
64416
|
-
unit: string().optional(),
|
|
64417
|
-
/** Suggested decimal places for numeric display.
|
|
64418
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
64419
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
64420
|
-
* auto-formatting when absent. */
|
|
64421
|
-
precision: number().int().min(0).max(10).optional()
|
|
65341
|
+
lastFetchedAt: number()
|
|
64422
65342
|
});
|
|
64423
65343
|
var pressureSensorCapability = {
|
|
64424
65344
|
name: "pressure-sensor",
|
|
@@ -64832,8 +65752,8 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
|
|
|
64832
65752
|
}), _void(), { kind: "mutation" });
|
|
64833
65753
|
var PtzAutotrackSettingsSchema = object({
|
|
64834
65754
|
targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
|
|
64835
|
-
stopDelaySeconds: number().int().min(0).max(300),
|
|
64836
|
-
disappearDelaySeconds: number().int().min(0).max(300)
|
|
65755
|
+
stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
|
|
65756
|
+
disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
|
|
64837
65757
|
});
|
|
64838
65758
|
/**
|
|
64839
65759
|
* One option the camera advertises as a valid `targetType`. Drivers
|
|
@@ -64987,7 +65907,7 @@ var RunningStateStatusSchema = object({
|
|
|
64987
65907
|
/** null = unknown — never coerced to false (D393). */
|
|
64988
65908
|
running: boolean().nullable(),
|
|
64989
65909
|
/** Epoch ms the current boolean began; null while `running` is null. */
|
|
64990
|
-
since: number().nullable(),
|
|
65910
|
+
since: number().nullable().meta(readingMeta("none")),
|
|
64991
65911
|
source: RunningStateSourceSchema.meta({ label: "Source" }),
|
|
64992
65912
|
phase: string().nullable(),
|
|
64993
65913
|
/** null until a reading has been published (D393). */
|
|
@@ -65002,7 +65922,6 @@ var RUNNING_DEVICE_TYPES = Object.values(DeviceType);
|
|
|
65002
65922
|
/** Generic Customize target for the running boolean (D936). */
|
|
65003
65923
|
var runningCustomize = {
|
|
65004
65924
|
version: 1,
|
|
65005
|
-
advanced: "derive",
|
|
65006
65925
|
targets: [{
|
|
65007
65926
|
id: "running",
|
|
65008
65927
|
cardinality: "scalar",
|
|
@@ -65011,6 +65930,8 @@ var runningCustomize = {
|
|
|
65011
65930
|
stateField: "running"
|
|
65012
65931
|
},
|
|
65013
65932
|
deviceTypes: [
|
|
65933
|
+
DeviceType.Washer,
|
|
65934
|
+
DeviceType.Dryer,
|
|
65014
65935
|
DeviceType.Dishwasher,
|
|
65015
65936
|
DeviceType.Vacuum,
|
|
65016
65937
|
DeviceType.LawnMower,
|
|
@@ -65022,7 +65943,7 @@ var runningCustomize = {
|
|
|
65022
65943
|
DeviceType.Thermostat,
|
|
65023
65944
|
DeviceType.PetFeeder
|
|
65024
65945
|
],
|
|
65025
|
-
apply: "add
|
|
65946
|
+
apply: "add",
|
|
65026
65947
|
path: "running",
|
|
65027
65948
|
sources: [
|
|
65028
65949
|
{
|
|
@@ -66286,9 +67207,9 @@ var RecordTriggerSchema = _enum([
|
|
|
66286
67207
|
*/
|
|
66287
67208
|
var RecordWindowSchema = object({
|
|
66288
67209
|
trigger: RecordTriggerSchema,
|
|
66289
|
-
day: number().int().min(0).max(6),
|
|
66290
|
-
startMinute: number().int().min(0).max(1439),
|
|
66291
|
-
endMinute: number().int().min(1).max(1440)
|
|
67210
|
+
day: number().int().min(0).max(6).meta(readingMeta("none")),
|
|
67211
|
+
startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
|
|
67212
|
+
endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
|
|
66292
67213
|
});
|
|
66293
67214
|
/** Status of one physical volume, as the camera itself describes it. */
|
|
66294
67215
|
var OnboardStorageVolumeSchema = object({
|
|
@@ -66364,10 +67285,10 @@ var RecordingOnboardStatusSchema = object({
|
|
|
66364
67285
|
/** Master "record to the card at all" switch. */
|
|
66365
67286
|
enabled: boolean().nullable(),
|
|
66366
67287
|
overwriteWhenFull: boolean().nullable(),
|
|
66367
|
-
preRecordSec: number().nullable(),
|
|
66368
|
-
postRecordSec: number().nullable(),
|
|
67288
|
+
preRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
67289
|
+
postRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
66369
67290
|
/** Length of one recorded file, in minutes. */
|
|
66370
|
-
segmentMinutes: number().nullable(),
|
|
67291
|
+
segmentMinutes: number().nullable().meta(readingMeta("none")),
|
|
66371
67292
|
/** The primary track's weekly windows, flattened. */
|
|
66372
67293
|
windows: array(RecordWindowSchema),
|
|
66373
67294
|
/**
|
|
@@ -66383,7 +67304,7 @@ var RecordingOnboardStatusSchema = object({
|
|
|
66383
67304
|
* provider reporting a non-zero count also reports the schedule as not
|
|
66384
67305
|
* writable.
|
|
66385
67306
|
*/
|
|
66386
|
-
unreadableWindows: number(),
|
|
67307
|
+
unreadableWindows: number().meta(readingMeta("count")),
|
|
66387
67308
|
/**
|
|
66388
67309
|
* The camera is scheduled to record and has NO usable storage.
|
|
66389
67310
|
*
|
|
@@ -67228,7 +68149,7 @@ var CamStreamDescriptorSchema = object({
|
|
|
67228
68149
|
url: string().optional(),
|
|
67229
68150
|
codec: string().optional(),
|
|
67230
68151
|
resolution: CamStreamResolutionSchema.optional(),
|
|
67231
|
-
fps: number().positive().optional(),
|
|
68152
|
+
fps: number().positive().optional().meta(readingMeta("rate")),
|
|
67232
68153
|
label: string().optional(),
|
|
67233
68154
|
/** Device-level features (e.g. `battery-operated`) — drives broker policy. */
|
|
67234
68155
|
deviceFeatures: array(string()).optional(),
|
|
@@ -67315,18 +68236,18 @@ var StreamProfileSchema = _enum([
|
|
|
67315
68236
|
]);
|
|
67316
68237
|
/** The current encoding config of one stream profile. */
|
|
67317
68238
|
var StreamProfileConfigSchema = object({
|
|
67318
|
-
width: number(),
|
|
67319
|
-
height: number(),
|
|
68239
|
+
width: number().meta(readingMeta("none")),
|
|
68240
|
+
height: number().meta(readingMeta("none")),
|
|
67320
68241
|
codec: _enum(["h264", "h265"]),
|
|
67321
|
-
framerate: number(),
|
|
67322
|
-
bitrate: number(),
|
|
68242
|
+
framerate: number().meta(readingMeta("rate")),
|
|
68243
|
+
bitrate: number().meta(readingMeta("bitrate")),
|
|
67323
68244
|
bitrateMode: _enum(["vbr", "cbr"]).optional(),
|
|
67324
68245
|
encoderProfile: _enum([
|
|
67325
68246
|
"high",
|
|
67326
68247
|
"main",
|
|
67327
68248
|
"baseline"
|
|
67328
68249
|
]).optional(),
|
|
67329
|
-
gop: number().optional(),
|
|
68250
|
+
gop: number().optional().meta(readingMeta("none")),
|
|
67330
68251
|
/**
|
|
67331
68252
|
* Whether THIS profile currently carries an audio track. READ-ONLY here.
|
|
67332
68253
|
*
|
|
@@ -67500,7 +68421,8 @@ var switchCapability = {
|
|
|
67500
68421
|
deviceTypes: [
|
|
67501
68422
|
DeviceType.Switch,
|
|
67502
68423
|
DeviceType.Siren,
|
|
67503
|
-
DeviceType.Light
|
|
68424
|
+
DeviceType.Light,
|
|
68425
|
+
DeviceType.Fan
|
|
67504
68426
|
],
|
|
67505
68427
|
methods: { setState: method(object({
|
|
67506
68428
|
deviceId: number(),
|
|
@@ -67547,12 +68469,7 @@ var switchCapability = {
|
|
|
67547
68469
|
label: "On",
|
|
67548
68470
|
kind: "boolean"
|
|
67549
68471
|
}
|
|
67550
|
-
}] }
|
|
67551
|
-
customize: {
|
|
67552
|
-
version: 1,
|
|
67553
|
-
advanced: "derive",
|
|
67554
|
-
targets: []
|
|
67555
|
-
}
|
|
68472
|
+
}] }
|
|
67556
68473
|
};
|
|
67557
68474
|
/** C0 controls, DEL and the C1 range: none of them belongs in a name. */
|
|
67558
68475
|
function hasControlCharacter(value) {
|
|
@@ -68135,35 +69052,21 @@ var tamperCapability = {
|
|
|
68135
69052
|
* entries with `device_class: temperature` and any future native
|
|
68136
69053
|
* thermometer.
|
|
68137
69054
|
*
|
|
68138
|
-
*
|
|
68139
|
-
*
|
|
68140
|
-
* (climate-control's `currentTemp`, energy analytics) can compose
|
|
68141
|
-
* without per-source unit fixups. The canonical display unit (`°C`) is
|
|
68142
|
-
* a descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
68143
|
-
* `sourceInfo`.
|
|
69055
|
+
* The number is Celsius (D944). Providers convert on ingest. A failed
|
|
69056
|
+
* conversion is `null`, never a guess. Display unit is a read-time preference.
|
|
68144
69057
|
*
|
|
68145
69058
|
* Status `lastFetchedAt` lets staleness-aware consumers detect a
|
|
68146
69059
|
* frozen feed (provider hung) distinct from a "temperature hasn't
|
|
68147
69060
|
* changed" steady state.
|
|
68148
69061
|
*/
|
|
68149
69062
|
var TemperatureSensorStatusSchema = object({
|
|
68150
|
-
/** Current temperature in Celsius. */
|
|
68151
|
-
celsius: number().meta({
|
|
68152
|
-
|
|
69063
|
+
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
69064
|
+
celsius: number().nullable().meta(readingMeta("temperature", {
|
|
69065
|
+
precision: 1,
|
|
68153
69066
|
label: "Temperature"
|
|
68154
|
-
}),
|
|
69067
|
+
})),
|
|
68155
69068
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
68156
|
-
lastFetchedAt: number()
|
|
68157
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
68158
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
68159
|
-
* role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
|
|
68160
|
-
* Absent → the UI falls back to the role's canonical unit. */
|
|
68161
|
-
unit: string().optional(),
|
|
68162
|
-
/** Suggested decimal places for numeric display.
|
|
68163
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
68164
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
68165
|
-
* auto-formatting when absent. */
|
|
68166
|
-
precision: number().int().min(0).max(10).optional()
|
|
69069
|
+
lastFetchedAt: number()
|
|
68167
69070
|
});
|
|
68168
69071
|
var temperatureSensorCapability = {
|
|
68169
69072
|
name: "temperature-sensor",
|
|
@@ -68194,12 +69097,7 @@ var temperatureSensorCapability = {
|
|
|
68194
69097
|
} } },
|
|
68195
69098
|
/** Clock fields: written, but excluded from the compare that decides
|
|
68196
69099
|
* whether persisting is worth a SQLite commit. */
|
|
68197
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
68198
|
-
customize: {
|
|
68199
|
-
version: 1,
|
|
68200
|
-
advanced: "derive",
|
|
68201
|
-
targets: []
|
|
68202
|
-
}
|
|
69100
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
68203
69101
|
};
|
|
68204
69102
|
/**
|
|
68205
69103
|
* toast — system-scoped singleton capability that streams toast
|
|
@@ -68482,16 +69380,16 @@ var VacuumControlStatusSchema = object({
|
|
|
68482
69380
|
]),
|
|
68483
69381
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
68484
69382
|
* reading. */
|
|
68485
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
69383
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68486
69384
|
/** Current fan-speed token (provider-verbatim). Null when unknown or
|
|
68487
69385
|
* the vacuum has no speed control. */
|
|
68488
69386
|
fanSpeed: string().nullable(),
|
|
68489
69387
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
68490
69388
|
availableFanSpeeds: array(string()),
|
|
68491
69389
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
68492
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
69390
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68493
69391
|
/** Current error code (0 / null = no error). */
|
|
68494
|
-
errorCode: number().nullable(),
|
|
69392
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
68495
69393
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
68496
69394
|
errorLabel: string().nullable(),
|
|
68497
69395
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -68569,7 +69467,7 @@ var ValveStatusSchema = object({
|
|
|
68569
69467
|
]),
|
|
68570
69468
|
/** 0 = fully closed, 100 = fully open. Null when the device has no
|
|
68571
69469
|
* intermediate position surface. */
|
|
68572
|
-
position: number().min(0).max(100).nullable(),
|
|
69470
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68573
69471
|
/** Ms epoch when the slice was last updated. */
|
|
68574
69472
|
lastChangedAt: number()
|
|
68575
69473
|
});
|
|
@@ -68677,9 +69575,9 @@ var vibrationCapability = {
|
|
|
68677
69575
|
*/
|
|
68678
69576
|
var WaterHeaterStatusSchema = object({
|
|
68679
69577
|
/** Current measured temperature. Null when not reported. */
|
|
68680
|
-
currentTemp: number().nullable(),
|
|
69578
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68681
69579
|
/** Target temperature setpoint. Null when no setpoint surface. */
|
|
68682
|
-
targetTemp: number().nullable(),
|
|
69580
|
+
targetTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68683
69581
|
/** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
|
|
68684
69582
|
* `heat_pump` / `high_demand` / `performance` / `off`). Null when the
|
|
68685
69583
|
* device reports an unknown state. */
|
|
@@ -68690,9 +69588,9 @@ var WaterHeaterStatusSchema = object({
|
|
|
68690
69588
|
* has no away surface. */
|
|
68691
69589
|
away: boolean().nullable(),
|
|
68692
69590
|
/** HA `min_temp` attribute. Null when not reported. */
|
|
68693
|
-
minTemp: number().nullable(),
|
|
69591
|
+
minTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68694
69592
|
/** HA `max_temp` attribute. Null when not reported. */
|
|
68695
|
-
maxTemp: number().nullable(),
|
|
69593
|
+
maxTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68696
69594
|
/** Ms epoch when the slice was last updated. */
|
|
68697
69595
|
lastChangedAt: number()
|
|
68698
69596
|
});
|
|
@@ -68777,25 +69675,18 @@ var waterHeaterCapability = {
|
|
|
68777
69675
|
* Forecast deferred — current conditions only for v1.
|
|
68778
69676
|
*/
|
|
68779
69677
|
var WeatherStatusSchema = object({
|
|
68780
|
-
/** Verbatim
|
|
68781
|
-
* when no condition has been reported yet. */
|
|
69678
|
+
/** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
|
|
68782
69679
|
condition: string().nullable(),
|
|
68783
|
-
/** Current temperature in
|
|
68784
|
-
temperature: number().nullable(),
|
|
68785
|
-
/** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
|
|
68786
|
-
temperatureUnit: string().nullable(),
|
|
69680
|
+
/** Current temperature in Celsius. Null when not provided or refused. */
|
|
69681
|
+
temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
|
|
68787
69682
|
/** Relative humidity (0..100). Null when not provided. */
|
|
68788
|
-
humidity: number().min(0).max(100).nullable(),
|
|
68789
|
-
/** Barometric pressure in
|
|
68790
|
-
pressure: number().nullable(),
|
|
68791
|
-
/**
|
|
68792
|
-
|
|
68793
|
-
/** Wind speed in the reported unit. Null when not provided. */
|
|
68794
|
-
windSpeed: number().nullable(),
|
|
68795
|
-
/** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
|
|
68796
|
-
windSpeedUnit: string().nullable(),
|
|
69683
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
69684
|
+
/** Barometric pressure in hPa. Null when not provided or refused. */
|
|
69685
|
+
pressure: number().nullable().meta(readingMeta("pressure")),
|
|
69686
|
+
/** Wind speed in metres per second. Null when not provided or refused. */
|
|
69687
|
+
windSpeed: number().nullable().meta(readingMeta("speed")),
|
|
68797
69688
|
/** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
|
|
68798
|
-
windBearing: number().nullable(),
|
|
69689
|
+
windBearing: number().nullable().meta(readingMeta("angle")),
|
|
68799
69690
|
/** Ms epoch when the slice was last updated. */
|
|
68800
69691
|
lastFetchedAt: number()
|
|
68801
69692
|
});
|
|
@@ -68823,12 +69714,12 @@ var weatherCapability = {
|
|
|
68823
69714
|
*/
|
|
68824
69715
|
durability: "session",
|
|
68825
69716
|
/**
|
|
68826
|
-
* History (D850): recorded series.
|
|
69717
|
+
* History (D850): recorded series. Numbers are the quantity base (D944).
|
|
68827
69718
|
*/
|
|
68828
69719
|
history: { fields: {
|
|
68829
69720
|
temperature: {
|
|
68830
69721
|
kind: "gauge",
|
|
68831
|
-
|
|
69722
|
+
unit: "°C",
|
|
68832
69723
|
deadband: .2
|
|
68833
69724
|
},
|
|
68834
69725
|
humidity: {
|
|
@@ -68838,12 +69729,12 @@ var weatherCapability = {
|
|
|
68838
69729
|
},
|
|
68839
69730
|
pressure: {
|
|
68840
69731
|
kind: "gauge",
|
|
68841
|
-
|
|
69732
|
+
unit: "hPa",
|
|
68842
69733
|
deadband: .5
|
|
68843
69734
|
},
|
|
68844
69735
|
windSpeed: {
|
|
68845
69736
|
kind: "gauge",
|
|
68846
|
-
|
|
69737
|
+
unit: "m/s",
|
|
68847
69738
|
deadband: .5
|
|
68848
69739
|
}
|
|
68849
69740
|
} }
|
|
@@ -69744,8 +70635,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
69744
70635
|
kind: "mutation",
|
|
69745
70636
|
auth: "admin"
|
|
69746
70637
|
});
|
|
70638
|
+
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
69747
70639
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
69748
|
-
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;
|
|
70640
|
+
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;
|
|
69749
70641
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
69750
70642
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
69751
70643
|
new Set(CAP_NAMES);
|
|
@@ -70074,6 +70966,9 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
70074
70966
|
featureProbe: featureProbeCapability,
|
|
70075
70967
|
flood: floodCapability,
|
|
70076
70968
|
gas: gasCapability,
|
|
70969
|
+
groupActive: groupActiveCapability,
|
|
70970
|
+
groupCommands: groupCommandsCapability,
|
|
70971
|
+
groupStatus: groupStatusCapability,
|
|
70077
70972
|
heatingValve: heatingValveCapability,
|
|
70078
70973
|
humidifier: humidifierCapability,
|
|
70079
70974
|
humiditySensor: humiditySensorCapability,
|
|
@@ -70083,6 +70978,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
70083
70978
|
lawnMowerControl: lawnMowerControlCapability,
|
|
70084
70979
|
lockControl: lockControlCapability,
|
|
70085
70980
|
mediaPlayer: mediaPlayerCapability,
|
|
70981
|
+
meter: meterCapability,
|
|
70086
70982
|
motion: motionCapability,
|
|
70087
70983
|
motionTrigger: motionTriggerCapability,
|
|
70088
70984
|
motionZones: motionZonesCapability,
|
|
@@ -71342,7 +72238,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
71342
72238
|
return toDeviceSummary(device, this.addonId);
|
|
71343
72239
|
}
|
|
71344
72240
|
};
|
|
71345
|
-
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;
|
|
72241
|
+
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;
|
|
71346
72242
|
new Set(Object.values(DeviceType));
|
|
71347
72243
|
Object.freeze({
|
|
71348
72244
|
"accessories.getStatus": {
|
|
@@ -71450,6 +72346,13 @@ Object.freeze({
|
|
|
71450
72346
|
access: "view",
|
|
71451
72347
|
role: "admin"
|
|
71452
72348
|
},
|
|
72349
|
+
"addons.getPackageDocs": {
|
|
72350
|
+
capName: "addons",
|
|
72351
|
+
capScope: "system",
|
|
72352
|
+
addonId: null,
|
|
72353
|
+
access: "view",
|
|
72354
|
+
role: "admin"
|
|
72355
|
+
},
|
|
71453
72356
|
"addons.getVersions": {
|
|
71454
72357
|
capName: "addons",
|
|
71455
72358
|
capScope: "system",
|
|
@@ -74558,6 +75461,27 @@ Object.freeze({
|
|
|
74558
75461
|
access: "view",
|
|
74559
75462
|
role: "viewer"
|
|
74560
75463
|
},
|
|
75464
|
+
"groupActive.getStatus": {
|
|
75465
|
+
capName: "group-active",
|
|
75466
|
+
capScope: "device",
|
|
75467
|
+
addonId: null,
|
|
75468
|
+
access: "view",
|
|
75469
|
+
role: "viewer"
|
|
75470
|
+
},
|
|
75471
|
+
"groupCommands.getStatus": {
|
|
75472
|
+
capName: "group-commands",
|
|
75473
|
+
capScope: "device",
|
|
75474
|
+
addonId: null,
|
|
75475
|
+
access: "view",
|
|
75476
|
+
role: "viewer"
|
|
75477
|
+
},
|
|
75478
|
+
"groupStatus.getStatus": {
|
|
75479
|
+
capName: "group-status",
|
|
75480
|
+
capScope: "device",
|
|
75481
|
+
addonId: null,
|
|
75482
|
+
access: "view",
|
|
75483
|
+
role: "viewer"
|
|
75484
|
+
},
|
|
74561
75485
|
"heatingValve.getStatus": {
|
|
74562
75486
|
capName: "heating-valve",
|
|
74563
75487
|
capScope: "device",
|
|
@@ -75510,6 +76434,13 @@ Object.freeze({
|
|
|
75510
76434
|
access: "create",
|
|
75511
76435
|
role: "admin"
|
|
75512
76436
|
},
|
|
76437
|
+
"meter.getStatus": {
|
|
76438
|
+
capName: "meter",
|
|
76439
|
+
capScope: "device",
|
|
76440
|
+
addonId: null,
|
|
76441
|
+
access: "view",
|
|
76442
|
+
role: "viewer"
|
|
76443
|
+
},
|
|
75513
76444
|
"metricsProvider.collectSnapshot": {
|
|
75514
76445
|
capName: "metrics-provider",
|
|
75515
76446
|
capScope: "system",
|
|
@@ -83608,7 +84539,39 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
83608
84539
|
phrase: t("When {device} finishes", "Quando {device} finisce"),
|
|
83609
84540
|
icon: "square"
|
|
83610
84541
|
}
|
|
83611
|
-
}
|
|
84542
|
+
},
|
|
84543
|
+
overrides: { byType: {
|
|
84544
|
+
[DeviceType.Washer]: {
|
|
84545
|
+
state: { running: {
|
|
84546
|
+
whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
|
|
84547
|
+
whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
|
|
84548
|
+
} },
|
|
84549
|
+
events: {
|
|
84550
|
+
onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
|
|
84551
|
+
onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
|
|
84552
|
+
}
|
|
84553
|
+
},
|
|
84554
|
+
[DeviceType.Dryer]: {
|
|
84555
|
+
state: { running: {
|
|
84556
|
+
whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
|
|
84557
|
+
whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
|
|
84558
|
+
} },
|
|
84559
|
+
events: {
|
|
84560
|
+
onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
|
|
84561
|
+
onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
|
|
84562
|
+
}
|
|
84563
|
+
},
|
|
84564
|
+
[DeviceType.Dishwasher]: {
|
|
84565
|
+
state: { running: {
|
|
84566
|
+
whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
|
|
84567
|
+
whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
|
|
84568
|
+
} },
|
|
84569
|
+
events: {
|
|
84570
|
+
onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
|
|
84571
|
+
onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
|
|
84572
|
+
}
|
|
84573
|
+
}
|
|
84574
|
+
} }
|
|
83612
84575
|
},
|
|
83613
84576
|
update: {
|
|
83614
84577
|
scope: "device",
|
|
@@ -84380,6 +85343,41 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84380
85343
|
icon: "flame"
|
|
84381
85344
|
}]
|
|
84382
85345
|
},
|
|
85346
|
+
"group-active": {
|
|
85347
|
+
scope: "device",
|
|
85348
|
+
noun: t("Group state", "Stato del gruppo")
|
|
85349
|
+
},
|
|
85350
|
+
"group-commands": {
|
|
85351
|
+
scope: "device",
|
|
85352
|
+
noun: t("Group command", "Comando di gruppo")
|
|
85353
|
+
},
|
|
85354
|
+
"group-status": {
|
|
85355
|
+
scope: "device",
|
|
85356
|
+
noun: t("Group", "Gruppo"),
|
|
85357
|
+
state: [
|
|
85358
|
+
{
|
|
85359
|
+
kind: "numeric",
|
|
85360
|
+
field: "included",
|
|
85361
|
+
quantity: t("the members reporting", "i membri che rispondono"),
|
|
85362
|
+
label: t("Reporting", "Rispondono"),
|
|
85363
|
+
icon: "users"
|
|
85364
|
+
},
|
|
85365
|
+
{
|
|
85366
|
+
kind: "numeric",
|
|
85367
|
+
field: "excluded",
|
|
85368
|
+
quantity: t("the members left out", "i membri esclusi"),
|
|
85369
|
+
label: t("Excluded", "Esclusi"),
|
|
85370
|
+
icon: "users"
|
|
85371
|
+
},
|
|
85372
|
+
{
|
|
85373
|
+
kind: "numeric",
|
|
85374
|
+
field: "total",
|
|
85375
|
+
quantity: t("the member count", "il numero di membri"),
|
|
85376
|
+
label: t("Members", "Membri"),
|
|
85377
|
+
icon: "sigma"
|
|
85378
|
+
}
|
|
85379
|
+
]
|
|
85380
|
+
},
|
|
84383
85381
|
"humidity-sensor": {
|
|
84384
85382
|
scope: "device",
|
|
84385
85383
|
noun: t("Humidity", "Umidità"),
|
|
@@ -84405,6 +85403,17 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84405
85403
|
}],
|
|
84406
85404
|
events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
|
|
84407
85405
|
},
|
|
85406
|
+
meter: {
|
|
85407
|
+
scope: "device",
|
|
85408
|
+
noun: t("Meter", "Contatore"),
|
|
85409
|
+
state: [{
|
|
85410
|
+
kind: "numeric",
|
|
85411
|
+
field: "value",
|
|
85412
|
+
quantity: t("the total", "il totale"),
|
|
85413
|
+
label: t("Total", "Totale"),
|
|
85414
|
+
icon: "gauge"
|
|
85415
|
+
}]
|
|
85416
|
+
},
|
|
84408
85417
|
"numeric-sensor": {
|
|
84409
85418
|
scope: "device",
|
|
84410
85419
|
noun: t("Sensor", "Sensore"),
|
|
@@ -84413,7 +85422,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84413
85422
|
field: "value",
|
|
84414
85423
|
quantity: t("the reading", "il valore"),
|
|
84415
85424
|
label: t("Reading", "Valore"),
|
|
84416
|
-
unitField: "unit",
|
|
84417
85425
|
icon: "gauge"
|
|
84418
85426
|
}]
|
|
84419
85427
|
},
|
|
@@ -84549,7 +85557,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84549
85557
|
field: "temperature",
|
|
84550
85558
|
quantity: t("the outdoor temperature", "la temperatura esterna"),
|
|
84551
85559
|
label: t("Temperature", "Temperatura"),
|
|
84552
|
-
|
|
85560
|
+
unit: "°C",
|
|
84553
85561
|
icon: "thermometer"
|
|
84554
85562
|
},
|
|
84555
85563
|
{
|
|
@@ -84565,7 +85573,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84565
85573
|
field: "windSpeed",
|
|
84566
85574
|
quantity: t("the wind speed", "la velocità del vento"),
|
|
84567
85575
|
label: t("Wind", "Vento"),
|
|
84568
|
-
|
|
85576
|
+
unit: "m/s",
|
|
84569
85577
|
icon: "wind"
|
|
84570
85578
|
},
|
|
84571
85579
|
{
|
|
@@ -84573,7 +85581,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84573
85581
|
field: "pressure",
|
|
84574
85582
|
quantity: t("the air pressure", "la pressione atmosferica"),
|
|
84575
85583
|
label: t("Pressure", "Pressione"),
|
|
84576
|
-
|
|
85584
|
+
unit: "hPa",
|
|
84577
85585
|
icon: "gauge"
|
|
84578
85586
|
}
|
|
84579
85587
|
]
|
|
@@ -84878,8 +85886,47 @@ function projectAction(cap, action) {
|
|
|
84878
85886
|
};
|
|
84879
85887
|
}
|
|
84880
85888
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
84881
|
-
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;
|
|
85889
|
+
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;
|
|
84882
85890
|
DeviceFeature.BatteryOperated;
|
|
85891
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
85892
|
+
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.";
|
|
85893
|
+
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.";
|
|
85894
|
+
var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
|
|
85895
|
+
var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
|
|
85896
|
+
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.";
|
|
85897
|
+
function withheld(reason) {
|
|
85898
|
+
return {
|
|
85899
|
+
[DeviceType.Washer]: {
|
|
85900
|
+
exported: false,
|
|
85901
|
+
reason
|
|
85902
|
+
},
|
|
85903
|
+
[DeviceType.Dryer]: {
|
|
85904
|
+
exported: false,
|
|
85905
|
+
reason
|
|
85906
|
+
},
|
|
85907
|
+
[DeviceType.Dishwasher]: {
|
|
85908
|
+
exported: false,
|
|
85909
|
+
reason
|
|
85910
|
+
}
|
|
85911
|
+
};
|
|
85912
|
+
}
|
|
85913
|
+
function offered(received) {
|
|
85914
|
+
return {
|
|
85915
|
+
[DeviceType.Washer]: {
|
|
85916
|
+
exported: true,
|
|
85917
|
+
as: received
|
|
85918
|
+
},
|
|
85919
|
+
[DeviceType.Dryer]: {
|
|
85920
|
+
exported: true,
|
|
85921
|
+
as: received
|
|
85922
|
+
},
|
|
85923
|
+
[DeviceType.Dishwasher]: {
|
|
85924
|
+
exported: true,
|
|
85925
|
+
as: received
|
|
85926
|
+
}
|
|
85927
|
+
};
|
|
85928
|
+
}
|
|
85929
|
+
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
84883
85930
|
Object.freeze({
|
|
84884
85931
|
"accessories.getStatus": [{
|
|
84885
85932
|
name: "deviceId",
|
|
@@ -85846,6 +86893,21 @@ Object.freeze({
|
|
|
85846
86893
|
form: "single",
|
|
85847
86894
|
optional: false
|
|
85848
86895
|
}],
|
|
86896
|
+
"groupActive.getStatus": [{
|
|
86897
|
+
name: "deviceId",
|
|
86898
|
+
form: "single",
|
|
86899
|
+
optional: false
|
|
86900
|
+
}],
|
|
86901
|
+
"groupCommands.getStatus": [{
|
|
86902
|
+
name: "deviceId",
|
|
86903
|
+
form: "single",
|
|
86904
|
+
optional: false
|
|
86905
|
+
}],
|
|
86906
|
+
"groupStatus.getStatus": [{
|
|
86907
|
+
name: "deviceId",
|
|
86908
|
+
form: "single",
|
|
86909
|
+
optional: false
|
|
86910
|
+
}],
|
|
85849
86911
|
"heatingValve.getStatus": [{
|
|
85850
86912
|
name: "deviceId",
|
|
85851
86913
|
form: "single",
|
|
@@ -86046,6 +87108,11 @@ Object.freeze({
|
|
|
86046
87108
|
form: "single",
|
|
86047
87109
|
optional: false
|
|
86048
87110
|
}],
|
|
87111
|
+
"meter.getStatus": [{
|
|
87112
|
+
name: "deviceId",
|
|
87113
|
+
form: "single",
|
|
87114
|
+
optional: false
|
|
87115
|
+
}],
|
|
86049
87116
|
"motion.getStatus": [{
|
|
86050
87117
|
name: "deviceId",
|
|
86051
87118
|
form: "single",
|
|
@@ -87653,7 +88720,6 @@ Object.freeze({
|
|
|
87653
88720
|
"network-access": "ingress",
|
|
87654
88721
|
"smtp-provider": "email"
|
|
87655
88722
|
});
|
|
87656
|
-
DeviceType.Camera;
|
|
87657
88723
|
/**
|
|
87658
88724
|
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
87659
88725
|
* `z.infer`, they do not restate the shape.
|
|
@@ -87830,6 +88896,8 @@ new Map(Object.entries({
|
|
|
87830
88896
|
"detection-blind": "device-events",
|
|
87831
88897
|
"motion-noisy": "device-events",
|
|
87832
88898
|
"motion-calm": "device-events",
|
|
88899
|
+
"package-llm-silent": "device-events",
|
|
88900
|
+
"package-llm-answered": "device-events",
|
|
87833
88901
|
"recording-stalled": "device-events",
|
|
87834
88902
|
"recording-healthy": "device-events",
|
|
87835
88903
|
"recording-degraded": "device-events",
|
|
@@ -87913,6 +88981,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
87913
88981
|
label: "Motion without a subject",
|
|
87914
88982
|
kinds: ["motion-noisy", "motion-calm"]
|
|
87915
88983
|
},
|
|
88984
|
+
{
|
|
88985
|
+
id: "package-llm",
|
|
88986
|
+
family: "device-events",
|
|
88987
|
+
label: "Package confirm",
|
|
88988
|
+
kinds: ["package-llm-silent", "package-llm-answered"]
|
|
88989
|
+
},
|
|
87916
88990
|
{
|
|
87917
88991
|
id: "recording",
|
|
87918
88992
|
family: "device-events",
|
|
@@ -88094,6 +89168,8 @@ var DEVICE_EVENTS = [
|
|
|
88094
89168
|
"detection-blind",
|
|
88095
89169
|
"motion-noisy",
|
|
88096
89170
|
"motion-calm",
|
|
89171
|
+
"package-llm-silent",
|
|
89172
|
+
"package-llm-answered",
|
|
88097
89173
|
"recording-stalled",
|
|
88098
89174
|
"recording-healthy",
|
|
88099
89175
|
"recording-degraded",
|
|
@@ -88362,6 +89438,15 @@ new Map([
|
|
|
88362
89438
|
snapshotLabel: "Cameras with constant motion now",
|
|
88363
89439
|
snapshotSection: null
|
|
88364
89440
|
},
|
|
89441
|
+
{
|
|
89442
|
+
id: "package-llm-confirm",
|
|
89443
|
+
family: "device-events",
|
|
89444
|
+
label: "Package confirm (not answering / answering again)",
|
|
89445
|
+
bad: ["package-llm-silent"],
|
|
89446
|
+
recovery: ["package-llm-answered"],
|
|
89447
|
+
snapshotLabel: "Cameras whose package confirm is not answering",
|
|
89448
|
+
snapshotSection: null
|
|
89449
|
+
},
|
|
88365
89450
|
{
|
|
88366
89451
|
id: "recording-health",
|
|
88367
89452
|
family: "device-events",
|