@camstack/addon-provider-reolink 1.2.210 → 1.2.212
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 +57 -0
- package/dist/addon.js +1607 -522
- package/dist/addon.mjs +1607 -522
- package/package.json +4 -2
package/dist/addon.mjs
CHANGED
|
@@ -28,7 +28,7 @@ import { URL as URL$1 } from "url";
|
|
|
28
28
|
import { spawn as spawn$1 } from "node:child_process";
|
|
29
29
|
import os, { tmpdir } from "node:os";
|
|
30
30
|
import { createReadStream } from "node:fs";
|
|
31
|
-
//#region ../types/dist/event-category-
|
|
31
|
+
//#region ../types/dist/event-category-CT-hz5Rb.mjs
|
|
32
32
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
33
33
|
EventCategory["SystemBoot"] = "system.boot";
|
|
34
34
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -354,6 +354,14 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
354
354
|
/** The recovery edge of {@link PipelineMotionNoisy}: the motion fell back, or a detection arrived. */
|
|
355
355
|
EventCategory["PipelineMotionCalm"] = "pipeline.motion-calm";
|
|
356
356
|
/**
|
|
357
|
+
* A camera's package LLM confirm has failed closed three times in a row
|
|
358
|
+
* (D931, 2026-10-26). One edge per camera until
|
|
359
|
+
* {@link PipelinePackageLlmAnswered}.
|
|
360
|
+
*/
|
|
361
|
+
EventCategory["PipelinePackageLlmSilent"] = "pipeline.package-llm-silent";
|
|
362
|
+
/** The recovery edge of {@link PipelinePackageLlmSilent}: a confirm returned yes or no. */
|
|
363
|
+
EventCategory["PipelinePackageLlmAnswered"] = "pipeline.package-llm-answered";
|
|
364
|
+
/**
|
|
357
365
|
* Per-camera pipeline config was mutated by the orchestrator
|
|
358
366
|
* (a settings change via `setAgentInferenceDevices` /
|
|
359
367
|
* `setCameraStepToggle` / `setCameraStepSettings`). Orchestrator
|
|
@@ -5463,7 +5471,7 @@ var ZodIssueCode = {
|
|
|
5463
5471
|
var ZodFirstPartyTypeKind;
|
|
5464
5472
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5465
5473
|
//#endregion
|
|
5466
|
-
//#region ../types/dist/sleep
|
|
5474
|
+
//#region ../types/dist/sleep--jXS9hWo.mjs
|
|
5467
5475
|
/**
|
|
5468
5476
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5469
5477
|
* window to float samples (D455).
|
|
@@ -8021,6 +8029,99 @@ var RecordKindDeclarationSchema = object({
|
|
|
8021
8029
|
}).strict();
|
|
8022
8030
|
/** The kind names: lower-case, dash separated, short enough for an index name. */
|
|
8023
8031
|
var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
|
|
8032
|
+
var READING_QUANTITIES = [
|
|
8033
|
+
"temperature",
|
|
8034
|
+
"pressure",
|
|
8035
|
+
"speed",
|
|
8036
|
+
"precipitation",
|
|
8037
|
+
"precipitation-rate",
|
|
8038
|
+
"length",
|
|
8039
|
+
"area",
|
|
8040
|
+
"illuminance",
|
|
8041
|
+
"irradiance",
|
|
8042
|
+
"power",
|
|
8043
|
+
"energy",
|
|
8044
|
+
"voltage",
|
|
8045
|
+
"current",
|
|
8046
|
+
"apparent-power",
|
|
8047
|
+
"humidity",
|
|
8048
|
+
"percentage",
|
|
8049
|
+
"concentration-volume",
|
|
8050
|
+
"concentration-mass",
|
|
8051
|
+
"concentration-ppb",
|
|
8052
|
+
"volume",
|
|
8053
|
+
"duration",
|
|
8054
|
+
"duration-ms",
|
|
8055
|
+
"information",
|
|
8056
|
+
"sound-level",
|
|
8057
|
+
"signal-level",
|
|
8058
|
+
"colour-temperature",
|
|
8059
|
+
"angle",
|
|
8060
|
+
"bitrate",
|
|
8061
|
+
"rate",
|
|
8062
|
+
"mass",
|
|
8063
|
+
"index",
|
|
8064
|
+
"ratio",
|
|
8065
|
+
"count",
|
|
8066
|
+
"none",
|
|
8067
|
+
"device"
|
|
8068
|
+
];
|
|
8069
|
+
var ReadingQuantitySchema = _enum(READING_QUANTITIES);
|
|
8070
|
+
new Set(READING_QUANTITIES);
|
|
8071
|
+
/**
|
|
8072
|
+
* Stored unit symbol for a quantity. `none` means the number is dimensionless
|
|
8073
|
+
* (or an index / ratio / count). `device` means the cap has no single unit.
|
|
8074
|
+
*/
|
|
8075
|
+
var QUANTITY_BASE = {
|
|
8076
|
+
temperature: "°C",
|
|
8077
|
+
pressure: "hPa",
|
|
8078
|
+
speed: "m/s",
|
|
8079
|
+
precipitation: "mm",
|
|
8080
|
+
"precipitation-rate": "mm/h",
|
|
8081
|
+
length: "m",
|
|
8082
|
+
area: "m²",
|
|
8083
|
+
illuminance: "lx",
|
|
8084
|
+
irradiance: "W/m²",
|
|
8085
|
+
power: "W",
|
|
8086
|
+
energy: "kWh",
|
|
8087
|
+
voltage: "V",
|
|
8088
|
+
current: "A",
|
|
8089
|
+
"apparent-power": "VA",
|
|
8090
|
+
humidity: "%",
|
|
8091
|
+
percentage: "%",
|
|
8092
|
+
"concentration-volume": "ppm",
|
|
8093
|
+
"concentration-mass": "µg/m³",
|
|
8094
|
+
"concentration-ppb": "ppb",
|
|
8095
|
+
volume: "L",
|
|
8096
|
+
duration: "s",
|
|
8097
|
+
"duration-ms": "ms",
|
|
8098
|
+
information: "B",
|
|
8099
|
+
"sound-level": "dBFS",
|
|
8100
|
+
"signal-level": "dBm",
|
|
8101
|
+
"colour-temperature": "mired",
|
|
8102
|
+
angle: "°",
|
|
8103
|
+
bitrate: "kbps",
|
|
8104
|
+
rate: "fps",
|
|
8105
|
+
mass: "kg",
|
|
8106
|
+
index: "none",
|
|
8107
|
+
ratio: "none",
|
|
8108
|
+
count: "none",
|
|
8109
|
+
none: "none",
|
|
8110
|
+
device: "device"
|
|
8111
|
+
};
|
|
8112
|
+
/** The stored unit, or `undefined` when the quantity has no unit symbol. */
|
|
8113
|
+
function baseUnitOf(quantity) {
|
|
8114
|
+
const symbol = QUANTITY_BASE[quantity];
|
|
8115
|
+
if (symbol === "none" || symbol === "device") return void 0;
|
|
8116
|
+
return symbol;
|
|
8117
|
+
}
|
|
8118
|
+
function readingMeta(quantity, extras) {
|
|
8119
|
+
return {
|
|
8120
|
+
quantity,
|
|
8121
|
+
...extras?.precision !== void 0 ? { precision: extras.precision } : {},
|
|
8122
|
+
...extras?.label !== void 0 ? { label: extras.label } : {}
|
|
8123
|
+
};
|
|
8124
|
+
}
|
|
8024
8125
|
/** Shared Zod schemas used across streaming capabilities. */
|
|
8025
8126
|
var CamProfileSchema = _enum([
|
|
8026
8127
|
"high",
|
|
@@ -8048,8 +8149,8 @@ var CamStreamKindSchema = _enum([
|
|
|
8048
8149
|
"derived"
|
|
8049
8150
|
]);
|
|
8050
8151
|
var CamStreamResolutionSchema = object({
|
|
8051
|
-
width: number().int().positive(),
|
|
8052
|
-
height: number().int().positive()
|
|
8152
|
+
width: number().int().positive().meta(readingMeta("none")),
|
|
8153
|
+
height: number().int().positive().meta(readingMeta("none"))
|
|
8053
8154
|
});
|
|
8054
8155
|
var CameraStreamSchema = object({
|
|
8055
8156
|
/** Stable, provider-assigned id unique within the (deviceId) scope. */
|
|
@@ -8601,6 +8702,9 @@ var CAP_NAMES = Object.freeze([
|
|
|
8601
8702
|
"filesystem-browse",
|
|
8602
8703
|
"flood",
|
|
8603
8704
|
"gas",
|
|
8705
|
+
"group-active",
|
|
8706
|
+
"group-commands",
|
|
8707
|
+
"group-status",
|
|
8604
8708
|
"heating-valve",
|
|
8605
8709
|
"humidifier",
|
|
8606
8710
|
"humidity-sensor",
|
|
@@ -8621,6 +8725,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8621
8725
|
"login-method",
|
|
8622
8726
|
"media-player",
|
|
8623
8727
|
"mesh-network",
|
|
8728
|
+
"meter",
|
|
8624
8729
|
"metrics-provider",
|
|
8625
8730
|
"model-convert",
|
|
8626
8731
|
"model-distributor",
|
|
@@ -8730,14 +8835,6 @@ Object.freeze({
|
|
|
8730
8835
|
{
|
|
8731
8836
|
"path": "aqi",
|
|
8732
8837
|
"valueType": "number"
|
|
8733
|
-
},
|
|
8734
|
-
{
|
|
8735
|
-
"path": "unit",
|
|
8736
|
-
"valueType": "string"
|
|
8737
|
-
},
|
|
8738
|
-
{
|
|
8739
|
-
"path": "precision",
|
|
8740
|
-
"valueType": "number"
|
|
8741
8838
|
}
|
|
8742
8839
|
],
|
|
8743
8840
|
"alarm-panel": [
|
|
@@ -8773,20 +8870,10 @@ Object.freeze({
|
|
|
8773
8870
|
"valueType": "boolean"
|
|
8774
8871
|
}
|
|
8775
8872
|
],
|
|
8776
|
-
"ambient-light-sensor": [
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
|
|
8780
|
-
},
|
|
8781
|
-
{
|
|
8782
|
-
"path": "unit",
|
|
8783
|
-
"valueType": "string"
|
|
8784
|
-
},
|
|
8785
|
-
{
|
|
8786
|
-
"path": "precision",
|
|
8787
|
-
"valueType": "number"
|
|
8788
|
-
}
|
|
8789
|
-
],
|
|
8873
|
+
"ambient-light-sensor": [{
|
|
8874
|
+
"path": "lux",
|
|
8875
|
+
"valueType": "number"
|
|
8876
|
+
}],
|
|
8790
8877
|
"audio-metrics": [
|
|
8791
8878
|
{
|
|
8792
8879
|
"path": "ts",
|
|
@@ -8866,6 +8953,10 @@ Object.freeze({
|
|
|
8866
8953
|
"path": "lastUpdated",
|
|
8867
8954
|
"valueType": "number"
|
|
8868
8955
|
},
|
|
8956
|
+
{
|
|
8957
|
+
"path": "volts",
|
|
8958
|
+
"valueType": "number"
|
|
8959
|
+
},
|
|
8869
8960
|
{
|
|
8870
8961
|
"path": "lastContactAt",
|
|
8871
8962
|
"valueType": "number"
|
|
@@ -9108,10 +9199,6 @@ Object.freeze({
|
|
|
9108
9199
|
"datetime"
|
|
9109
9200
|
]
|
|
9110
9201
|
},
|
|
9111
|
-
{
|
|
9112
|
-
"path": "unit",
|
|
9113
|
-
"valueType": "string"
|
|
9114
|
-
},
|
|
9115
9202
|
{
|
|
9116
9203
|
"path": "min",
|
|
9117
9204
|
"valueType": "number"
|
|
@@ -9123,19 +9210,6 @@ Object.freeze({
|
|
|
9123
9210
|
{
|
|
9124
9211
|
"path": "step",
|
|
9125
9212
|
"valueType": "number"
|
|
9126
|
-
},
|
|
9127
|
-
{
|
|
9128
|
-
"path": "precision",
|
|
9129
|
-
"valueType": "number"
|
|
9130
|
-
},
|
|
9131
|
-
{
|
|
9132
|
-
"path": "format",
|
|
9133
|
-
"valueType": "enum",
|
|
9134
|
-
"enumValues": [
|
|
9135
|
-
"date",
|
|
9136
|
-
"time",
|
|
9137
|
-
"datetime"
|
|
9138
|
-
]
|
|
9139
9213
|
}
|
|
9140
9214
|
],
|
|
9141
9215
|
"cover": [
|
|
@@ -9376,13 +9450,8 @@ Object.freeze({
|
|
|
9376
9450
|
"path": "value",
|
|
9377
9451
|
"valueType": "string"
|
|
9378
9452
|
}, {
|
|
9379
|
-
"path": "
|
|
9380
|
-
"valueType": "
|
|
9381
|
-
"enumValues": [
|
|
9382
|
-
"date",
|
|
9383
|
-
"time",
|
|
9384
|
-
"datetime"
|
|
9385
|
-
]
|
|
9453
|
+
"path": "timestamp",
|
|
9454
|
+
"valueType": "boolean"
|
|
9386
9455
|
}],
|
|
9387
9456
|
"event-emitter": [
|
|
9388
9457
|
{
|
|
@@ -9455,6 +9524,24 @@ Object.freeze({
|
|
|
9455
9524
|
"path": "detected",
|
|
9456
9525
|
"valueType": "boolean"
|
|
9457
9526
|
}],
|
|
9527
|
+
"group-active": [{
|
|
9528
|
+
"path": "active",
|
|
9529
|
+
"valueType": "boolean"
|
|
9530
|
+
}],
|
|
9531
|
+
"group-status": [
|
|
9532
|
+
{
|
|
9533
|
+
"path": "included",
|
|
9534
|
+
"valueType": "number"
|
|
9535
|
+
},
|
|
9536
|
+
{
|
|
9537
|
+
"path": "excluded",
|
|
9538
|
+
"valueType": "number"
|
|
9539
|
+
},
|
|
9540
|
+
{
|
|
9541
|
+
"path": "total",
|
|
9542
|
+
"valueType": "number"
|
|
9543
|
+
}
|
|
9544
|
+
],
|
|
9458
9545
|
"heating-valve": [{
|
|
9459
9546
|
"path": "position",
|
|
9460
9547
|
"valueType": "number"
|
|
@@ -9489,20 +9576,10 @@ Object.freeze({
|
|
|
9489
9576
|
"valueType": "number"
|
|
9490
9577
|
}
|
|
9491
9578
|
],
|
|
9492
|
-
"humidity-sensor": [
|
|
9493
|
-
|
|
9494
|
-
|
|
9495
|
-
|
|
9496
|
-
},
|
|
9497
|
-
{
|
|
9498
|
-
"path": "unit",
|
|
9499
|
-
"valueType": "string"
|
|
9500
|
-
},
|
|
9501
|
-
{
|
|
9502
|
-
"path": "precision",
|
|
9503
|
-
"valueType": "number"
|
|
9504
|
-
}
|
|
9505
|
-
],
|
|
9579
|
+
"humidity-sensor": [{
|
|
9580
|
+
"path": "percent",
|
|
9581
|
+
"valueType": "number"
|
|
9582
|
+
}],
|
|
9506
9583
|
"image": [{
|
|
9507
9584
|
"path": "url",
|
|
9508
9585
|
"valueType": "string"
|
|
@@ -9710,6 +9787,10 @@ Object.freeze({
|
|
|
9710
9787
|
]
|
|
9711
9788
|
}
|
|
9712
9789
|
],
|
|
9790
|
+
"meter": [{
|
|
9791
|
+
"path": "value",
|
|
9792
|
+
"valueType": "number"
|
|
9793
|
+
}],
|
|
9713
9794
|
"motion": [
|
|
9714
9795
|
{
|
|
9715
9796
|
"path": "detected",
|
|
@@ -9841,20 +9922,10 @@ Object.freeze({
|
|
|
9841
9922
|
"valueType": "boolean"
|
|
9842
9923
|
}
|
|
9843
9924
|
],
|
|
9844
|
-
"numeric-sensor": [
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
},
|
|
9849
|
-
{
|
|
9850
|
-
"path": "unit",
|
|
9851
|
-
"valueType": "string"
|
|
9852
|
-
},
|
|
9853
|
-
{
|
|
9854
|
-
"path": "precision",
|
|
9855
|
-
"valueType": "number"
|
|
9856
|
-
}
|
|
9857
|
-
],
|
|
9925
|
+
"numeric-sensor": [{
|
|
9926
|
+
"path": "value",
|
|
9927
|
+
"valueType": "number"
|
|
9928
|
+
}],
|
|
9858
9929
|
"pet-feeder": [
|
|
9859
9930
|
{
|
|
9860
9931
|
"path": "foodLevel",
|
|
@@ -9946,14 +10017,6 @@ Object.freeze({
|
|
|
9946
10017
|
{
|
|
9947
10018
|
"path": "powerFactor",
|
|
9948
10019
|
"valueType": "number"
|
|
9949
|
-
},
|
|
9950
|
-
{
|
|
9951
|
-
"path": "unit",
|
|
9952
|
-
"valueType": "string"
|
|
9953
|
-
},
|
|
9954
|
-
{
|
|
9955
|
-
"path": "precision",
|
|
9956
|
-
"valueType": "number"
|
|
9957
10020
|
}
|
|
9958
10021
|
],
|
|
9959
10022
|
"presence": [
|
|
@@ -9982,20 +10045,10 @@ Object.freeze({
|
|
|
9982
10045
|
"valueType": "number"
|
|
9983
10046
|
}
|
|
9984
10047
|
],
|
|
9985
|
-
"pressure-sensor": [
|
|
9986
|
-
|
|
9987
|
-
|
|
9988
|
-
|
|
9989
|
-
},
|
|
9990
|
-
{
|
|
9991
|
-
"path": "unit",
|
|
9992
|
-
"valueType": "string"
|
|
9993
|
-
},
|
|
9994
|
-
{
|
|
9995
|
-
"path": "precision",
|
|
9996
|
-
"valueType": "number"
|
|
9997
|
-
}
|
|
9998
|
-
],
|
|
10048
|
+
"pressure-sensor": [{
|
|
10049
|
+
"path": "hpa",
|
|
10050
|
+
"valueType": "number"
|
|
10051
|
+
}],
|
|
9999
10052
|
"privacy-mask": [{
|
|
10000
10053
|
"path": "enabled",
|
|
10001
10054
|
"valueType": "boolean"
|
|
@@ -10306,20 +10359,10 @@ Object.freeze({
|
|
|
10306
10359
|
"path": "tampered",
|
|
10307
10360
|
"valueType": "boolean"
|
|
10308
10361
|
}],
|
|
10309
|
-
"temperature-sensor": [
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
},
|
|
10314
|
-
{
|
|
10315
|
-
"path": "unit",
|
|
10316
|
-
"valueType": "string"
|
|
10317
|
-
},
|
|
10318
|
-
{
|
|
10319
|
-
"path": "precision",
|
|
10320
|
-
"valueType": "number"
|
|
10321
|
-
}
|
|
10322
|
-
],
|
|
10362
|
+
"temperature-sensor": [{
|
|
10363
|
+
"path": "celsius",
|
|
10364
|
+
"valueType": "number"
|
|
10365
|
+
}],
|
|
10323
10366
|
"update": [
|
|
10324
10367
|
{
|
|
10325
10368
|
"path": "currentVersion",
|
|
@@ -10430,10 +10473,6 @@ Object.freeze({
|
|
|
10430
10473
|
"path": "temperature",
|
|
10431
10474
|
"valueType": "number"
|
|
10432
10475
|
},
|
|
10433
|
-
{
|
|
10434
|
-
"path": "temperatureUnit",
|
|
10435
|
-
"valueType": "string"
|
|
10436
|
-
},
|
|
10437
10476
|
{
|
|
10438
10477
|
"path": "humidity",
|
|
10439
10478
|
"valueType": "number"
|
|
@@ -10442,18 +10481,10 @@ Object.freeze({
|
|
|
10442
10481
|
"path": "pressure",
|
|
10443
10482
|
"valueType": "number"
|
|
10444
10483
|
},
|
|
10445
|
-
{
|
|
10446
|
-
"path": "pressureUnit",
|
|
10447
|
-
"valueType": "string"
|
|
10448
|
-
},
|
|
10449
10484
|
{
|
|
10450
10485
|
"path": "windSpeed",
|
|
10451
10486
|
"valueType": "number"
|
|
10452
10487
|
},
|
|
10453
|
-
{
|
|
10454
|
-
"path": "windSpeedUnit",
|
|
10455
|
-
"valueType": "string"
|
|
10456
|
-
},
|
|
10457
10488
|
{
|
|
10458
10489
|
"path": "windBearing",
|
|
10459
10490
|
"valueType": "number"
|
|
@@ -10541,7 +10572,7 @@ function sleep$2(ms) {
|
|
|
10541
10572
|
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
10542
10573
|
}
|
|
10543
10574
|
//#endregion
|
|
10544
|
-
//#region ../types/dist/err-msg-
|
|
10575
|
+
//#region ../types/dist/err-msg-DIorGYeC.mjs
|
|
10545
10576
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
10546
10577
|
DeviceType["Camera"] = "camera";
|
|
10547
10578
|
DeviceType["Hub"] = "hub";
|
|
@@ -10635,8 +10666,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
10635
10666
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
10636
10667
|
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
10637
10668
|
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
10638
|
-
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10669
|
+
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10670
|
+
* A plug-built dishwasher carries running-state and power instead. */
|
|
10639
10671
|
DeviceType["Dishwasher"] = "dishwasher";
|
|
10672
|
+
/** Washing machine. A plug-built one is running while its power stays
|
|
10673
|
+
* above a threshold. No program cap — that surface is the dishwasher's. */
|
|
10674
|
+
DeviceType["Washer"] = "washer";
|
|
10675
|
+
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
10676
|
+
DeviceType["Dryer"] = "dryer";
|
|
10640
10677
|
return DeviceType;
|
|
10641
10678
|
}({});
|
|
10642
10679
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -10884,7 +10921,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
10884
10921
|
return DeviceRole;
|
|
10885
10922
|
}({});
|
|
10886
10923
|
//#endregion
|
|
10887
|
-
//#region ../types/dist/composition-
|
|
10924
|
+
//#region ../types/dist/composition-Bb8iwHEJ.mjs
|
|
10888
10925
|
/**
|
|
10889
10926
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
10890
10927
|
*
|
|
@@ -11199,6 +11236,13 @@ var table$1 = {
|
|
|
11199
11236
|
apply: () => {
|
|
11200
11237
|
throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
|
|
11201
11238
|
}
|
|
11239
|
+
},
|
|
11240
|
+
schmitt: {
|
|
11241
|
+
minArgs: 3,
|
|
11242
|
+
maxArgs: 5,
|
|
11243
|
+
apply: () => {
|
|
11244
|
+
throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
|
|
11245
|
+
}
|
|
11202
11246
|
}
|
|
11203
11247
|
};
|
|
11204
11248
|
Object.freeze(Object.assign(Object.create(null), table$1));
|
|
@@ -11639,15 +11683,232 @@ function validateExpressionSource(src) {
|
|
|
11639
11683
|
}
|
|
11640
11684
|
return null;
|
|
11641
11685
|
}
|
|
11642
|
-
/**
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
11686
|
+
/**
|
|
11687
|
+
* One power reading becomes a washer, dryer or dishwasher (D944 helper).
|
|
11688
|
+
*
|
|
11689
|
+
* Standby of the control board is under 5 W; a motor or a heater is far
|
|
11690
|
+
* above that, so 5 W is the line. A door lock or a short pump spike is not
|
|
11691
|
+
* a cycle, so running starts only after 30 s above the line.
|
|
11692
|
+
*
|
|
11693
|
+
* A wash, dry or dishwasher pause (soak, heat-wait, heater off between
|
|
11694
|
+
* bursts) lasts several minutes. Ending the cycle on the first dip would
|
|
11695
|
+
* report "finished" in the middle of the program, so the stop dwell is
|
|
11696
|
+
* 10 minutes on every type. The number is stored per type so one machine
|
|
11697
|
+
* can move later without moving the others.
|
|
11698
|
+
*/
|
|
11699
|
+
var APPLIANCE_TEMPLATE_ID = "appliance";
|
|
11700
|
+
var APPLIANCE_KINDS = [
|
|
11701
|
+
"washer",
|
|
11702
|
+
"dryer",
|
|
11703
|
+
"dishwasher"
|
|
11704
|
+
];
|
|
11705
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
11706
|
+
/**
|
|
11707
|
+
* Statistics and integrals over a history read (D944).
|
|
11708
|
+
*
|
|
11709
|
+
* Pure: the same points always give the same answer, so a restart recomputes
|
|
11710
|
+
* rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
|
|
11711
|
+
* never the mean of the samples. A hole with no held value is not integrated
|
|
11712
|
+
* as zero. A truncated read is not published.
|
|
11713
|
+
*/
|
|
11714
|
+
var STATISTIC_STATS = [
|
|
11715
|
+
"min",
|
|
11716
|
+
"max",
|
|
11717
|
+
"mean",
|
|
11718
|
+
"sum",
|
|
11719
|
+
"count",
|
|
11720
|
+
"change"
|
|
11721
|
+
];
|
|
11722
|
+
/**
|
|
11723
|
+
* The range a statistics or integral helper asks history for (D944).
|
|
11724
|
+
*
|
|
11725
|
+
* Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
|
|
11726
|
+
* or 25 hours across a DST change. A tier is eligible only when its retention
|
|
11727
|
+
* is strictly longer than the longest that range can be. Forever qualifies.
|
|
11728
|
+
* Equal to a tier's retention is a refusal, not a short answer.
|
|
11729
|
+
*/
|
|
11730
|
+
var MINUTE_MS = 6e4;
|
|
11731
|
+
var HOUR_MS = 60 * MINUTE_MS;
|
|
11732
|
+
var DAY_MS = 24 * HOUR_MS;
|
|
11733
|
+
var ROLLING_SPANS = [
|
|
11734
|
+
"1h",
|
|
11735
|
+
"24h",
|
|
11736
|
+
"2d",
|
|
11737
|
+
"7d",
|
|
11738
|
+
"30d",
|
|
11739
|
+
"custom"
|
|
11740
|
+
];
|
|
11741
|
+
var CALENDAR_PERIODS = [
|
|
11742
|
+
"today",
|
|
11743
|
+
"yesterday",
|
|
11744
|
+
"this-week",
|
|
11745
|
+
"this-month"
|
|
11746
|
+
];
|
|
11747
|
+
var RESET_PERIODS = [
|
|
11748
|
+
"never",
|
|
11749
|
+
"daily",
|
|
11750
|
+
"weekly",
|
|
11751
|
+
"monthly"
|
|
11752
|
+
];
|
|
11753
|
+
5 * MINUTE_MS;
|
|
11754
|
+
24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
|
|
11755
|
+
7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
|
|
11756
|
+
var rollingWindowObject = object({
|
|
11757
|
+
kind: literal("rolling"),
|
|
11758
|
+
span: _enum(ROLLING_SPANS),
|
|
11759
|
+
customMinutes: number().int().min(5).optional()
|
|
11760
|
+
}).check((payload) => {
|
|
11761
|
+
if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
|
|
11762
|
+
code: "custom",
|
|
11763
|
+
message: "a custom range needs a duration in minutes",
|
|
11764
|
+
path: ["customMinutes"],
|
|
11765
|
+
input: payload.value
|
|
11766
|
+
});
|
|
11767
|
+
if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
|
|
11768
|
+
code: "custom",
|
|
11769
|
+
message: "only a custom range carries a duration",
|
|
11770
|
+
path: ["customMinutes"],
|
|
11771
|
+
input: payload.value
|
|
11772
|
+
});
|
|
11773
|
+
});
|
|
11774
|
+
var RollingWindowSchema = rollingWindowObject;
|
|
11775
|
+
var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
|
|
11776
|
+
kind: literal("calendar"),
|
|
11777
|
+
period: _enum(CALENDAR_PERIODS)
|
|
11778
|
+
})]);
|
|
11779
|
+
/**
|
|
11780
|
+
* A composition source identity: `(addonId, stableId)`, never a numeric device
|
|
11781
|
+
* id. Split out so helper schemas can name it without a cycle through
|
|
11782
|
+
* `composition.ts` (D659).
|
|
11783
|
+
*/
|
|
11646
11784
|
var CompositionSourceRefSchema = object({
|
|
11647
11785
|
/** The owning addon; `stableId` is unique only within it. */
|
|
11648
11786
|
addonId: string().min(1),
|
|
11649
11787
|
stableId: string().min(1)
|
|
11650
11788
|
});
|
|
11789
|
+
var GROUP_TEMPLATE_IDS = [
|
|
11790
|
+
"temperature",
|
|
11791
|
+
"humidity",
|
|
11792
|
+
"pressure",
|
|
11793
|
+
"illuminance",
|
|
11794
|
+
"power",
|
|
11795
|
+
"energy",
|
|
11796
|
+
"battery",
|
|
11797
|
+
"numeric",
|
|
11798
|
+
"motion",
|
|
11799
|
+
"contact",
|
|
11800
|
+
"leak",
|
|
11801
|
+
"smoke",
|
|
11802
|
+
"occupancy",
|
|
11803
|
+
"binary",
|
|
11804
|
+
"switch",
|
|
11805
|
+
"light",
|
|
11806
|
+
"fan",
|
|
11807
|
+
"cover",
|
|
11808
|
+
"valve",
|
|
11809
|
+
"lock",
|
|
11810
|
+
"media-player",
|
|
11811
|
+
"climate"
|
|
11812
|
+
];
|
|
11813
|
+
var NUMERIC_AGGREGATES = [
|
|
11814
|
+
"mean",
|
|
11815
|
+
"median",
|
|
11816
|
+
"min",
|
|
11817
|
+
"max",
|
|
11818
|
+
"sum"
|
|
11819
|
+
];
|
|
11820
|
+
var BINARY_AGGREGATES = ["any", "all"];
|
|
11821
|
+
/** How an actuator group decides it is on. Unknown members never count as off. */
|
|
11822
|
+
var ACTIVE_CRITERIA = ["at-least-one", "all"];
|
|
11823
|
+
var AGGREGATE_REDUCERS = [
|
|
11824
|
+
"mean",
|
|
11825
|
+
"median",
|
|
11826
|
+
"min",
|
|
11827
|
+
"max",
|
|
11828
|
+
"sum",
|
|
11829
|
+
"any",
|
|
11830
|
+
"all"
|
|
11831
|
+
];
|
|
11832
|
+
var GroupTemplateIdSchema = _enum(GROUP_TEMPLATE_IDS);
|
|
11833
|
+
var AggregateReducerSchema = _enum(AGGREGATE_REDUCERS);
|
|
11834
|
+
var ActiveCriterionSchema = _enum(ACTIVE_CRITERIA);
|
|
11835
|
+
/** One member. The field is stored so occupancy can name motion or binary. */
|
|
11836
|
+
var HelperMemberSchema = object({
|
|
11837
|
+
source: CompositionSourceRefSchema,
|
|
11838
|
+
cap: string().min(1),
|
|
11839
|
+
fieldPath: string().min(1)
|
|
11840
|
+
});
|
|
11841
|
+
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
11842
|
+
object({
|
|
11843
|
+
kind: literal("group"),
|
|
11844
|
+
templateId: GroupTemplateIdSchema,
|
|
11845
|
+
aggregate: AggregateReducerSchema,
|
|
11846
|
+
members: array(HelperMemberSchema).min(1).max(64),
|
|
11847
|
+
/** Actuator groups only. Readings leave this unset. */
|
|
11848
|
+
active: ActiveCriterionSchema.optional()
|
|
11849
|
+
}),
|
|
11850
|
+
object({
|
|
11851
|
+
kind: literal("appliance"),
|
|
11852
|
+
templateId: literal(APPLIANCE_TEMPLATE_ID),
|
|
11853
|
+
appliance: _enum(APPLIANCE_KINDS),
|
|
11854
|
+
thresholdWatts: number().positive(),
|
|
11855
|
+
startDwellMs: number().int().nonnegative(),
|
|
11856
|
+
stopDwellMs: number().int().nonnegative(),
|
|
11857
|
+
member: HelperMemberSchema
|
|
11858
|
+
}),
|
|
11859
|
+
object({
|
|
11860
|
+
kind: literal("statistic"),
|
|
11861
|
+
stat: _enum(STATISTIC_STATS),
|
|
11862
|
+
window: StatisticWindowSchema,
|
|
11863
|
+
source: HelperMemberSchema
|
|
11864
|
+
}),
|
|
11865
|
+
object({
|
|
11866
|
+
kind: literal("integral"),
|
|
11867
|
+
reset: _enum(RESET_PERIODS),
|
|
11868
|
+
output: _enum(["energy", "meter"]),
|
|
11869
|
+
source: HelperMemberSchema
|
|
11870
|
+
}),
|
|
11871
|
+
object({
|
|
11872
|
+
kind: literal("threshold"),
|
|
11873
|
+
source: HelperMemberSchema,
|
|
11874
|
+
high: number(),
|
|
11875
|
+
low: number(),
|
|
11876
|
+
onForMs: number().int().nonnegative(),
|
|
11877
|
+
offForMs: number().int().nonnegative()
|
|
11878
|
+
}),
|
|
11879
|
+
object({
|
|
11880
|
+
kind: literal("arithmetic"),
|
|
11881
|
+
op: _enum([
|
|
11882
|
+
"sum",
|
|
11883
|
+
"difference",
|
|
11884
|
+
"ratio"
|
|
11885
|
+
]),
|
|
11886
|
+
inputs: array(HelperMemberSchema).min(2).max(64)
|
|
11887
|
+
}),
|
|
11888
|
+
object({
|
|
11889
|
+
kind: literal("expression"),
|
|
11890
|
+
expr: string().min(1),
|
|
11891
|
+
inputs: array(object({
|
|
11892
|
+
name: string().min(1),
|
|
11893
|
+
member: HelperMemberSchema
|
|
11894
|
+
})).min(1).max(64),
|
|
11895
|
+
output: _enum(["number", "boolean"])
|
|
11896
|
+
}),
|
|
11897
|
+
object({
|
|
11898
|
+
kind: literal("derivative"),
|
|
11899
|
+
window: RollingWindowSchema,
|
|
11900
|
+
per: _enum([
|
|
11901
|
+
"second",
|
|
11902
|
+
"minute",
|
|
11903
|
+
"hour"
|
|
11904
|
+
]),
|
|
11905
|
+
source: HelperMemberSchema
|
|
11906
|
+
})
|
|
11907
|
+
]);
|
|
11908
|
+
/** A composed device's stableId is this prefix plus the block id. */
|
|
11909
|
+
var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
|
|
11910
|
+
/** Longest snippet a `code` source may carry (slice 4). */
|
|
11911
|
+
var MAX_COMPOSITION_CODE_LENGTH = 2e4;
|
|
11651
11912
|
/** One field of one capability of one source device. */
|
|
11652
11913
|
var CompositionFieldReadSchema = object({
|
|
11653
11914
|
source: CompositionSourceRefSchema,
|
|
@@ -11691,7 +11952,59 @@ var CompositionCodeSourceSchema = object({
|
|
|
11691
11952
|
var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
11692
11953
|
CompositionFromSourceSchema,
|
|
11693
11954
|
CompositionExpressionSourceSchema,
|
|
11694
|
-
CompositionCodeSourceSchema
|
|
11955
|
+
CompositionCodeSourceSchema,
|
|
11956
|
+
object({
|
|
11957
|
+
kind: literal("aggregate"),
|
|
11958
|
+
reducer: _enum([
|
|
11959
|
+
"mean",
|
|
11960
|
+
"median",
|
|
11961
|
+
"min",
|
|
11962
|
+
"max",
|
|
11963
|
+
"sum",
|
|
11964
|
+
"any",
|
|
11965
|
+
"all"
|
|
11966
|
+
]),
|
|
11967
|
+
reads: array(CompositionFieldReadSchema).min(1).max(64),
|
|
11968
|
+
/**
|
|
11969
|
+
* Set when the reads are an enum state, not a yes/no: the listed states
|
|
11970
|
+
* count as active (a cover is `open`). A state outside the list is inactive;
|
|
11971
|
+
* a read that is not a state at all is left out. Only `any` / `all` use it.
|
|
11972
|
+
*/
|
|
11973
|
+
activeWhen: array(string().min(1)).min(1).optional(),
|
|
11974
|
+
/** `value` is the reading; the others are the member counts. */
|
|
11975
|
+
publish: _enum([
|
|
11976
|
+
"value",
|
|
11977
|
+
"included",
|
|
11978
|
+
"excluded",
|
|
11979
|
+
"total"
|
|
11980
|
+
]),
|
|
11981
|
+
/** What a `value` publish does when nobody is included. Counts ignore this. */
|
|
11982
|
+
empty: _enum([
|
|
11983
|
+
"null",
|
|
11984
|
+
"omit",
|
|
11985
|
+
"offline"
|
|
11986
|
+
])
|
|
11987
|
+
}),
|
|
11988
|
+
object({
|
|
11989
|
+
kind: literal("series"),
|
|
11990
|
+
read: CompositionFieldReadSchema,
|
|
11991
|
+
/** `value` is the statistic or the integral. The others are the gap counts. */
|
|
11992
|
+
publish: _enum([
|
|
11993
|
+
"value",
|
|
11994
|
+
"included",
|
|
11995
|
+
"excluded",
|
|
11996
|
+
"total"
|
|
11997
|
+
]),
|
|
11998
|
+
/**
|
|
11999
|
+
* What an unanswered `value` does. Counts ignore this. `offline` leaves the
|
|
12000
|
+
* device unseeded until history answers, so a restart cannot publish 0.
|
|
12001
|
+
*/
|
|
12002
|
+
empty: _enum([
|
|
12003
|
+
"null",
|
|
12004
|
+
"omit",
|
|
12005
|
+
"offline"
|
|
12006
|
+
])
|
|
12007
|
+
})
|
|
11695
12008
|
]);
|
|
11696
12009
|
var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
|
|
11697
12010
|
kind: literal("forward"),
|
|
@@ -11737,7 +12050,13 @@ var CompositionSchema = object({
|
|
|
11737
12050
|
kind: literal("existing"),
|
|
11738
12051
|
device: CompositionSourceRefSchema
|
|
11739
12052
|
})]),
|
|
11740
|
-
features: array(CompositionFeatureSchema).min(1).max(16)
|
|
12053
|
+
features: array(CompositionFeatureSchema).min(1).max(16),
|
|
12054
|
+
/**
|
|
12055
|
+
* Present when the block is a helper template. Absent on a free
|
|
12056
|
+
* composition (the room example, device 4603). The editor reads this,
|
|
12057
|
+
* never the compiled features.
|
|
12058
|
+
*/
|
|
12059
|
+
helper: HelperSpecSchema.optional()
|
|
11741
12060
|
}).superRefine((composition, ctx) => {
|
|
11742
12061
|
const seen = /* @__PURE__ */ new Set();
|
|
11743
12062
|
composition.features.forEach((feature, index) => {
|
|
@@ -17414,7 +17733,22 @@ var BrokerProviderInfoSchema = object({
|
|
|
17414
17733
|
*/
|
|
17415
17734
|
interactiveConnectPath: string().min(1).optional(),
|
|
17416
17735
|
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
17417
|
-
interactiveDisconnectPath: string().min(1).optional()
|
|
17736
|
+
interactiveDisconnectPath: string().min(1).optional(),
|
|
17737
|
+
/**
|
|
17738
|
+
* The kind signs in through a server-held interactive session instead of
|
|
17739
|
+
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
17740
|
+
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
17741
|
+
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
17742
|
+
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
17743
|
+
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
17744
|
+
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
17745
|
+
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
17746
|
+
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
17747
|
+
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
17748
|
+
* offers it as the default path; the fields the session fills are
|
|
17749
|
+
* marked `filledBySignIn` in the kind's settings schema.
|
|
17750
|
+
*/
|
|
17751
|
+
signIn: object({ basePath: string().min(1) }).optional()
|
|
17418
17752
|
}))
|
|
17419
17753
|
});
|
|
17420
17754
|
var ListInputSchema = object({
|
|
@@ -18515,7 +18849,12 @@ var CompositionProblemSchema = object({
|
|
|
18515
18849
|
"item-field-reserved",
|
|
18516
18850
|
"target-missing",
|
|
18517
18851
|
"target-unreadable",
|
|
18518
|
-
"not-replaceable"
|
|
18852
|
+
"not-replaceable",
|
|
18853
|
+
"native-capability-replace",
|
|
18854
|
+
"mixed-quantity",
|
|
18855
|
+
"helper-mismatch",
|
|
18856
|
+
"member-constraint",
|
|
18857
|
+
"retention"
|
|
18519
18858
|
]),
|
|
18520
18859
|
path: string(),
|
|
18521
18860
|
message: string()
|
|
@@ -18536,7 +18875,8 @@ var CompositionUnavailableModeSchema = _enum([
|
|
|
18536
18875
|
"null",
|
|
18537
18876
|
"offline",
|
|
18538
18877
|
"unknown",
|
|
18539
|
-
"release"
|
|
18878
|
+
"release",
|
|
18879
|
+
"omit"
|
|
18540
18880
|
]);
|
|
18541
18881
|
/**
|
|
18542
18882
|
* `derived` reads a source · `clock` reads only `now`, recomputed when a sibling
|
|
@@ -18573,9 +18913,11 @@ var CompositionFieldPlanSchema = object({
|
|
|
18573
18913
|
reads: array(CompositionFieldReadSchema)
|
|
18574
18914
|
});
|
|
18575
18915
|
/**
|
|
18576
|
-
* What a feature does to its target (D663): `new` builds a capability on a
|
|
18577
|
-
* device; on an existing device it `add`s a capability the device lacks
|
|
18578
|
-
*
|
|
18916
|
+
* What a feature does to its target (D663, D944): `new` builds a capability on a
|
|
18917
|
+
* new device; on an existing device it `add`s a capability the device lacks.
|
|
18918
|
+
* Replacing a capability the device already serves is refused
|
|
18919
|
+
* (`native-capability-replace`), not planned. `replace` remains on the claim
|
|
18920
|
+
* wire for a claim the hub already holds.
|
|
18579
18921
|
*/
|
|
18580
18922
|
var CompositionFeatureModeSchema = _enum([
|
|
18581
18923
|
"new",
|
|
@@ -18746,8 +19088,392 @@ Object.freeze({
|
|
|
18746
19088
|
latchedAt: {
|
|
18747
19089
|
kind: "fixed",
|
|
18748
19090
|
result: ["number", "null"]
|
|
19091
|
+
},
|
|
19092
|
+
schmitt: {
|
|
19093
|
+
kind: "fixed",
|
|
19094
|
+
result: ["boolean", "null"]
|
|
18749
19095
|
}
|
|
18750
19096
|
});
|
|
19097
|
+
/**
|
|
19098
|
+
* One catalog row per base reading a group helper can build (D944 §3).
|
|
19099
|
+
* Data, not a form default written into the block.
|
|
19100
|
+
*/
|
|
19101
|
+
var NOW$2 = {
|
|
19102
|
+
fieldPath: "lastFetchedAt",
|
|
19103
|
+
expr: "now"
|
|
19104
|
+
};
|
|
19105
|
+
var CHANGED = {
|
|
19106
|
+
fieldPath: "lastChangedAt",
|
|
19107
|
+
expr: "now"
|
|
19108
|
+
};
|
|
19109
|
+
function numeric(input) {
|
|
19110
|
+
return {
|
|
19111
|
+
...input,
|
|
19112
|
+
family: "numeric",
|
|
19113
|
+
deviceType: DeviceType.Sensor,
|
|
19114
|
+
memberDeviceType: DeviceType.Sensor,
|
|
19115
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
19116
|
+
activeWhen: void 0,
|
|
19117
|
+
commandCaps: [],
|
|
19118
|
+
baseUnit: baseUnitOf(input.quantity),
|
|
19119
|
+
aggregates: NUMERIC_AGGREGATES,
|
|
19120
|
+
accept: [{
|
|
19121
|
+
cap: input.outputCap,
|
|
19122
|
+
fieldPath: input.outputField
|
|
19123
|
+
}]
|
|
19124
|
+
};
|
|
19125
|
+
}
|
|
19126
|
+
function binary(input) {
|
|
19127
|
+
return {
|
|
19128
|
+
...input,
|
|
19129
|
+
family: "binary",
|
|
19130
|
+
deviceType: DeviceType.Sensor,
|
|
19131
|
+
memberDeviceType: DeviceType.Sensor,
|
|
19132
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
19133
|
+
activeWhen: void 0,
|
|
19134
|
+
commandCaps: [],
|
|
19135
|
+
quantity: "none",
|
|
19136
|
+
baseUnit: void 0,
|
|
19137
|
+
aggregates: BINARY_AGGREGATES,
|
|
19138
|
+
empty: "offline"
|
|
19139
|
+
};
|
|
19140
|
+
}
|
|
19141
|
+
var GROUP_TEMPLATES = [
|
|
19142
|
+
numeric({
|
|
19143
|
+
id: "temperature",
|
|
19144
|
+
summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
|
|
19145
|
+
role: DeviceRole.TemperatureSensor,
|
|
19146
|
+
outputCap: "temperature-sensor",
|
|
19147
|
+
outputField: "celsius",
|
|
19148
|
+
quantity: "temperature",
|
|
19149
|
+
defaultAggregate: "mean",
|
|
19150
|
+
empty: "null",
|
|
19151
|
+
companions: [NOW$2]
|
|
19152
|
+
}),
|
|
19153
|
+
numeric({
|
|
19154
|
+
id: "humidity",
|
|
19155
|
+
summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
19156
|
+
role: DeviceRole.HumiditySensor,
|
|
19157
|
+
outputCap: "humidity-sensor",
|
|
19158
|
+
outputField: "percent",
|
|
19159
|
+
quantity: "humidity",
|
|
19160
|
+
defaultAggregate: "mean",
|
|
19161
|
+
empty: "null",
|
|
19162
|
+
companions: [NOW$2]
|
|
19163
|
+
}),
|
|
19164
|
+
numeric({
|
|
19165
|
+
id: "pressure",
|
|
19166
|
+
summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
|
|
19167
|
+
role: DeviceRole.PressureSensor,
|
|
19168
|
+
outputCap: "pressure-sensor",
|
|
19169
|
+
outputField: "hpa",
|
|
19170
|
+
quantity: "pressure",
|
|
19171
|
+
defaultAggregate: "mean",
|
|
19172
|
+
empty: "null",
|
|
19173
|
+
companions: [NOW$2]
|
|
19174
|
+
}),
|
|
19175
|
+
numeric({
|
|
19176
|
+
id: "illuminance",
|
|
19177
|
+
summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
|
|
19178
|
+
role: DeviceRole.AmbientLightSensor,
|
|
19179
|
+
outputCap: "ambient-light-sensor",
|
|
19180
|
+
outputField: "lux",
|
|
19181
|
+
quantity: "illuminance",
|
|
19182
|
+
defaultAggregate: "mean",
|
|
19183
|
+
empty: "null",
|
|
19184
|
+
companions: [NOW$2]
|
|
19185
|
+
}),
|
|
19186
|
+
numeric({
|
|
19187
|
+
id: "power",
|
|
19188
|
+
summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
|
|
19189
|
+
role: DeviceRole.PowerSensor,
|
|
19190
|
+
outputCap: "power-meter",
|
|
19191
|
+
outputField: "watts",
|
|
19192
|
+
quantity: "power",
|
|
19193
|
+
defaultAggregate: "sum",
|
|
19194
|
+
empty: "omit",
|
|
19195
|
+
companions: [NOW$2]
|
|
19196
|
+
}),
|
|
19197
|
+
numeric({
|
|
19198
|
+
id: "energy",
|
|
19199
|
+
summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
|
|
19200
|
+
role: DeviceRole.EnergySensor,
|
|
19201
|
+
outputCap: "power-meter",
|
|
19202
|
+
outputField: "kwhTotal",
|
|
19203
|
+
quantity: "energy",
|
|
19204
|
+
defaultAggregate: "sum",
|
|
19205
|
+
empty: "omit",
|
|
19206
|
+
companions: [NOW$2]
|
|
19207
|
+
}),
|
|
19208
|
+
numeric({
|
|
19209
|
+
id: "battery",
|
|
19210
|
+
summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
19211
|
+
role: DeviceRole.BatterySensor,
|
|
19212
|
+
outputCap: "battery",
|
|
19213
|
+
outputField: "percentage",
|
|
19214
|
+
quantity: "percentage",
|
|
19215
|
+
defaultAggregate: "mean",
|
|
19216
|
+
empty: "null",
|
|
19217
|
+
companions: [
|
|
19218
|
+
{
|
|
19219
|
+
fieldPath: "charging",
|
|
19220
|
+
expr: "\"unknown\""
|
|
19221
|
+
},
|
|
19222
|
+
{
|
|
19223
|
+
fieldPath: "sleeping",
|
|
19224
|
+
expr: "false"
|
|
19225
|
+
},
|
|
19226
|
+
{
|
|
19227
|
+
fieldPath: "lastUpdated",
|
|
19228
|
+
expr: "now"
|
|
19229
|
+
}
|
|
19230
|
+
]
|
|
19231
|
+
}),
|
|
19232
|
+
numeric({
|
|
19233
|
+
id: "numeric",
|
|
19234
|
+
summary: "A numeric reading in the members’ shared quantity. Mixed quantities are refused. Nobody reporting publishes null, not 0.",
|
|
19235
|
+
role: DeviceRole.NumericSensor,
|
|
19236
|
+
outputCap: "numeric-sensor",
|
|
19237
|
+
outputField: "value",
|
|
19238
|
+
quantity: "device",
|
|
19239
|
+
defaultAggregate: "mean",
|
|
19240
|
+
empty: "null",
|
|
19241
|
+
companions: [NOW$2]
|
|
19242
|
+
}),
|
|
19243
|
+
binary({
|
|
19244
|
+
id: "motion",
|
|
19245
|
+
summary: "Any or all motion. Offline and unknown members are left out, never read as off. Nobody reporting takes the device offline.",
|
|
19246
|
+
role: DeviceRole.MotionSensor,
|
|
19247
|
+
outputCap: "motion",
|
|
19248
|
+
outputField: "detected",
|
|
19249
|
+
defaultAggregate: "any",
|
|
19250
|
+
accept: [{
|
|
19251
|
+
cap: "motion",
|
|
19252
|
+
fieldPath: "detected"
|
|
19253
|
+
}],
|
|
19254
|
+
companions: [{
|
|
19255
|
+
fieldPath: "lastDetectedAt",
|
|
19256
|
+
expr: "null"
|
|
19257
|
+
}, {
|
|
19258
|
+
fieldPath: "autoClearAfterMs",
|
|
19259
|
+
expr: "null"
|
|
19260
|
+
}]
|
|
19261
|
+
}),
|
|
19262
|
+
binary({
|
|
19263
|
+
id: "contact",
|
|
19264
|
+
summary: "Any or all open contacts. Offline and unknown members are left out, never read as closed.",
|
|
19265
|
+
role: DeviceRole.ContactSensor,
|
|
19266
|
+
outputCap: "contact",
|
|
19267
|
+
outputField: "entryOpen",
|
|
19268
|
+
defaultAggregate: "any",
|
|
19269
|
+
accept: [{
|
|
19270
|
+
cap: "contact",
|
|
19271
|
+
fieldPath: "entryOpen"
|
|
19272
|
+
}],
|
|
19273
|
+
companions: [CHANGED]
|
|
19274
|
+
}),
|
|
19275
|
+
binary({
|
|
19276
|
+
id: "leak",
|
|
19277
|
+
summary: "Any or all leak detections. Offline and unknown members are left out, never read as dry.",
|
|
19278
|
+
role: DeviceRole.LeakSensor,
|
|
19279
|
+
outputCap: "flood",
|
|
19280
|
+
outputField: "flooded",
|
|
19281
|
+
defaultAggregate: "any",
|
|
19282
|
+
accept: [{
|
|
19283
|
+
cap: "flood",
|
|
19284
|
+
fieldPath: "flooded"
|
|
19285
|
+
}],
|
|
19286
|
+
companions: [CHANGED]
|
|
19287
|
+
}),
|
|
19288
|
+
binary({
|
|
19289
|
+
id: "smoke",
|
|
19290
|
+
summary: "Any or all smoke detections. Offline and unknown members are left out, never read as clear.",
|
|
19291
|
+
role: DeviceRole.SmokeSensor,
|
|
19292
|
+
outputCap: "smoke",
|
|
19293
|
+
outputField: "detected",
|
|
19294
|
+
defaultAggregate: "any",
|
|
19295
|
+
accept: [{
|
|
19296
|
+
cap: "smoke",
|
|
19297
|
+
fieldPath: "detected"
|
|
19298
|
+
}],
|
|
19299
|
+
companions: [CHANGED]
|
|
19300
|
+
}),
|
|
19301
|
+
binary({
|
|
19302
|
+
id: "occupancy",
|
|
19303
|
+
summary: "Any or all of motion or binary sensors, published as a binary. Presence zones are not aggregated. Nobody reporting is offline, not off.",
|
|
19304
|
+
role: DeviceRole.BinarySensor,
|
|
19305
|
+
outputCap: "binary",
|
|
19306
|
+
outputField: "on",
|
|
19307
|
+
defaultAggregate: "any",
|
|
19308
|
+
accept: [{
|
|
19309
|
+
cap: "motion",
|
|
19310
|
+
fieldPath: "detected"
|
|
19311
|
+
}, {
|
|
19312
|
+
cap: "binary",
|
|
19313
|
+
fieldPath: "on"
|
|
19314
|
+
}],
|
|
19315
|
+
companions: [CHANGED]
|
|
19316
|
+
}),
|
|
19317
|
+
binary({
|
|
19318
|
+
id: "binary",
|
|
19319
|
+
summary: "Any or all generic binary sensors. Offline and unknown members are left out, never read as off.",
|
|
19320
|
+
role: DeviceRole.BinarySensor,
|
|
19321
|
+
outputCap: "binary",
|
|
19322
|
+
outputField: "on",
|
|
19323
|
+
defaultAggregate: "any",
|
|
19324
|
+
accept: [{
|
|
19325
|
+
cap: "binary",
|
|
19326
|
+
fieldPath: "on"
|
|
19327
|
+
}],
|
|
19328
|
+
companions: [CHANGED]
|
|
19329
|
+
}),
|
|
19330
|
+
actuator({
|
|
19331
|
+
id: "switch",
|
|
19332
|
+
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.",
|
|
19333
|
+
deviceType: DeviceType.Switch,
|
|
19334
|
+
outputCap: "switch",
|
|
19335
|
+
outputField: "on",
|
|
19336
|
+
member: {
|
|
19337
|
+
cap: "switch",
|
|
19338
|
+
fieldPath: "on"
|
|
19339
|
+
},
|
|
19340
|
+
commandCaps: ["switch"]
|
|
19341
|
+
}),
|
|
19342
|
+
actuator({
|
|
19343
|
+
id: "light",
|
|
19344
|
+
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.",
|
|
19345
|
+
deviceType: DeviceType.Light,
|
|
19346
|
+
outputCap: "switch",
|
|
19347
|
+
outputField: "on",
|
|
19348
|
+
member: {
|
|
19349
|
+
cap: "switch",
|
|
19350
|
+
fieldPath: "on"
|
|
19351
|
+
},
|
|
19352
|
+
commandCaps: [
|
|
19353
|
+
"switch",
|
|
19354
|
+
"brightness",
|
|
19355
|
+
"color"
|
|
19356
|
+
]
|
|
19357
|
+
}),
|
|
19358
|
+
actuator({
|
|
19359
|
+
id: "fan",
|
|
19360
|
+
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.",
|
|
19361
|
+
deviceType: DeviceType.Fan,
|
|
19362
|
+
outputCap: "switch",
|
|
19363
|
+
outputField: "on",
|
|
19364
|
+
member: {
|
|
19365
|
+
cap: "switch",
|
|
19366
|
+
fieldPath: "on"
|
|
19367
|
+
},
|
|
19368
|
+
commandCaps: ["switch", "fan-control"]
|
|
19369
|
+
}),
|
|
19370
|
+
actuator({
|
|
19371
|
+
id: "cover",
|
|
19372
|
+
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.",
|
|
19373
|
+
deviceType: DeviceType.Cover,
|
|
19374
|
+
outputCap: "group-active",
|
|
19375
|
+
outputField: "active",
|
|
19376
|
+
member: {
|
|
19377
|
+
cap: "cover",
|
|
19378
|
+
fieldPath: "state"
|
|
19379
|
+
},
|
|
19380
|
+
activeWhen: [
|
|
19381
|
+
"open",
|
|
19382
|
+
"opening",
|
|
19383
|
+
"stopped"
|
|
19384
|
+
],
|
|
19385
|
+
commandCaps: ["cover"]
|
|
19386
|
+
}),
|
|
19387
|
+
actuator({
|
|
19388
|
+
id: "valve",
|
|
19389
|
+
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.",
|
|
19390
|
+
deviceType: DeviceType.Valve,
|
|
19391
|
+
outputCap: "group-active",
|
|
19392
|
+
outputField: "active",
|
|
19393
|
+
member: {
|
|
19394
|
+
cap: "valve",
|
|
19395
|
+
fieldPath: "state"
|
|
19396
|
+
},
|
|
19397
|
+
activeWhen: [
|
|
19398
|
+
"open",
|
|
19399
|
+
"opening",
|
|
19400
|
+
"stopped"
|
|
19401
|
+
],
|
|
19402
|
+
commandCaps: ["valve"]
|
|
19403
|
+
}),
|
|
19404
|
+
actuator({
|
|
19405
|
+
id: "lock",
|
|
19406
|
+
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.",
|
|
19407
|
+
deviceType: DeviceType.Lock,
|
|
19408
|
+
outputCap: "group-active",
|
|
19409
|
+
outputField: "active",
|
|
19410
|
+
member: {
|
|
19411
|
+
cap: "lock-control",
|
|
19412
|
+
fieldPath: "state"
|
|
19413
|
+
},
|
|
19414
|
+
activeWhen: ["locked"],
|
|
19415
|
+
commandCaps: ["lock-control"]
|
|
19416
|
+
}),
|
|
19417
|
+
actuator({
|
|
19418
|
+
id: "media-player",
|
|
19419
|
+
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.",
|
|
19420
|
+
deviceType: DeviceType.MediaPlayer,
|
|
19421
|
+
outputCap: "group-active",
|
|
19422
|
+
outputField: "active",
|
|
19423
|
+
member: {
|
|
19424
|
+
cap: "media-player",
|
|
19425
|
+
fieldPath: "state"
|
|
19426
|
+
},
|
|
19427
|
+
activeWhen: ["playing", "buffering"],
|
|
19428
|
+
commandCaps: ["media-player"]
|
|
19429
|
+
}),
|
|
19430
|
+
actuator({
|
|
19431
|
+
id: "climate",
|
|
19432
|
+
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.",
|
|
19433
|
+
deviceType: DeviceType.Thermostat,
|
|
19434
|
+
memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
|
|
19435
|
+
outputCap: "group-active",
|
|
19436
|
+
outputField: "active",
|
|
19437
|
+
member: {
|
|
19438
|
+
cap: "climate-control",
|
|
19439
|
+
fieldPath: "mode"
|
|
19440
|
+
},
|
|
19441
|
+
activeWhen: [
|
|
19442
|
+
"heat",
|
|
19443
|
+
"cool",
|
|
19444
|
+
"auto",
|
|
19445
|
+
"heat_cool",
|
|
19446
|
+
"fan_only",
|
|
19447
|
+
"dry"
|
|
19448
|
+
],
|
|
19449
|
+
commandCaps: ["climate-control"]
|
|
19450
|
+
})
|
|
19451
|
+
];
|
|
19452
|
+
function actuator(input) {
|
|
19453
|
+
return {
|
|
19454
|
+
id: input.id,
|
|
19455
|
+
summary: input.summary,
|
|
19456
|
+
deviceType: input.deviceType,
|
|
19457
|
+
outputCap: input.outputCap,
|
|
19458
|
+
outputField: input.outputField,
|
|
19459
|
+
commandCaps: input.commandCaps,
|
|
19460
|
+
family: "actuator",
|
|
19461
|
+
role: void 0,
|
|
19462
|
+
memberDeviceType: input.deviceType,
|
|
19463
|
+
memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
|
|
19464
|
+
activeWhen: input.activeWhen,
|
|
19465
|
+
quantity: "none",
|
|
19466
|
+
baseUnit: void 0,
|
|
19467
|
+
aggregates: BINARY_AGGREGATES,
|
|
19468
|
+
defaultAggregate: "any",
|
|
19469
|
+
empty: "offline",
|
|
19470
|
+
accept: [input.member],
|
|
19471
|
+
companions: [CHANGED]
|
|
19472
|
+
};
|
|
19473
|
+
}
|
|
19474
|
+
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
19475
|
+
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
19476
|
+
DeviceType.Camera;
|
|
18751
19477
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
18752
19478
|
/**
|
|
18753
19479
|
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
@@ -20574,12 +21300,12 @@ function buildDeviceAdminUrl(parts) {
|
|
|
20574
21300
|
* providers detect renames by matching `uniqueId` and patch `id`
|
|
20575
21301
|
* in-place so dispatch keeps working.
|
|
20576
21302
|
*
|
|
20577
|
-
* **
|
|
20578
|
-
*
|
|
20579
|
-
*
|
|
20580
|
-
*
|
|
20581
|
-
* (`ha-domain-mapping`)
|
|
20582
|
-
* stored after that point.
|
|
21303
|
+
* **Display unit and precision are NOT stored here.** They are a read-time
|
|
21304
|
+
* preference (D944). A polymorphic reading stores its `quantity` here once,
|
|
21305
|
+
* at adopt, and a date/time value stores `dateTimeShape` the same way —
|
|
21306
|
+
* neither is repeated on each sample. The `device_class` tag is an
|
|
21307
|
+
* HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
|
|
21308
|
+
* to derive the CamStack `DeviceRole`; it is never stored after that point.
|
|
20583
21309
|
*
|
|
20584
21310
|
* The `raw` escape hatch carries provider-specific attributes that don't
|
|
20585
21311
|
* fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
|
|
@@ -20593,6 +21319,14 @@ var SourceInfoSchema = object({
|
|
|
20593
21319
|
/** Immutable upstream identifier when available (HA `unique_id`, … ).
|
|
20594
21320
|
* Used to detect rename when `id` changes. */
|
|
20595
21321
|
uniqueId: string().optional(),
|
|
21322
|
+
/** Quantity of a polymorphic reading, written once at adopt (D944). */
|
|
21323
|
+
quantity: ReadingQuantitySchema.optional(),
|
|
21324
|
+
/** Date / time / datetime shape of a string reading, written once at adopt. */
|
|
21325
|
+
dateTimeShape: _enum([
|
|
21326
|
+
"date",
|
|
21327
|
+
"time",
|
|
21328
|
+
"datetime"
|
|
21329
|
+
]).optional(),
|
|
20596
21330
|
/** Provider-specific extras that don't fit the structured slots.
|
|
20597
21331
|
* Whitelist what goes here per provider — do NOT dump entire HA
|
|
20598
21332
|
* attribute blobs (would explode the meta JSON column). */
|
|
@@ -41697,7 +42431,8 @@ method(object({
|
|
|
41697
42431
|
"enum"
|
|
41698
42432
|
]),
|
|
41699
42433
|
enumValues: array(string()).optional(),
|
|
41700
|
-
item: boolean().optional()
|
|
42434
|
+
item: boolean().optional(),
|
|
42435
|
+
quantity: ReadingQuantitySchema.optional()
|
|
41701
42436
|
})).readonly(),
|
|
41702
42437
|
itemArray: object({
|
|
41703
42438
|
path: string(),
|
|
@@ -44098,7 +44833,7 @@ var NetworkAccessStatusSchema = object({
|
|
|
44098
44833
|
});
|
|
44099
44834
|
/**
|
|
44100
44835
|
* Optional, richer endpoint shape returned by providers that expose
|
|
44101
|
-
* MORE than one ingress concurrently (Tailscale
|
|
44836
|
+
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
44102
44837
|
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
44103
44838
|
* the originating provider config (mode + sourcePort) so the
|
|
44104
44839
|
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
@@ -45581,8 +46316,8 @@ var MaskPolygonShapeSchema = object({
|
|
|
45581
46316
|
/** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
|
|
45582
46317
|
var MaskGridShapeSchema = object({
|
|
45583
46318
|
kind: literal("grid"),
|
|
45584
|
-
gridWidth: number(),
|
|
45585
|
-
gridHeight: number(),
|
|
46319
|
+
gridWidth: number().meta(readingMeta("none")),
|
|
46320
|
+
gridHeight: number().meta(readingMeta("none")),
|
|
45586
46321
|
cells: array(boolean())
|
|
45587
46322
|
});
|
|
45588
46323
|
discriminatedUnion("kind", [
|
|
@@ -45694,6 +46429,8 @@ var NcSystemEventKindSchema = _enum([
|
|
|
45694
46429
|
"detection-blind",
|
|
45695
46430
|
"motion-noisy",
|
|
45696
46431
|
"motion-calm",
|
|
46432
|
+
"package-llm-silent",
|
|
46433
|
+
"package-llm-answered",
|
|
45697
46434
|
"recording-stalled",
|
|
45698
46435
|
"recording-healthy",
|
|
45699
46436
|
"recording-degraded",
|
|
@@ -46783,7 +47520,20 @@ var NcRuleInputSchema = object({
|
|
|
46783
47520
|
* save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
|
|
46784
47521
|
* path does not run Zod defaults.
|
|
46785
47522
|
*/
|
|
46786
|
-
groupingSec: number().int().min(5).max(60).optional()
|
|
47523
|
+
groupingSec: number().int().min(5).max(60).optional(),
|
|
47524
|
+
/**
|
|
47525
|
+
* ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
|
|
47526
|
+
* carries "Correct" / "Wrong" buttons (and, where the channel has room, a
|
|
47527
|
+
* "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
|
|
47528
|
+
* same mark the Library writes, and records whose notification it was and
|
|
47529
|
+
* when. A channel with no buttons gets the same two choices as links.
|
|
47530
|
+
*
|
|
47531
|
+
* `.optional()`, never `.default()` (a Zod default does not run on the addon
|
|
47532
|
+
* cap path): absent and `false` both mean off, which is what every rule
|
|
47533
|
+
* authored before this field meant. An editor turns it off by writing
|
|
47534
|
+
* `false`, so it needs no `clear` key.
|
|
47535
|
+
*/
|
|
47536
|
+
askVerdict: boolean().optional()
|
|
46787
47537
|
});
|
|
46788
47538
|
/**
|
|
46789
47539
|
* Partial patch for `updateRule` — any subset of the input fields, plus the
|
|
@@ -57307,6 +58057,28 @@ var PackageVersionInfoSchema = object({
|
|
|
57307
58057
|
deprecated: string().optional(),
|
|
57308
58058
|
distTags: array(string()).readonly()
|
|
57309
58059
|
});
|
|
58060
|
+
/**
|
|
58061
|
+
* One published document. `ok` is markdown from the registry. `missing` means
|
|
58062
|
+
* the registry answered and the file was not there. `error` is a registry or
|
|
58063
|
+
* tarball failure — never an empty string standing in for either.
|
|
58064
|
+
*/
|
|
58065
|
+
var PackageDocBodySchema = discriminatedUnion("status", [
|
|
58066
|
+
object({
|
|
58067
|
+
status: literal("ok"),
|
|
58068
|
+
markdown: string()
|
|
58069
|
+
}),
|
|
58070
|
+
object({ status: literal("missing") }),
|
|
58071
|
+
object({
|
|
58072
|
+
status: literal("error"),
|
|
58073
|
+
message: string()
|
|
58074
|
+
})
|
|
58075
|
+
]);
|
|
58076
|
+
var PackageDocsSchema = object({
|
|
58077
|
+
name: string(),
|
|
58078
|
+
version: string(),
|
|
58079
|
+
readme: PackageDocBodySchema,
|
|
58080
|
+
changelog: PackageDocBodySchema
|
|
58081
|
+
});
|
|
57310
58082
|
var SearchResultSchema = object({
|
|
57311
58083
|
name: string(),
|
|
57312
58084
|
version: string(),
|
|
@@ -57525,7 +58297,10 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
57525
58297
|
}), object({ success: literal(true) }), {
|
|
57526
58298
|
kind: "mutation",
|
|
57527
58299
|
auth: "admin"
|
|
57528
|
-
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
|
|
58300
|
+
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
|
|
58301
|
+
name: string().min(1),
|
|
58302
|
+
version: string().min(1).optional()
|
|
58303
|
+
}), PackageDocsSchema, { auth: "admin" }), method(object({ addonId: string() }), RestartAddonResultSchema, {
|
|
57529
58304
|
kind: "mutation",
|
|
57530
58305
|
auth: "admin"
|
|
57531
58306
|
}), method(object({ packageName: string() }), object({ success: literal(true) }), {
|
|
@@ -57584,27 +58359,17 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
57584
58359
|
*/
|
|
57585
58360
|
var AirQualitySensorStatusSchema = object({
|
|
57586
58361
|
/** Carbon dioxide concentration in ppm. */
|
|
57587
|
-
co2Ppm: number().min(0).optional(),
|
|
58362
|
+
co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
57588
58363
|
/** Total volatile organic compounds in ppb. */
|
|
57589
|
-
vocPpb: number().min(0).optional(),
|
|
58364
|
+
vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
57590
58365
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
57591
|
-
pm25: number().min(0).optional(),
|
|
58366
|
+
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
57592
58367
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
57593
|
-
pm10: number().min(0).optional(),
|
|
58368
|
+
pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
57594
58369
|
/** Composite AQI value (typically 0..500). */
|
|
57595
|
-
aqi: number().optional(),
|
|
58370
|
+
aqi: number().optional().meta(readingMeta("index")),
|
|
57596
58371
|
/** Ms epoch when the slice was last updated. */
|
|
57597
|
-
lastFetchedAt: number()
|
|
57598
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
57599
|
-
* `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
|
|
57600
|
-
* upstream `sensor.*` entity surfaces ONE device_class, so one unit
|
|
57601
|
-
* per slice is unambiguous. */
|
|
57602
|
-
unit: string().optional(),
|
|
57603
|
-
/** Suggested decimal places for numeric display.
|
|
57604
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
57605
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
57606
|
-
* auto-formatting when absent. */
|
|
57607
|
-
precision: number().int().min(0).max(10).optional()
|
|
58372
|
+
lastFetchedAt: number()
|
|
57608
58373
|
});
|
|
57609
58374
|
var airQualitySensorCapability = {
|
|
57610
58375
|
name: "air-quality-sensor",
|
|
@@ -57641,12 +58406,12 @@ var airQualitySensorCapability = {
|
|
|
57641
58406
|
},
|
|
57642
58407
|
pm25: {
|
|
57643
58408
|
kind: "gauge",
|
|
57644
|
-
unit: "
|
|
58409
|
+
unit: "µg/m³",
|
|
57645
58410
|
deadband: 1
|
|
57646
58411
|
},
|
|
57647
58412
|
pm10: {
|
|
57648
58413
|
kind: "gauge",
|
|
57649
|
-
unit: "
|
|
58414
|
+
unit: "µg/m³",
|
|
57650
58415
|
deadband: 1
|
|
57651
58416
|
},
|
|
57652
58417
|
aqi: {
|
|
@@ -57664,22 +58429,13 @@ var airQualitySensorCapability = {
|
|
|
57664
58429
|
* entries with `device_class: illuminance`.
|
|
57665
58430
|
*/
|
|
57666
58431
|
var AmbientLightSensorStatusSchema = object({
|
|
57667
|
-
/** Current illuminance in lux (lx). */
|
|
57668
|
-
lux: number().min(0).meta({
|
|
57669
|
-
|
|
58432
|
+
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
58433
|
+
lux: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
58434
|
+
precision: 0,
|
|
57670
58435
|
label: "Illuminance"
|
|
57671
|
-
}),
|
|
58436
|
+
})),
|
|
57672
58437
|
/** Ms epoch when the slice was last updated. */
|
|
57673
|
-
lastFetchedAt: number()
|
|
57674
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
57675
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
57676
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
57677
|
-
unit: string().optional(),
|
|
57678
|
-
/** Suggested decimal places for numeric display.
|
|
57679
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
57680
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
57681
|
-
* auto-formatting when absent. */
|
|
57682
|
-
precision: number().int().min(0).max(10).optional()
|
|
58438
|
+
lastFetchedAt: number()
|
|
57683
58439
|
});
|
|
57684
58440
|
var ambientLightSensorCapability = {
|
|
57685
58441
|
name: "ambient-light-sensor",
|
|
@@ -57711,12 +58467,7 @@ var ambientLightSensorCapability = {
|
|
|
57711
58467
|
} } },
|
|
57712
58468
|
/** Clock fields: written, but excluded from the compare that decides
|
|
57713
58469
|
* whether persisting is worth a SQLite commit. */
|
|
57714
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
57715
|
-
customize: {
|
|
57716
|
-
version: 1,
|
|
57717
|
-
advanced: "derive",
|
|
57718
|
-
targets: []
|
|
57719
|
-
}
|
|
58470
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
57720
58471
|
};
|
|
57721
58472
|
/**
|
|
57722
58473
|
* Per-class audio metrics aggregated over a sliding window.
|
|
@@ -57943,10 +58694,7 @@ var BatteryStatusSchema = object({
|
|
|
57943
58694
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
57944
58695
|
* declines to band a non-finite reading, which is the correct shape.
|
|
57945
58696
|
*/
|
|
57946
|
-
percentage: number().min(0).max(100).nullable().meta({
|
|
57947
|
-
unit: "%",
|
|
57948
|
-
label: "Charge"
|
|
57949
|
-
}),
|
|
58697
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
57950
58698
|
/**
|
|
57951
58699
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
57952
58700
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -57973,7 +58721,14 @@ var BatteryStatusSchema = object({
|
|
|
57973
58721
|
*/
|
|
57974
58722
|
sleeping: boolean().meta({ label: "Sleeping" }),
|
|
57975
58723
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
57976
|
-
lastUpdated: number(),
|
|
58724
|
+
lastUpdated: number().meta(readingMeta("none")),
|
|
58725
|
+
/**
|
|
58726
|
+
* Battery voltage in volts, for a cell the firmware reports as voltage
|
|
58727
|
+
* rather than a percentage (Ecowitt capacitor packs). Absent on cameras
|
|
58728
|
+
* that only report charge. A second reading on this cap, not a second
|
|
58729
|
+
* `numeric-sensor` on the same device.
|
|
58730
|
+
*/
|
|
58731
|
+
volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
57977
58732
|
/**
|
|
57978
58733
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
57979
58734
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -58020,12 +58775,12 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
58020
58775
|
DeviceType.Switch,
|
|
58021
58776
|
DeviceType.PetFeeder,
|
|
58022
58777
|
DeviceType.Container,
|
|
58023
|
-
DeviceType.Thermostat
|
|
58778
|
+
DeviceType.Thermostat,
|
|
58779
|
+
DeviceType.EventEmitter
|
|
58024
58780
|
];
|
|
58025
58781
|
/** Generic Customize target for the percentage reading (D936). */
|
|
58026
58782
|
var batteryCustomize = {
|
|
58027
58783
|
version: 1,
|
|
58028
|
-
advanced: "derive",
|
|
58029
58784
|
targets: [{
|
|
58030
58785
|
id: "percentage",
|
|
58031
58786
|
cardinality: "scalar",
|
|
@@ -58034,7 +58789,7 @@ var batteryCustomize = {
|
|
|
58034
58789
|
stateField: "percentage"
|
|
58035
58790
|
},
|
|
58036
58791
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
58037
|
-
apply: "add
|
|
58792
|
+
apply: "add",
|
|
58038
58793
|
path: "percentage",
|
|
58039
58794
|
sources: [{
|
|
58040
58795
|
kind: "field",
|
|
@@ -58155,10 +58910,16 @@ onStatusChanged: { data: object({
|
|
|
58155
58910
|
/**
|
|
58156
58911
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
58157
58912
|
*/
|
|
58158
|
-
history: { fields: {
|
|
58159
|
-
|
|
58160
|
-
|
|
58161
|
-
|
|
58913
|
+
history: { fields: {
|
|
58914
|
+
percentage: {
|
|
58915
|
+
kind: "gauge",
|
|
58916
|
+
unit: "%"
|
|
58917
|
+
},
|
|
58918
|
+
volts: {
|
|
58919
|
+
kind: "gauge",
|
|
58920
|
+
unit: "V"
|
|
58921
|
+
}
|
|
58922
|
+
} },
|
|
58162
58923
|
customize: batteryCustomize,
|
|
58163
58924
|
/** Clock fields: written, but excluded from the compare that decides
|
|
58164
58925
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -58205,12 +58966,7 @@ var binaryCapability = {
|
|
|
58205
58966
|
history: { fields: { on: { kind: "boolean" } } },
|
|
58206
58967
|
/** Clock fields: written, but excluded from the compare that decides
|
|
58207
58968
|
* whether persisting is worth a SQLite commit. */
|
|
58208
|
-
volatileStateFields: ["lastChangedAt"]
|
|
58209
|
-
customize: {
|
|
58210
|
-
version: 1,
|
|
58211
|
-
advanced: "derive",
|
|
58212
|
-
targets: []
|
|
58213
|
-
}
|
|
58969
|
+
volatileStateFields: ["lastChangedAt"]
|
|
58214
58970
|
};
|
|
58215
58971
|
/**
|
|
58216
58972
|
* Dimmable-light brightness control. Co-exists with `switch` on the
|
|
@@ -58226,7 +58982,7 @@ var binaryCapability = {
|
|
|
58226
58982
|
*/
|
|
58227
58983
|
var BrightnessStatusSchema = object({
|
|
58228
58984
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
58229
|
-
percentage: number().min(0).max(100),
|
|
58985
|
+
percentage: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
58230
58986
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
58231
58987
|
lastChangedAt: number()
|
|
58232
58988
|
});
|
|
@@ -58680,21 +59436,21 @@ var ClimateControlStatusSchema = object({
|
|
|
58680
59436
|
/** Single setpoint in Celsius. Used by single-target modes
|
|
58681
59437
|
* (heat / cool / dry). Null when the device is in a range mode
|
|
58682
59438
|
* (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
|
|
58683
|
-
target: number().nullable(),
|
|
59439
|
+
target: number().nullable().meta(readingMeta("temperature")),
|
|
58684
59440
|
/** Upper bound of the dual setpoint in Celsius. Populated only
|
|
58685
59441
|
* in `heat_cool` mode. */
|
|
58686
|
-
targetHigh: number().nullable(),
|
|
59442
|
+
targetHigh: number().nullable().meta(readingMeta("temperature")),
|
|
58687
59443
|
/** Lower bound of the dual setpoint in Celsius. Populated only
|
|
58688
59444
|
* in `heat_cool` mode. */
|
|
58689
|
-
targetLow: number().nullable(),
|
|
59445
|
+
targetLow: number().nullable().meta(readingMeta("temperature")),
|
|
58690
59446
|
/** Target relative humidity (0..100). Null when the device has no
|
|
58691
59447
|
* humidity control. */
|
|
58692
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
59448
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
58693
59449
|
/** Current measured temperature in Celsius. Read-only — pushed by
|
|
58694
59450
|
* the device's internal sensor. */
|
|
58695
|
-
currentTemp: number().nullable(),
|
|
59451
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
58696
59452
|
/** Current measured relative humidity (0..100). Read-only. */
|
|
58697
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
59453
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
58698
59454
|
/** Vertical louver swing on/off. Null when the device has no vertical
|
|
58699
59455
|
* swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
|
|
58700
59456
|
swingVertical: boolean().nullable(),
|
|
@@ -58702,11 +59458,11 @@ var ClimateControlStatusSchema = object({
|
|
|
58702
59458
|
* horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
|
|
58703
59459
|
swingHorizontal: boolean().nullable(),
|
|
58704
59460
|
/** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
58705
|
-
minTemp: number().nullable().optional(),
|
|
59461
|
+
minTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
58706
59462
|
/** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
58707
|
-
maxTemp: number().nullable().optional(),
|
|
59463
|
+
maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
58708
59464
|
/** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
|
|
58709
|
-
tempStep: number().positive().nullable().optional(),
|
|
59465
|
+
tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
|
|
58710
59466
|
/** Ms epoch when the slice was last updated (push or command). */
|
|
58711
59467
|
lastFetchedAt: number()
|
|
58712
59468
|
});
|
|
@@ -58848,17 +59604,17 @@ var climateControlCapability = {
|
|
|
58848
59604
|
* modes to expose in the picker without a slice fetch.
|
|
58849
59605
|
*/
|
|
58850
59606
|
var RgbTripletSchema = object({
|
|
58851
|
-
r: number().int().min(0).max(255),
|
|
58852
|
-
g: number().int().min(0).max(255),
|
|
58853
|
-
b: number().int().min(0).max(255)
|
|
59607
|
+
r: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
59608
|
+
g: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
59609
|
+
b: number().int().min(0).max(255).meta(readingMeta("none"))
|
|
58854
59610
|
});
|
|
58855
59611
|
var HsvTripletSchema = object({
|
|
58856
59612
|
/** Hue in degrees, 0..360. */
|
|
58857
|
-
h: number().min(0).max(360),
|
|
59613
|
+
h: number().min(0).max(360).meta(readingMeta("none")),
|
|
58858
59614
|
/** Saturation as 0..100 inclusive. */
|
|
58859
|
-
s: number().min(0).max(100),
|
|
59615
|
+
s: number().min(0).max(100).meta(readingMeta("none")),
|
|
58860
59616
|
/** Value/brightness as 0..100 inclusive. */
|
|
58861
|
-
v: number().min(0).max(100)
|
|
59617
|
+
v: number().min(0).max(100).meta(readingMeta("none"))
|
|
58862
59618
|
});
|
|
58863
59619
|
var ColorInputSchema = discriminatedUnion("mode", [
|
|
58864
59620
|
object({
|
|
@@ -58887,7 +59643,7 @@ var ColorStatusSchema = object({
|
|
|
58887
59643
|
/** Populated when `mode === 'hsv'`. */
|
|
58888
59644
|
hsv: HsvTripletSchema.optional(),
|
|
58889
59645
|
/** Populated when `mode === 'mired'`. */
|
|
58890
|
-
mireds: number().int().optional(),
|
|
59646
|
+
mireds: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
58891
59647
|
/** Ms epoch of the last operator-driven change. */
|
|
58892
59648
|
lastChangedAt: number()
|
|
58893
59649
|
});
|
|
@@ -59082,18 +59838,12 @@ _enum([
|
|
|
59082
59838
|
"empty",
|
|
59083
59839
|
"full"
|
|
59084
59840
|
]);
|
|
59085
|
-
/**
|
|
59086
|
-
|
|
59087
|
-
|
|
59088
|
-
"days",
|
|
59089
|
-
"hours",
|
|
59090
|
-
"cycles"
|
|
59091
|
-
]);
|
|
59092
|
-
/** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
|
|
59093
|
-
* days). `value` null = unknown (D393). */
|
|
59841
|
+
/** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
|
|
59842
|
+
var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
|
|
59843
|
+
/** Remaining life. `value` null = unknown (D393). Duration is seconds. */
|
|
59094
59844
|
var ConsumableRemainingSchema = object({
|
|
59095
|
-
value: number().min(0).nullable(),
|
|
59096
|
-
|
|
59845
|
+
value: number().min(0).nullable().meta(readingMeta("device")),
|
|
59846
|
+
kind: ConsumableRemainingKindSchema
|
|
59097
59847
|
});
|
|
59098
59848
|
/** A single consumable item. Either a continuous `level` (remaining
|
|
59099
59849
|
* life %, or fill % for a `supply` / `waste` item) or a discrete `status`
|
|
@@ -59106,7 +59856,7 @@ var ConsumableItemSchema = object({
|
|
|
59106
59856
|
/** Display name. */
|
|
59107
59857
|
label: string().min(1),
|
|
59108
59858
|
/** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
|
|
59109
|
-
level: number().min(0).max(100).nullable(),
|
|
59859
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59110
59860
|
/** The provider's discrete verdict when known (binary mode). */
|
|
59111
59861
|
status: ConsumableStatusSchema.nullable(),
|
|
59112
59862
|
/** What the item is — decides whether low or full is bad. Absent = `wear`. */
|
|
@@ -59188,8 +59938,8 @@ var consumablesCustomize = {
|
|
|
59188
59938
|
transforms: [{ kind: "identity" }]
|
|
59189
59939
|
}],
|
|
59190
59940
|
literals: [{
|
|
59191
|
-
path: "remaining.
|
|
59192
|
-
values: [...
|
|
59941
|
+
path: "remaining.kind",
|
|
59942
|
+
values: [...ConsumableRemainingKindSchema.options]
|
|
59193
59943
|
}]
|
|
59194
59944
|
},
|
|
59195
59945
|
{
|
|
@@ -59318,7 +60068,6 @@ reset: method(object({
|
|
|
59318
60068
|
},
|
|
59319
60069
|
"remaining.value": {
|
|
59320
60070
|
kind: "gauge",
|
|
59321
|
-
unitFrom: "remaining.unit",
|
|
59322
60071
|
deadband: 1,
|
|
59323
60072
|
displayName: "Remaining"
|
|
59324
60073
|
}
|
|
@@ -59388,10 +60137,9 @@ var contactCapability = {
|
|
|
59388
60137
|
* (empty array for the other kinds). It lives in the slice because
|
|
59389
60138
|
* HA `select.*` entities may update their options at runtime.
|
|
59390
60139
|
*
|
|
59391
|
-
*
|
|
59392
|
-
*
|
|
59393
|
-
*
|
|
59394
|
-
* push — consistent with `options` and the sensor-unit approach.
|
|
60140
|
+
* Numeric `min` / `max` / `step` live in the slice and share the device
|
|
60141
|
+
* quantity (`SourceInfo.quantity`, D944). Display precision is a read-time
|
|
60142
|
+
* preference. A datetime control's shape is `SourceInfo.dateTimeShape`.
|
|
59395
60143
|
*/
|
|
59396
60144
|
var ControlStatusSchema = object({
|
|
59397
60145
|
kind: _enum([
|
|
@@ -59403,39 +60151,17 @@ var ControlStatusSchema = object({
|
|
|
59403
60151
|
/** Current value. Numeric kind → number; select/text/datetime → string.
|
|
59404
60152
|
* Datetime values are ISO 8601 strings (full timestamp, date-only,
|
|
59405
60153
|
* or time-only — HA accepts all three forms). */
|
|
59406
|
-
value: union([number(), string()]),
|
|
60154
|
+
value: union([number().meta(readingMeta("device")), string()]),
|
|
59407
60155
|
/** Acceptable values when `kind === 'select'`. Empty for other kinds. */
|
|
59408
60156
|
options: array(string()),
|
|
59409
60157
|
/** Ms epoch when the slice was last updated. */
|
|
59410
60158
|
lastChangedAt: number(),
|
|
59411
|
-
/**
|
|
59412
|
-
|
|
59413
|
-
|
|
59414
|
-
|
|
59415
|
-
/**
|
|
59416
|
-
|
|
59417
|
-
min: number().optional(),
|
|
59418
|
-
/** Maximum allowed value. Populated live from HA `attributes.max`.
|
|
59419
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
59420
|
-
max: number().optional(),
|
|
59421
|
-
/** Step increment. Populated live from HA `attributes.step`.
|
|
59422
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
59423
|
-
step: number().optional(),
|
|
59424
|
-
/** Suggested decimal places for numeric display. Populated live from HA
|
|
59425
|
-
* `attributes.suggested_display_precision`, or derived from `step` when the
|
|
59426
|
-
* HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
|
|
59427
|
-
* other kinds. Falls back to auto-formatting when absent. */
|
|
59428
|
-
precision: number().int().min(0).max(10).optional(),
|
|
59429
|
-
/** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
|
|
59430
|
-
* 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
|
|
59431
|
-
* (UI defaults to 'datetime'). Derived live from the device's HA domain
|
|
59432
|
-
* (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
|
|
59433
|
-
* `has_date`/`has_time` attributes on each push. */
|
|
59434
|
-
format: _enum([
|
|
59435
|
-
"date",
|
|
59436
|
-
"time",
|
|
59437
|
-
"datetime"
|
|
59438
|
-
]).optional()
|
|
60159
|
+
/** Minimum allowed value, in the device quantity's base unit. */
|
|
60160
|
+
min: number().optional().meta(readingMeta("device")),
|
|
60161
|
+
/** Maximum allowed value, in the device quantity's base unit. */
|
|
60162
|
+
max: number().optional().meta(readingMeta("device")),
|
|
60163
|
+
/** Step increment, in the device quantity's base unit. */
|
|
60164
|
+
step: number().optional().meta(readingMeta("device"))
|
|
59439
60165
|
});
|
|
59440
60166
|
var ControlSetValueInputSchema = discriminatedUnion("kind", [
|
|
59441
60167
|
object({
|
|
@@ -59501,9 +60227,9 @@ var CoverStatusSchema = object({
|
|
|
59501
60227
|
]),
|
|
59502
60228
|
/** 0 = fully closed, 100 = fully open. Null when the device has
|
|
59503
60229
|
* no intermediate position surface. */
|
|
59504
|
-
position: number().min(0).max(100).nullable(),
|
|
60230
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59505
60231
|
/** 0 = closed slats, 100 = open slats. Null when no tilt support. */
|
|
59506
|
-
tiltPosition: number().min(0).max(100).nullable(),
|
|
60232
|
+
tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59507
60233
|
/** Ms epoch when the slice was last updated. */
|
|
59508
60234
|
lastChangedAt: number()
|
|
59509
60235
|
});
|
|
@@ -59606,9 +60332,9 @@ var NormalizedRangeSchema$1 = object({
|
|
|
59606
60332
|
var DayNightStatusSchema = object({
|
|
59607
60333
|
mode: DayNightModeSchema,
|
|
59608
60334
|
/** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
|
|
59609
|
-
sensitivity: number().optional(),
|
|
60335
|
+
sensitivity: number().optional().meta(readingMeta("none")),
|
|
59610
60336
|
/** Delay before the IR-cut filter flips, in seconds. */
|
|
59611
|
-
switchDelaySec: number().optional(),
|
|
60337
|
+
switchDelaySec: number().optional().meta(readingMeta("duration")),
|
|
59612
60338
|
lastFetchedAt: number()
|
|
59613
60339
|
});
|
|
59614
60340
|
/**
|
|
@@ -59826,11 +60552,11 @@ var DishwasherStatusSchema = object({
|
|
|
59826
60552
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
59827
60553
|
availablePrograms: array(string()),
|
|
59828
60554
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
59829
|
-
remainingSeconds: number().int().nonnegative().nullable(),
|
|
60555
|
+
remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
59830
60556
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
59831
|
-
delayedStartSeconds: number().int().nonnegative().nullable(),
|
|
60557
|
+
delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
59832
60558
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
59833
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
60559
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59834
60560
|
/** True when the door is reported open; null when the door state is not known. */
|
|
59835
60561
|
doorOpen: boolean().nullable(),
|
|
59836
60562
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -59854,7 +60580,7 @@ var DishwasherStatusSchema = object({
|
|
|
59854
60580
|
/** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
|
|
59855
60581
|
machineCareDue: boolean().nullable(),
|
|
59856
60582
|
/** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
|
|
59857
|
-
machineCareRemainingRuns: number().int().nonnegative().nullable(),
|
|
60583
|
+
machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
|
|
59858
60584
|
/** Option states: null when the appliance does not expose the option or has not said. */
|
|
59859
60585
|
halfLoad: boolean().nullable(),
|
|
59860
60586
|
hygienePlus: boolean().nullable(),
|
|
@@ -59869,7 +60595,7 @@ var DishwasherStatusSchema = object({
|
|
|
59869
60595
|
/** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
|
|
59870
60596
|
activeEvents: array(string()),
|
|
59871
60597
|
/** Numeric error code when the appliance reports one; null otherwise. */
|
|
59872
|
-
errorCode: number().nullable(),
|
|
60598
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
59873
60599
|
/** Name of the raised alert-level event while the appliance is in error; null otherwise. */
|
|
59874
60600
|
errorLabel: string().nullable(),
|
|
59875
60601
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
@@ -60100,7 +60826,7 @@ var DoorbellStatusSchema = object({
|
|
|
60100
60826
|
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
60101
60827
|
lastPressedAt: number().nullable(),
|
|
60102
60828
|
/** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
|
|
60103
|
-
pressCountSinceStart: number()
|
|
60829
|
+
pressCountSinceStart: number().meta(readingMeta("count"))
|
|
60104
60830
|
});
|
|
60105
60831
|
var DoorbellPressEventSchema = object({
|
|
60106
60832
|
deviceId: number(),
|
|
@@ -60165,17 +60891,7 @@ onPressed: {
|
|
|
60165
60891
|
history: "none",
|
|
60166
60892
|
customize: doorbellCustomize
|
|
60167
60893
|
};
|
|
60168
|
-
|
|
60169
|
-
* Enum-state sensor — a string value picked from a finite option set.
|
|
60170
|
-
* Drives HA `sensor` entries with `state_class: enum` (HVAC action
|
|
60171
|
-
* states, weather conditions, contact-source channels, mode strings).
|
|
60172
|
-
* The option set is stable across observations; it is persisted in the
|
|
60173
|
-
* device config blob (via the control slice at adoption) and is NOT
|
|
60174
|
-
* stored in `sourceInfo`.
|
|
60175
|
-
*/
|
|
60176
|
-
/** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
|
|
60177
|
-
* enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
|
|
60178
|
-
var EnumSensorDateTimeFormatSchema = _enum([
|
|
60894
|
+
_enum([
|
|
60179
60895
|
"date",
|
|
60180
60896
|
"time",
|
|
60181
60897
|
"datetime"
|
|
@@ -60183,12 +60899,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
|
|
|
60183
60899
|
var EnumSensorStatusSchema = object({
|
|
60184
60900
|
value: string(),
|
|
60185
60901
|
/**
|
|
60186
|
-
* Set
|
|
60187
|
-
* as
|
|
60188
|
-
*
|
|
60189
|
-
* a time-only sensor → `'time'`.
|
|
60902
|
+
* Set when `value` is a timestamp rather than an enum state, so history
|
|
60903
|
+
* does not learn ISO strings as states. The display shape (date / time /
|
|
60904
|
+
* datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
|
|
60190
60905
|
*/
|
|
60191
|
-
|
|
60906
|
+
timestamp: boolean().optional(),
|
|
60192
60907
|
/** Ms epoch when the slice was last updated. */
|
|
60193
60908
|
lastFetchedAt: number()
|
|
60194
60909
|
});
|
|
@@ -60215,11 +60930,11 @@ var enumSensorCapability = {
|
|
|
60215
60930
|
* History (D850): the value as an `enum` series (an appliance's program or
|
|
60216
60931
|
* operation state, an HVAC action). The option set is per device, so the
|
|
60217
60932
|
* value list is LEARNED as values are first seen (bounded). A sensor with a
|
|
60218
|
-
* `
|
|
60933
|
+
* `timestamp` carries a clock, not a state, and is not recorded.
|
|
60219
60934
|
*/
|
|
60220
60935
|
history: { fields: { value: {
|
|
60221
60936
|
kind: "enum",
|
|
60222
|
-
skipWhenSet: "
|
|
60937
|
+
skipWhenSet: "timestamp"
|
|
60223
60938
|
} } },
|
|
60224
60939
|
/** Clock fields: written, but excluded from the compare that decides
|
|
60225
60940
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -60239,13 +60954,13 @@ var EventFireSchema = object({
|
|
|
60239
60954
|
deviceId: number(),
|
|
60240
60955
|
eventType: string(),
|
|
60241
60956
|
data: record(string(), unknown()).nullable(),
|
|
60242
|
-
timestamp: number(),
|
|
60243
|
-
seq: number()
|
|
60957
|
+
timestamp: number().meta(readingMeta("none")),
|
|
60958
|
+
seq: number().meta(readingMeta("count"))
|
|
60244
60959
|
});
|
|
60245
60960
|
var EventEmitterStatusSchema = object({
|
|
60246
60961
|
eventTypes: array(string()),
|
|
60247
60962
|
lastEvent: EventFireSchema.nullable(),
|
|
60248
|
-
eventCountSinceStart: number()
|
|
60963
|
+
eventCountSinceStart: number().meta(readingMeta("count"))
|
|
60249
60964
|
});
|
|
60250
60965
|
var eventEmitterCapability = {
|
|
60251
60966
|
name: "event-emitter",
|
|
@@ -60658,10 +61373,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
60658
61373
|
var FanControlStatusSchema = object({
|
|
60659
61374
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
60660
61375
|
* speed surface (single-speed fan). */
|
|
60661
|
-
percentage: number().min(0).max(100).nullable(),
|
|
61376
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60662
61377
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
60663
61378
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
60664
|
-
percentageStep: number().positive().optional(),
|
|
61379
|
+
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
60665
61380
|
/** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
|
|
60666
61381
|
* — empty string when no preset is active or supported. */
|
|
60667
61382
|
preset: string(),
|
|
@@ -60782,7 +61497,7 @@ var FeatureProbeStatusSchema = object({
|
|
|
60782
61497
|
/** Camera/firmware model string. `null` when the firmware doesn't expose it. */
|
|
60783
61498
|
model: string().nullable(),
|
|
60784
61499
|
/** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
|
|
60785
|
-
channelCount: number().nullable(),
|
|
61500
|
+
channelCount: number().nullable().meta(readingMeta("count")),
|
|
60786
61501
|
/**
|
|
60787
61502
|
* Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
|
|
60788
61503
|
* completes — drivers' `getAccessoryChildren()` should treat zero as
|
|
@@ -60905,9 +61620,151 @@ var gasCapability = {
|
|
|
60905
61620
|
* whether persisting is worth a SQLite commit. */
|
|
60906
61621
|
volatileStateFields: ["lastChangedAt"]
|
|
60907
61622
|
};
|
|
61623
|
+
var GroupCommandsSchema = object({
|
|
61624
|
+
/** One row per member that has been commanded. A later command replaces that member's row. */
|
|
61625
|
+
results: array(object({
|
|
61626
|
+
deviceId: number().int().nonnegative(),
|
|
61627
|
+
cap: string().min(1),
|
|
61628
|
+
method: string().min(1),
|
|
61629
|
+
ok: boolean(),
|
|
61630
|
+
error: string().optional(),
|
|
61631
|
+
/** Ms epoch of the attempt. */
|
|
61632
|
+
at: number()
|
|
61633
|
+
})) });
|
|
61634
|
+
var groupCommandsCapability = {
|
|
61635
|
+
name: "group-commands",
|
|
61636
|
+
scope: "device",
|
|
61637
|
+
deviceNative: true,
|
|
61638
|
+
mode: "singleton",
|
|
61639
|
+
deviceTypes: [
|
|
61640
|
+
DeviceType.Switch,
|
|
61641
|
+
DeviceType.Light,
|
|
61642
|
+
DeviceType.Fan,
|
|
61643
|
+
DeviceType.Cover,
|
|
61644
|
+
DeviceType.Valve,
|
|
61645
|
+
DeviceType.Lock,
|
|
61646
|
+
DeviceType.MediaPlayer,
|
|
61647
|
+
DeviceType.Thermostat,
|
|
61648
|
+
DeviceType.Climate
|
|
61649
|
+
],
|
|
61650
|
+
methods: {},
|
|
61651
|
+
status: {
|
|
61652
|
+
schema: GroupCommandsSchema,
|
|
61653
|
+
kind: "push"
|
|
61654
|
+
},
|
|
61655
|
+
runtimeState: GroupCommandsSchema,
|
|
61656
|
+
/**
|
|
61657
|
+
* Runtime-state durability: **session** — the last attempt. A restored
|
|
61658
|
+
* failure would outlive the command that caused it.
|
|
61659
|
+
*
|
|
61660
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
61661
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
61662
|
+
*/
|
|
61663
|
+
durability: "session",
|
|
61664
|
+
history: "none"
|
|
61665
|
+
};
|
|
61666
|
+
/**
|
|
61667
|
+
* Whether a helper actuator group counts as active (D944): at least one
|
|
61668
|
+
* included member is, or every included member is. What "active" means is the
|
|
61669
|
+
* type's: a cover is open, a lock is locked, a media player is playing, a
|
|
61670
|
+
* climate device is not off. A group of lights, switches and fans reports
|
|
61671
|
+
* `switch.on` instead; this cap is for the types whose own state is not a
|
|
61672
|
+
* yes/no. Unknown members are left out (`group-status` counts them) and an
|
|
61673
|
+
* empty included set takes the device offline, never `active: false`.
|
|
61674
|
+
*/
|
|
61675
|
+
var GroupActiveSchema = object({
|
|
61676
|
+
active: boolean().meta({ label: "Active" }),
|
|
61677
|
+
/** Ms epoch when the slice was last updated. */
|
|
61678
|
+
lastChangedAt: number()
|
|
61679
|
+
});
|
|
61680
|
+
var groupActiveCapability = {
|
|
61681
|
+
name: "group-active",
|
|
61682
|
+
scope: "device",
|
|
61683
|
+
deviceNative: true,
|
|
61684
|
+
mode: "singleton",
|
|
61685
|
+
deviceTypes: [
|
|
61686
|
+
DeviceType.Cover,
|
|
61687
|
+
DeviceType.Valve,
|
|
61688
|
+
DeviceType.Lock,
|
|
61689
|
+
DeviceType.MediaPlayer,
|
|
61690
|
+
DeviceType.Climate,
|
|
61691
|
+
DeviceType.Thermostat
|
|
61692
|
+
],
|
|
61693
|
+
methods: {},
|
|
61694
|
+
status: {
|
|
61695
|
+
schema: GroupActiveSchema,
|
|
61696
|
+
kind: "push"
|
|
61697
|
+
},
|
|
61698
|
+
runtimeState: GroupActiveSchema,
|
|
61699
|
+
/**
|
|
61700
|
+
* Runtime-state durability: **session** — recomputed from the members on the
|
|
61701
|
+
* next source event; a restored value would outlive the members it named.
|
|
61702
|
+
*
|
|
61703
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
61704
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
61705
|
+
*/
|
|
61706
|
+
durability: "session",
|
|
61707
|
+
history: "none"
|
|
61708
|
+
};
|
|
61709
|
+
/**
|
|
61710
|
+
* How many members of a helper group answered (D944). Counts are integers,
|
|
61711
|
+
* not measurements: zero included is a real count, never a stand-in reading.
|
|
61712
|
+
* The reading itself stays on the output capability. History is none — the
|
|
61713
|
+
* series that later statistics read is the output cap's own history block.
|
|
61714
|
+
*/
|
|
61715
|
+
var GroupStatusSchema = object({
|
|
61716
|
+
/** Members whose current value entered the aggregate. */
|
|
61717
|
+
included: number().int().nonnegative().meta(readingMeta("count", {
|
|
61718
|
+
precision: 0,
|
|
61719
|
+
label: "Reporting"
|
|
61720
|
+
})),
|
|
61721
|
+
/** Members left out: offline, unknown, or not a finite reading (D393). */
|
|
61722
|
+
excluded: number().int().nonnegative().meta(readingMeta("count", {
|
|
61723
|
+
precision: 0,
|
|
61724
|
+
label: "Excluded"
|
|
61725
|
+
})),
|
|
61726
|
+
/** `included` + `excluded`. */
|
|
61727
|
+
total: number().int().nonnegative().meta(readingMeta("count", {
|
|
61728
|
+
precision: 0,
|
|
61729
|
+
label: "Members"
|
|
61730
|
+
}))
|
|
61731
|
+
});
|
|
61732
|
+
var groupStatusCapability = {
|
|
61733
|
+
name: "group-status",
|
|
61734
|
+
scope: "device",
|
|
61735
|
+
deviceNative: true,
|
|
61736
|
+
mode: "singleton",
|
|
61737
|
+
deviceTypes: [
|
|
61738
|
+
DeviceType.Sensor,
|
|
61739
|
+
DeviceType.Switch,
|
|
61740
|
+
DeviceType.Light,
|
|
61741
|
+
DeviceType.Fan,
|
|
61742
|
+
DeviceType.Cover,
|
|
61743
|
+
DeviceType.Valve,
|
|
61744
|
+
DeviceType.Lock,
|
|
61745
|
+
DeviceType.MediaPlayer,
|
|
61746
|
+
DeviceType.Thermostat,
|
|
61747
|
+
DeviceType.Climate
|
|
61748
|
+
],
|
|
61749
|
+
methods: {},
|
|
61750
|
+
status: {
|
|
61751
|
+
schema: GroupStatusSchema,
|
|
61752
|
+
kind: "push"
|
|
61753
|
+
},
|
|
61754
|
+
runtimeState: GroupStatusSchema,
|
|
61755
|
+
/**
|
|
61756
|
+
* Runtime-state durability: **session** — recomputed from members on the
|
|
61757
|
+
* next source event; a restored count would outlive the readings it named.
|
|
61758
|
+
*
|
|
61759
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
61760
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
61761
|
+
*/
|
|
61762
|
+
durability: "session",
|
|
61763
|
+
history: "none"
|
|
61764
|
+
};
|
|
60908
61765
|
var HeatingValveStatusSchema = object({
|
|
60909
61766
|
/** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
|
|
60910
|
-
position: number().min(0).max(100).nullable(),
|
|
61767
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60911
61768
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
60912
61769
|
lastFetchedAt: number()
|
|
60913
61770
|
});
|
|
@@ -60950,9 +61807,9 @@ var HumidifierStatusSchema = object({
|
|
|
60950
61807
|
/** Whether the humidifier is currently on. */
|
|
60951
61808
|
on: boolean(),
|
|
60952
61809
|
/** Current measured relative humidity (0..100). Null when not reported. */
|
|
60953
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
61810
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
60954
61811
|
/** Target relative humidity (0..100). Null when no setpoint surface. */
|
|
60955
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
61812
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
60956
61813
|
/** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
|
|
60957
61814
|
* device has no mode surface. */
|
|
60958
61815
|
mode: string().nullable(),
|
|
@@ -60962,9 +61819,9 @@ var HumidifierStatusSchema = object({
|
|
|
60962
61819
|
* `idle` / `off`). Null when the device doesn't report it. */
|
|
60963
61820
|
action: string().nullable(),
|
|
60964
61821
|
/** HA `min_humidity` attribute. Null → UI uses 0. */
|
|
60965
|
-
minHumidity: number().nullable(),
|
|
61822
|
+
minHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
60966
61823
|
/** HA `max_humidity` attribute. Null → UI uses 100. */
|
|
60967
|
-
maxHumidity: number().nullable(),
|
|
61824
|
+
maxHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
60968
61825
|
/** Ms epoch when the slice was last updated. */
|
|
60969
61826
|
lastChangedAt: number()
|
|
60970
61827
|
});
|
|
@@ -61034,22 +61891,13 @@ var humidifierCapability = {
|
|
|
61034
61891
|
* `sourceInfo`.
|
|
61035
61892
|
*/
|
|
61036
61893
|
var HumiditySensorStatusSchema = object({
|
|
61037
|
-
/** Current relative humidity, 0..100. */
|
|
61038
|
-
percent: number().min(0).max(100).meta({
|
|
61039
|
-
|
|
61894
|
+
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
61895
|
+
percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
61896
|
+
precision: 0,
|
|
61040
61897
|
label: "Humidity"
|
|
61041
|
-
}),
|
|
61898
|
+
})),
|
|
61042
61899
|
/** Ms epoch when the slice was last updated. */
|
|
61043
|
-
lastFetchedAt: number()
|
|
61044
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
61045
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
61046
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
61047
|
-
unit: string().optional(),
|
|
61048
|
-
/** Suggested decimal places for numeric display.
|
|
61049
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
61050
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
61051
|
-
* auto-formatting when absent. */
|
|
61052
|
-
precision: number().int().min(0).max(10).optional()
|
|
61900
|
+
lastFetchedAt: number()
|
|
61053
61901
|
});
|
|
61054
61902
|
var humiditySensorCapability = {
|
|
61055
61903
|
name: "humidity-sensor",
|
|
@@ -61080,12 +61928,7 @@ var humiditySensorCapability = {
|
|
|
61080
61928
|
} } },
|
|
61081
61929
|
/** Clock fields: written, but excluded from the compare that decides
|
|
61082
61930
|
* whether persisting is worth a SQLite commit. */
|
|
61083
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
61084
|
-
customize: {
|
|
61085
|
-
version: 1,
|
|
61086
|
-
advanced: "derive",
|
|
61087
|
-
targets: []
|
|
61088
|
-
}
|
|
61931
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
61089
61932
|
};
|
|
61090
61933
|
/**
|
|
61091
61934
|
* Image display cap. Models a single still image exposed by an integration —
|
|
@@ -61106,7 +61949,7 @@ var ImageStatusSchema = object({
|
|
|
61106
61949
|
* entity exposes no `entity_picture` (yet). */
|
|
61107
61950
|
url: string().nullable(),
|
|
61108
61951
|
/** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
|
|
61109
|
-
lastUpdated: number().nullable()
|
|
61952
|
+
lastUpdated: number().nullable().meta(readingMeta("none"))
|
|
61110
61953
|
});
|
|
61111
61954
|
var imageCapability = {
|
|
61112
61955
|
name: "image",
|
|
@@ -61194,19 +62037,19 @@ var NormalizedRangeSchema = object({
|
|
|
61194
62037
|
*/
|
|
61195
62038
|
var ImageSettingsStatusSchema = object({
|
|
61196
62039
|
/** Normalized 0–100. */
|
|
61197
|
-
brightness: number().optional(),
|
|
62040
|
+
brightness: number().optional().meta(readingMeta("none")),
|
|
61198
62041
|
/** Normalized 0–100. */
|
|
61199
|
-
contrast: number().optional(),
|
|
62042
|
+
contrast: number().optional().meta(readingMeta("none")),
|
|
61200
62043
|
/** Normalized 0–100. */
|
|
61201
|
-
saturation: number().optional(),
|
|
62044
|
+
saturation: number().optional().meta(readingMeta("none")),
|
|
61202
62045
|
/** Normalized 0–100. */
|
|
61203
|
-
sharpness: number().optional(),
|
|
62046
|
+
sharpness: number().optional().meta(readingMeta("none")),
|
|
61204
62047
|
mirror: boolean().optional(),
|
|
61205
62048
|
flip: boolean().optional(),
|
|
61206
62049
|
rotate: ImageRotateSchema.optional(),
|
|
61207
62050
|
whiteBalance: WhiteBalanceModeSchema.optional(),
|
|
61208
62051
|
/** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
|
|
61209
|
-
warmth: number().optional(),
|
|
62052
|
+
warmth: number().optional().meta(readingMeta("none")),
|
|
61210
62053
|
exposureMode: ExposureModeSchema.optional(),
|
|
61211
62054
|
backlightMode: BacklightModeSchema.optional(),
|
|
61212
62055
|
lastFetchedAt: number()
|
|
@@ -61485,10 +62328,10 @@ var IntercomAbilitySchema = object({
|
|
|
61485
62328
|
/** Codecs accepted by firmware, in preference order. Driver-authored strings. */
|
|
61486
62329
|
codecs: array(string()),
|
|
61487
62330
|
/** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
|
|
61488
|
-
sampleRate: number(),
|
|
62331
|
+
sampleRate: number().meta(readingMeta("none")),
|
|
61489
62332
|
duplex: _enum(["half", "full"]),
|
|
61490
62333
|
/** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
|
|
61491
|
-
maxBacklogMs: number()
|
|
62334
|
+
maxBacklogMs: number().meta(readingMeta("duration-ms"))
|
|
61492
62335
|
});
|
|
61493
62336
|
var IntercomStatusSchema = object({
|
|
61494
62337
|
/** True while a talk session is open. */
|
|
@@ -61715,13 +62558,13 @@ var LawnMowerControlStatusSchema = object({
|
|
|
61715
62558
|
activity: LawnMowerActivitySchema,
|
|
61716
62559
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
61717
62560
|
* reading. */
|
|
61718
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
62561
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61719
62562
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
61720
62563
|
* task is active / progress is unavailable. */
|
|
61721
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
62564
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61722
62565
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
61723
62566
|
* or null when unknown. */
|
|
61724
|
-
currentCode: number().nullable(),
|
|
62567
|
+
currentCode: number().nullable().meta(readingMeta("none")),
|
|
61725
62568
|
/** Human label for {@link currentCode}, or null when undecodable. */
|
|
61726
62569
|
currentCodeLabel: string().nullable(),
|
|
61727
62570
|
/** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
|
|
@@ -62174,7 +63017,14 @@ var lockControlCapability = {
|
|
|
62174
63017
|
}),
|
|
62175
63018
|
unlock: method(object({
|
|
62176
63019
|
deviceId: number().int().nonnegative(),
|
|
62177
|
-
code: string().min(1).optional()
|
|
63020
|
+
code: string().min(1).optional(),
|
|
63021
|
+
/**
|
|
63022
|
+
* Honoured by a helper lock GROUP only (D944): unlocking several doors
|
|
63023
|
+
* with one call is refused unless the caller says it was confirmed. A
|
|
63024
|
+
* single lock ignores it. The refusal lives in the callee, so no
|
|
63025
|
+
* automation or API caller reaches the doors by skipping a dialog.
|
|
63026
|
+
*/
|
|
63027
|
+
confirm: boolean().optional()
|
|
62178
63028
|
}), _void(), {
|
|
62179
63029
|
kind: "mutation",
|
|
62180
63030
|
auth: "protected"
|
|
@@ -62211,6 +63061,44 @@ var lockControlCapability = {
|
|
|
62211
63061
|
volatileStateFields: ["lastChangedAt"]
|
|
62212
63062
|
};
|
|
62213
63063
|
/**
|
|
63064
|
+
* A monotonic total between resets (D944). Energy uses `power-meter.kwhTotal`
|
|
63065
|
+
* so dashboards keep one series; every other instantaneous reading lands here.
|
|
63066
|
+
* `value` is the time integral in the source quantity's accumulated base
|
|
63067
|
+
* (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
|
|
63068
|
+
*/
|
|
63069
|
+
var MeterStatusSchema = object({
|
|
63070
|
+
value: number().nullable().meta(readingMeta("device", { label: "Total" })),
|
|
63071
|
+
lastFetchedAt: number()
|
|
63072
|
+
});
|
|
63073
|
+
var meterCapability = {
|
|
63074
|
+
name: "meter",
|
|
63075
|
+
scope: "device",
|
|
63076
|
+
deviceNative: true,
|
|
63077
|
+
mode: "singleton",
|
|
63078
|
+
deviceTypes: [DeviceType.Sensor],
|
|
63079
|
+
methods: {},
|
|
63080
|
+
status: {
|
|
63081
|
+
schema: MeterStatusSchema,
|
|
63082
|
+
kind: "push"
|
|
63083
|
+
},
|
|
63084
|
+
runtimeState: MeterStatusSchema,
|
|
63085
|
+
/**
|
|
63086
|
+
* Runtime-state durability: **restored** — the total is a cache of the
|
|
63087
|
+
* history integral. A restart shows it only until the history pass
|
|
63088
|
+
* recomputes the open period; it is not an in-memory buffer.
|
|
63089
|
+
*
|
|
63090
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
63091
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
63092
|
+
*/
|
|
63093
|
+
durability: "restored",
|
|
63094
|
+
history: { fields: { value: {
|
|
63095
|
+
kind: "counter",
|
|
63096
|
+
deadband: .001,
|
|
63097
|
+
minIntervalMs: 6e4
|
|
63098
|
+
} } },
|
|
63099
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
63100
|
+
};
|
|
63101
|
+
/**
|
|
62214
63102
|
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
62215
63103
|
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
62216
63104
|
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
@@ -62254,7 +63142,7 @@ var MediaPlayerStatusSchema = object({
|
|
|
62254
63142
|
state: MediaPlayerStateSchema,
|
|
62255
63143
|
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
62256
63144
|
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
62257
|
-
volumeLevel: number().min(0).max(100).nullable(),
|
|
63145
|
+
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62258
63146
|
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
62259
63147
|
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
62260
63148
|
isMuted: boolean().nullable(),
|
|
@@ -62267,10 +63155,10 @@ var MediaPlayerStatusSchema = object({
|
|
|
62267
63155
|
currentMedia: MediaInfoSchema.nullable(),
|
|
62268
63156
|
/** Current playback position in ms. Null when not seekable or
|
|
62269
63157
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
62270
|
-
positionMs: number().int().nonnegative().nullable(),
|
|
63158
|
+
positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
62271
63159
|
/** Total duration of the current media in ms. Null when unknown
|
|
62272
63160
|
* (live stream) or nothing is playing. */
|
|
62273
|
-
durationMs: number().int().nonnegative().nullable(),
|
|
63161
|
+
durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
62274
63162
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
62275
63163
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
62276
63164
|
shuffle: boolean().nullable(),
|
|
@@ -62579,7 +63467,7 @@ var motionCustomize = {
|
|
|
62579
63467
|
eventName: "onMotionChanged"
|
|
62580
63468
|
},
|
|
62581
63469
|
deviceTypes: [DeviceType.Camera],
|
|
62582
|
-
apply: "
|
|
63470
|
+
apply: "add",
|
|
62583
63471
|
applyAuthority: {
|
|
62584
63472
|
kind: "camera-sensors",
|
|
62585
63473
|
timeoutField: "motionCooldownMs"
|
|
@@ -62651,7 +63539,7 @@ var MotionStatusSchema = object({
|
|
|
62651
63539
|
* it, every provider seeded it at boot, and every session left it
|
|
62652
63540
|
* `detected: true` after its detach.
|
|
62653
63541
|
*/
|
|
62654
|
-
autoClearAfterMs: number().nullable()
|
|
63542
|
+
autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
|
|
62655
63543
|
});
|
|
62656
63544
|
/**
|
|
62657
63545
|
* Payload of `motion.onMotionChanged` event + the corresponding bus
|
|
@@ -62688,12 +63576,18 @@ var motionCapability = {
|
|
|
62688
63576
|
methods: {
|
|
62689
63577
|
/**
|
|
62690
63578
|
* Pull the current motion state synchronously. Convenience shortcut
|
|
62691
|
-
* for consumers that don't need the full status object
|
|
62692
|
-
*
|
|
62693
|
-
* `getStatus` once the auto-injected status surface lands in every
|
|
62694
|
-
* consumer.
|
|
63579
|
+
* for consumers that don't need the full status object. A composed
|
|
63580
|
+
* group answers from its slice (D944): unknown throws, it is not off.
|
|
62695
63581
|
*/
|
|
62696
63582
|
isDetected: method(object({ deviceId: number() }), boolean()) },
|
|
63583
|
+
/**
|
|
63584
|
+
* A composed motion group serves `isDetected` from `detected` (D944).
|
|
63585
|
+
* Without this the cap stays uncomposable (`has-commands`).
|
|
63586
|
+
*/
|
|
63587
|
+
composedMethods: { isDetected: {
|
|
63588
|
+
kind: "slice",
|
|
63589
|
+
field: "detected"
|
|
63590
|
+
} },
|
|
62697
63591
|
events: {
|
|
62698
63592
|
/**
|
|
62699
63593
|
* Fires every time the runner transitions a camera between
|
|
@@ -62833,7 +63727,7 @@ var MotionZoneRegionSchema = object({
|
|
|
62833
63727
|
* the grid region(s). */
|
|
62834
63728
|
var MotionZoneStatusSchema = object({
|
|
62835
63729
|
enabled: boolean(),
|
|
62836
|
-
sensitivity: number(),
|
|
63730
|
+
sensitivity: number().meta(readingMeta("none")),
|
|
62837
63731
|
/** Grid region(s). Today exactly one `grid` shape. */
|
|
62838
63732
|
regions: array(MotionZoneRegionSchema),
|
|
62839
63733
|
lastFetchedAt: number()
|
|
@@ -63203,7 +64097,7 @@ var NavigationStatusSchema = object({
|
|
|
63203
64097
|
following: boolean(),
|
|
63204
64098
|
flash: boolean(),
|
|
63205
64099
|
lightMode: NavigationLightModeSchema,
|
|
63206
|
-
lightLevel: number().min(40).max(100),
|
|
64100
|
+
lightLevel: number().min(40).max(100).meta(readingMeta("none")),
|
|
63207
64101
|
/** Ms epoch when the slice was last updated. */
|
|
63208
64102
|
lastChangedAt: number()
|
|
63209
64103
|
});
|
|
@@ -63337,13 +64231,13 @@ var NetworkLinkStatusSchema = object({
|
|
|
63337
64231
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
63338
64232
|
* SKIP a null rather than coerce it.
|
|
63339
64233
|
*/
|
|
63340
|
-
signalPercent: number().min(0).max(100).nullable(),
|
|
64234
|
+
signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63341
64235
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
63342
|
-
rssiDbm: number().optional(),
|
|
64236
|
+
rssiDbm: number().optional().meta(readingMeta("signal-level")),
|
|
63343
64237
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
63344
64238
|
ssid: string().optional(),
|
|
63345
64239
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
63346
|
-
lastUpdated: number()
|
|
64240
|
+
lastUpdated: number().meta(readingMeta("none"))
|
|
63347
64241
|
});
|
|
63348
64242
|
/** The slice a provider seeds before its first read: nothing is known yet. */
|
|
63349
64243
|
var NETWORK_LINK_UNKNOWN = {
|
|
@@ -63748,7 +64642,7 @@ var NotifierStatusSchema = object({
|
|
|
63748
64642
|
lastError: string().nullable(),
|
|
63749
64643
|
/** Number of deliveries currently buffered server-side (rate-limited
|
|
63750
64644
|
* notifiers may queue). 0 when the provider sends synchronously. */
|
|
63751
|
-
queueDepth: number().int().nonnegative(),
|
|
64645
|
+
queueDepth: number().int().nonnegative().meta(readingMeta("count")),
|
|
63752
64646
|
/** Per-feature capability matrix — authoritative source for the
|
|
63753
64647
|
* compose-form field gating. */
|
|
63754
64648
|
supports: NotifierSupportsSchema
|
|
@@ -63840,11 +64734,9 @@ onSent: { data: object({
|
|
|
63840
64734
|
* cap fits (HA `sensor` whose `device_class` we don't have a typed cap
|
|
63841
64735
|
* for: water flow, distance, weight, frequency, signal strength, …).
|
|
63842
64736
|
*
|
|
63843
|
-
* The
|
|
63844
|
-
*
|
|
63845
|
-
*
|
|
63846
|
-
* The UI reads them directly from the slice — they are the single source
|
|
63847
|
-
* of truth for generic numeric rendering and do NOT live on `sourceInfo`.
|
|
64737
|
+
* The quantity is not on this slice. It is chosen when the device is
|
|
64738
|
+
* adopted and stored once on `SourceInfo.quantity` (D944). `value` is in
|
|
64739
|
+
* that quantity's base unit. `null` is a refused sample (D393).
|
|
63848
64740
|
*
|
|
63849
64741
|
* Prefer the typed alternatives (`temperature-sensor`, `humidity-sensor`,
|
|
63850
64742
|
* `ambient-light-sensor`, `pressure-sensor`, `power-meter`,
|
|
@@ -63853,15 +64745,8 @@ onSent: { data: object({
|
|
|
63853
64745
|
* numeric values land here.
|
|
63854
64746
|
*/
|
|
63855
64747
|
var NumericSensorStatusSchema = object({
|
|
63856
|
-
|
|
63857
|
-
|
|
63858
|
-
* Populated live from the upstream source on each state push.
|
|
63859
|
-
* Absent for unitless sensors. */
|
|
63860
|
-
unit: string().optional(),
|
|
63861
|
-
/** Suggested decimal places for numeric display.
|
|
63862
|
-
* Populated live from the upstream source when provided.
|
|
63863
|
-
* Falls back to auto-formatting when absent. */
|
|
63864
|
-
precision: number().int().min(0).max(10).optional(),
|
|
64748
|
+
/** Reading in the device quantity's base unit. `null` when refused (D393). */
|
|
64749
|
+
value: number().nullable().meta(readingMeta("device")),
|
|
63865
64750
|
/** Ms epoch when the slice was last updated. */
|
|
63866
64751
|
lastFetchedAt: number()
|
|
63867
64752
|
});
|
|
@@ -63885,11 +64770,11 @@ var numericSensorCapability = {
|
|
|
63885
64770
|
*/
|
|
63886
64771
|
durability: "restored",
|
|
63887
64772
|
/**
|
|
63888
|
-
* History (D850): recorded series. The unit is
|
|
64773
|
+
* History (D850): recorded series. The unit is the device quantity's base,
|
|
64774
|
+
* resolved when the series is read — the cap has no single unit.
|
|
63889
64775
|
*/
|
|
63890
64776
|
history: { fields: { value: {
|
|
63891
64777
|
kind: "gauge",
|
|
63892
|
-
unitFrom: "unit",
|
|
63893
64778
|
minIntervalMs: 3e4
|
|
63894
64779
|
} } },
|
|
63895
64780
|
/** Clock fields: written, but excluded from the compare that decides
|
|
@@ -64227,23 +65112,26 @@ var PetFeederStatusSchema = object({
|
|
|
64227
65112
|
/** Food currently in the bowl (grams). Null when the device has not
|
|
64228
65113
|
* reported a reading yet. On dual-hopper models this is the combined
|
|
64229
65114
|
* bowl reading; per-hopper levels live in `food1`/`food2`. */
|
|
64230
|
-
foodLevel: number().nullable(),
|
|
65115
|
+
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
64231
65116
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
64232
65117
|
* single-hopper models. */
|
|
64233
|
-
food1: number().nullable(),
|
|
65118
|
+
food1: number().nullable().meta(readingMeta("none")),
|
|
64234
65119
|
/** Hopper-2 food level (grams) on dual-hopper feeders; null on
|
|
64235
65120
|
* single-hopper models. */
|
|
64236
|
-
food2: number().nullable(),
|
|
65121
|
+
food2: number().nullable().meta(readingMeta("none")),
|
|
64237
65122
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
64238
65123
|
* (`device_class: problem`, on = low). True when the bowl is empty /
|
|
64239
65124
|
* below the feeder's low threshold. */
|
|
64240
65125
|
lowFood: boolean(),
|
|
64241
65126
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
64242
65127
|
* device has no battery reading. */
|
|
64243
|
-
batteryPower: number().min(0).max(100).nullable(),
|
|
64244
|
-
/**
|
|
64245
|
-
*
|
|
64246
|
-
|
|
65128
|
+
batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
65129
|
+
/**
|
|
65130
|
+
* Desiccant life remaining, stored in seconds (duration base, D944).
|
|
65131
|
+
* Providers convert from the days the feeder reports. Null when the
|
|
65132
|
+
* model has no desiccant sensor.
|
|
65133
|
+
*/
|
|
65134
|
+
desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
|
|
64247
65135
|
/** True while a feed is in progress. */
|
|
64248
65136
|
feeding: boolean(),
|
|
64249
65137
|
/** Decoded connectivity / power status (HA petkit device-status enum).
|
|
@@ -64254,7 +65142,7 @@ var PetFeederStatusSchema = object({
|
|
|
64254
65142
|
* with `errorCode` for consumers that want the raw integer. */
|
|
64255
65143
|
error: string().nullable(),
|
|
64256
65144
|
/** Raw device error code (0 / null = no error). */
|
|
64257
|
-
errorCode: number().nullable(),
|
|
65145
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
64258
65146
|
/** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
|
|
64259
65147
|
isDualHopper: boolean(),
|
|
64260
65148
|
/** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
|
|
@@ -64264,7 +65152,7 @@ var PetFeederStatusSchema = object({
|
|
|
64264
65152
|
/** Play a chime when dispensing. */
|
|
64265
65153
|
feedSound: boolean(),
|
|
64266
65154
|
/** Speaker / prompt volume level (device-scaled integer). */
|
|
64267
|
-
volume: number(),
|
|
65155
|
+
volume: number().meta(readingMeta("none")),
|
|
64268
65156
|
/** Ms epoch when the slice was last refreshed from the cloud. */
|
|
64269
65157
|
lastFetchedAt: number()
|
|
64270
65158
|
});
|
|
@@ -64383,7 +65271,7 @@ var petFeederCapability = {
|
|
|
64383
65271
|
},
|
|
64384
65272
|
desiccantLeftDays: {
|
|
64385
65273
|
kind: "gauge",
|
|
64386
|
-
unit: "
|
|
65274
|
+
unit: "s"
|
|
64387
65275
|
}
|
|
64388
65276
|
} }
|
|
64389
65277
|
};
|
|
@@ -64784,43 +65672,88 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
64784
65672
|
*/
|
|
64785
65673
|
var PowerMeterStatusSchema = object({
|
|
64786
65674
|
/** Instantaneous power draw in watts. */
|
|
64787
|
-
watts: number().optional().meta({
|
|
64788
|
-
unit: "W",
|
|
64789
|
-
label: "Power"
|
|
64790
|
-
}),
|
|
65675
|
+
watts: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
64791
65676
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
64792
|
-
kwhTotal: number().optional().meta({
|
|
64793
|
-
unit: "kWh",
|
|
64794
|
-
label: "Energy"
|
|
64795
|
-
}),
|
|
65677
|
+
kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
64796
65678
|
/** Voltage in volts. */
|
|
64797
|
-
volts: number().optional(),
|
|
65679
|
+
volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
64798
65680
|
/** Current in amperes. */
|
|
64799
|
-
amps: number().optional(),
|
|
64800
|
-
/** Apparent power in volt-amperes
|
|
64801
|
-
va: number().optional(),
|
|
65681
|
+
amps: number().optional().meta(readingMeta("current", { label: "Current" })),
|
|
65682
|
+
/** Apparent power in volt-amperes. */
|
|
65683
|
+
va: number().optional().meta(readingMeta("apparent-power")),
|
|
64802
65684
|
/** Power factor (0..1) if reported. */
|
|
64803
|
-
powerFactor: number().min(0).max(1).optional(),
|
|
65685
|
+
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
64804
65686
|
/** Ms epoch when the slice was last updated. */
|
|
64805
|
-
lastFetchedAt: number()
|
|
64806
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
64807
|
-
* `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
|
|
64808
|
-
* Each upstream `sensor.*` entity surfaces ONE device_class, so one
|
|
64809
|
-
* unit per slice is unambiguous. The UI prefers this over the role's
|
|
64810
|
-
* canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
|
|
64811
|
-
unit: string().optional(),
|
|
64812
|
-
/** Suggested decimal places for numeric display.
|
|
64813
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
64814
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
64815
|
-
* auto-formatting when absent. */
|
|
64816
|
-
precision: number().int().min(0).max(10).optional()
|
|
65687
|
+
lastFetchedAt: number()
|
|
64817
65688
|
});
|
|
65689
|
+
/**
|
|
65690
|
+
* Loads that draw metered mains power (D951). The reading is a feature of the
|
|
65691
|
+
* device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
|
|
65692
|
+
* vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
|
|
65693
|
+
* meter or a standalone energy monitor IS the device. Cover, lock, valve,
|
|
65694
|
+
* camera and the like are left out: their draw is not what anyone meters.
|
|
65695
|
+
*/
|
|
65696
|
+
var POWER_METER_DEVICE_TYPES = [
|
|
65697
|
+
DeviceType.Sensor,
|
|
65698
|
+
DeviceType.Switch,
|
|
65699
|
+
DeviceType.Light,
|
|
65700
|
+
DeviceType.Fan,
|
|
65701
|
+
DeviceType.Climate,
|
|
65702
|
+
DeviceType.Thermostat,
|
|
65703
|
+
DeviceType.Humidifier,
|
|
65704
|
+
DeviceType.WaterHeater,
|
|
65705
|
+
DeviceType.MediaPlayer,
|
|
65706
|
+
DeviceType.Washer,
|
|
65707
|
+
DeviceType.Dryer,
|
|
65708
|
+
DeviceType.Dishwasher,
|
|
65709
|
+
DeviceType.Vacuum,
|
|
65710
|
+
DeviceType.LawnMower,
|
|
65711
|
+
DeviceType.PetFeeder
|
|
65712
|
+
];
|
|
65713
|
+
/** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
|
|
65714
|
+
var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
|
|
65715
|
+
/** One scalar target per reading; each is sourced from a device of its own (D951). */
|
|
65716
|
+
function powerTarget(id, unit) {
|
|
65717
|
+
return {
|
|
65718
|
+
id,
|
|
65719
|
+
cardinality: "scalar",
|
|
65720
|
+
vocabulary: {
|
|
65721
|
+
cap: "power-meter",
|
|
65722
|
+
stateField: id
|
|
65723
|
+
},
|
|
65724
|
+
deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
|
|
65725
|
+
apply: "add",
|
|
65726
|
+
path: id,
|
|
65727
|
+
sources: [{
|
|
65728
|
+
kind: "numeric",
|
|
65729
|
+
requiredUnit: unit,
|
|
65730
|
+
transforms: [{ kind: "identity" }]
|
|
65731
|
+
}],
|
|
65732
|
+
companions: [{
|
|
65733
|
+
kind: "clock",
|
|
65734
|
+
path: "lastFetchedAt",
|
|
65735
|
+
when: "always"
|
|
65736
|
+
}],
|
|
65737
|
+
sourceReport: { status: "assignment-status" },
|
|
65738
|
+
advancedExpression: true
|
|
65739
|
+
};
|
|
65740
|
+
}
|
|
65741
|
+
/** "Power and energy" extension: watts, energy, voltage and current from other devices (D951). */
|
|
65742
|
+
var powerMeterCustomize = {
|
|
65743
|
+
version: 1,
|
|
65744
|
+
targets: [
|
|
65745
|
+
powerTarget("watts", "W"),
|
|
65746
|
+
powerTarget("kwhTotal", "kWh"),
|
|
65747
|
+
powerTarget("volts", "V"),
|
|
65748
|
+
powerTarget("amps", "A")
|
|
65749
|
+
]
|
|
65750
|
+
};
|
|
64818
65751
|
var powerMeterCapability = {
|
|
64819
65752
|
name: "power-meter",
|
|
64820
65753
|
scope: "device",
|
|
64821
65754
|
deviceNative: true,
|
|
64822
65755
|
mode: "singleton",
|
|
64823
|
-
deviceTypes:
|
|
65756
|
+
deviceTypes: POWER_METER_DEVICE_TYPES,
|
|
64824
65757
|
methods: {},
|
|
64825
65758
|
status: {
|
|
64826
65759
|
schema: PowerMeterStatusSchema,
|
|
@@ -64878,11 +65811,7 @@ var powerMeterCapability = {
|
|
|
64878
65811
|
/** Clock fields: written, but excluded from the compare that decides
|
|
64879
65812
|
* whether persisting is worth a SQLite commit. */
|
|
64880
65813
|
volatileStateFields: ["lastFetchedAt"],
|
|
64881
|
-
customize:
|
|
64882
|
-
version: 1,
|
|
64883
|
-
advanced: "derive",
|
|
64884
|
-
targets: []
|
|
64885
|
-
}
|
|
65814
|
+
customize: powerMeterCustomize
|
|
64886
65815
|
};
|
|
64887
65816
|
/**
|
|
64888
65817
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
@@ -64902,11 +65831,11 @@ var powerMeterCapability = {
|
|
|
64902
65831
|
*/
|
|
64903
65832
|
var GpsLocationSchema = object({
|
|
64904
65833
|
/** Latitude in decimal degrees, -90..90. */
|
|
64905
|
-
latitude: number().min(-90).max(90),
|
|
65834
|
+
latitude: number().min(-90).max(90).meta(readingMeta("angle")),
|
|
64906
65835
|
/** Longitude in decimal degrees, -180..180. */
|
|
64907
|
-
longitude: number().min(-180).max(180),
|
|
65836
|
+
longitude: number().min(-180).max(180).meta(readingMeta("angle")),
|
|
64908
65837
|
/** Reported accuracy in meters (lower = better). */
|
|
64909
|
-
accuracyMeters: number().nonnegative()
|
|
65838
|
+
accuracyMeters: number().nonnegative().meta(readingMeta("length"))
|
|
64910
65839
|
});
|
|
64911
65840
|
var PresenceStatusSchema = object({
|
|
64912
65841
|
/** `home` / `not_home` / any user-defined zone name. */
|
|
@@ -64918,7 +65847,7 @@ var PresenceStatusSchema = object({
|
|
|
64918
65847
|
gps: GpsLocationSchema.nullable(),
|
|
64919
65848
|
/** Optional battery level of the tracking device (0..100). Null
|
|
64920
65849
|
* when not reported. */
|
|
64921
|
-
batteryPercent: number().min(0).max(100).nullable(),
|
|
65850
|
+
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
64922
65851
|
/** Ms epoch when the slice was last updated. */
|
|
64923
65852
|
lastChangedAt: number()
|
|
64924
65853
|
});
|
|
@@ -64968,19 +65897,10 @@ var presenceCapability = {
|
|
|
64968
65897
|
* `sourceInfo`.
|
|
64969
65898
|
*/
|
|
64970
65899
|
var PressureSensorStatusSchema = object({
|
|
64971
|
-
/** Current pressure in hPa. */
|
|
64972
|
-
hpa: number(),
|
|
65900
|
+
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
65901
|
+
hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
64973
65902
|
/** Ms epoch when the slice was last updated. */
|
|
64974
|
-
lastFetchedAt: number()
|
|
64975
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
64976
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
64977
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
64978
|
-
unit: string().optional(),
|
|
64979
|
-
/** Suggested decimal places for numeric display.
|
|
64980
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
64981
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
64982
|
-
* auto-formatting when absent. */
|
|
64983
|
-
precision: number().int().min(0).max(10).optional()
|
|
65903
|
+
lastFetchedAt: number()
|
|
64984
65904
|
});
|
|
64985
65905
|
var pressureSensorCapability = {
|
|
64986
65906
|
name: "pressure-sensor",
|
|
@@ -65506,8 +66426,8 @@ var ptzCapability = {
|
|
|
65506
66426
|
};
|
|
65507
66427
|
var PtzAutotrackSettingsSchema = object({
|
|
65508
66428
|
targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
|
|
65509
|
-
stopDelaySeconds: number().int().min(0).max(300),
|
|
65510
|
-
disappearDelaySeconds: number().int().min(0).max(300)
|
|
66429
|
+
stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
|
|
66430
|
+
disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
|
|
65511
66431
|
});
|
|
65512
66432
|
/**
|
|
65513
66433
|
* One option the camera advertises as a valid `targetType`. Drivers
|
|
@@ -65661,7 +66581,7 @@ var RunningStateStatusSchema = object({
|
|
|
65661
66581
|
/** null = unknown — never coerced to false (D393). */
|
|
65662
66582
|
running: boolean().nullable(),
|
|
65663
66583
|
/** Epoch ms the current boolean began; null while `running` is null. */
|
|
65664
|
-
since: number().nullable(),
|
|
66584
|
+
since: number().nullable().meta(readingMeta("none")),
|
|
65665
66585
|
source: RunningStateSourceSchema.meta({ label: "Source" }),
|
|
65666
66586
|
phase: string().nullable(),
|
|
65667
66587
|
/** null until a reading has been published (D393). */
|
|
@@ -65676,7 +66596,6 @@ var RUNNING_DEVICE_TYPES = Object.values(DeviceType);
|
|
|
65676
66596
|
/** Generic Customize target for the running boolean (D936). */
|
|
65677
66597
|
var runningCustomize = {
|
|
65678
66598
|
version: 1,
|
|
65679
|
-
advanced: "derive",
|
|
65680
66599
|
targets: [{
|
|
65681
66600
|
id: "running",
|
|
65682
66601
|
cardinality: "scalar",
|
|
@@ -65685,6 +66604,8 @@ var runningCustomize = {
|
|
|
65685
66604
|
stateField: "running"
|
|
65686
66605
|
},
|
|
65687
66606
|
deviceTypes: [
|
|
66607
|
+
DeviceType.Washer,
|
|
66608
|
+
DeviceType.Dryer,
|
|
65688
66609
|
DeviceType.Dishwasher,
|
|
65689
66610
|
DeviceType.Vacuum,
|
|
65690
66611
|
DeviceType.LawnMower,
|
|
@@ -65696,7 +66617,7 @@ var runningCustomize = {
|
|
|
65696
66617
|
DeviceType.Thermostat,
|
|
65697
66618
|
DeviceType.PetFeeder
|
|
65698
66619
|
],
|
|
65699
|
-
apply: "add
|
|
66620
|
+
apply: "add",
|
|
65700
66621
|
path: "running",
|
|
65701
66622
|
sources: [
|
|
65702
66623
|
{
|
|
@@ -66989,9 +67910,9 @@ var RecordTriggerSchema = _enum([
|
|
|
66989
67910
|
*/
|
|
66990
67911
|
var RecordWindowSchema = object({
|
|
66991
67912
|
trigger: RecordTriggerSchema,
|
|
66992
|
-
day: number().int().min(0).max(6),
|
|
66993
|
-
startMinute: number().int().min(0).max(1439),
|
|
66994
|
-
endMinute: number().int().min(1).max(1440)
|
|
67913
|
+
day: number().int().min(0).max(6).meta(readingMeta("none")),
|
|
67914
|
+
startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
|
|
67915
|
+
endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
|
|
66995
67916
|
});
|
|
66996
67917
|
/** Status of one physical volume, as the camera itself describes it. */
|
|
66997
67918
|
var OnboardStorageVolumeSchema = object({
|
|
@@ -67067,10 +67988,10 @@ var RecordingOnboardStatusSchema = object({
|
|
|
67067
67988
|
/** Master "record to the card at all" switch. */
|
|
67068
67989
|
enabled: boolean().nullable(),
|
|
67069
67990
|
overwriteWhenFull: boolean().nullable(),
|
|
67070
|
-
preRecordSec: number().nullable(),
|
|
67071
|
-
postRecordSec: number().nullable(),
|
|
67991
|
+
preRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
67992
|
+
postRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
67072
67993
|
/** Length of one recorded file, in minutes. */
|
|
67073
|
-
segmentMinutes: number().nullable(),
|
|
67994
|
+
segmentMinutes: number().nullable().meta(readingMeta("none")),
|
|
67074
67995
|
/** The primary track's weekly windows, flattened. */
|
|
67075
67996
|
windows: array(RecordWindowSchema),
|
|
67076
67997
|
/**
|
|
@@ -67086,7 +68007,7 @@ var RecordingOnboardStatusSchema = object({
|
|
|
67086
68007
|
* provider reporting a non-zero count also reports the schedule as not
|
|
67087
68008
|
* writable.
|
|
67088
68009
|
*/
|
|
67089
|
-
unreadableWindows: number(),
|
|
68010
|
+
unreadableWindows: number().meta(readingMeta("count")),
|
|
67090
68011
|
/**
|
|
67091
68012
|
* The camera is scheduled to record and has NO usable storage.
|
|
67092
68013
|
*
|
|
@@ -68096,7 +69017,7 @@ var CamStreamDescriptorSchema = object({
|
|
|
68096
69017
|
url: string().optional(),
|
|
68097
69018
|
codec: string().optional(),
|
|
68098
69019
|
resolution: CamStreamResolutionSchema.optional(),
|
|
68099
|
-
fps: number().positive().optional(),
|
|
69020
|
+
fps: number().positive().optional().meta(readingMeta("rate")),
|
|
68100
69021
|
label: string().optional(),
|
|
68101
69022
|
/** Device-level features (e.g. `battery-operated`) — drives broker policy. */
|
|
68102
69023
|
deviceFeatures: array(string()).optional(),
|
|
@@ -68183,18 +69104,18 @@ var StreamProfileSchema = _enum([
|
|
|
68183
69104
|
]);
|
|
68184
69105
|
/** The current encoding config of one stream profile. */
|
|
68185
69106
|
var StreamProfileConfigSchema = object({
|
|
68186
|
-
width: number(),
|
|
68187
|
-
height: number(),
|
|
69107
|
+
width: number().meta(readingMeta("none")),
|
|
69108
|
+
height: number().meta(readingMeta("none")),
|
|
68188
69109
|
codec: _enum(["h264", "h265"]),
|
|
68189
|
-
framerate: number(),
|
|
68190
|
-
bitrate: number(),
|
|
69110
|
+
framerate: number().meta(readingMeta("rate")),
|
|
69111
|
+
bitrate: number().meta(readingMeta("bitrate")),
|
|
68191
69112
|
bitrateMode: _enum(["vbr", "cbr"]).optional(),
|
|
68192
69113
|
encoderProfile: _enum([
|
|
68193
69114
|
"high",
|
|
68194
69115
|
"main",
|
|
68195
69116
|
"baseline"
|
|
68196
69117
|
]).optional(),
|
|
68197
|
-
gop: number().optional(),
|
|
69118
|
+
gop: number().optional().meta(readingMeta("none")),
|
|
68198
69119
|
/**
|
|
68199
69120
|
* Whether THIS profile currently carries an audio track. READ-ONLY here.
|
|
68200
69121
|
*
|
|
@@ -68566,7 +69487,8 @@ var switchCapability = {
|
|
|
68566
69487
|
deviceTypes: [
|
|
68567
69488
|
DeviceType.Switch,
|
|
68568
69489
|
DeviceType.Siren,
|
|
68569
|
-
DeviceType.Light
|
|
69490
|
+
DeviceType.Light,
|
|
69491
|
+
DeviceType.Fan
|
|
68570
69492
|
],
|
|
68571
69493
|
methods: { setState: method(object({
|
|
68572
69494
|
deviceId: number(),
|
|
@@ -68613,12 +69535,7 @@ var switchCapability = {
|
|
|
68613
69535
|
label: "On",
|
|
68614
69536
|
kind: "boolean"
|
|
68615
69537
|
}
|
|
68616
|
-
}] }
|
|
68617
|
-
customize: {
|
|
68618
|
-
version: 1,
|
|
68619
|
-
advanced: "derive",
|
|
68620
|
-
targets: []
|
|
68621
|
-
}
|
|
69538
|
+
}] }
|
|
68622
69539
|
};
|
|
68623
69540
|
/** C0 controls, DEL and the C1 range: none of them belongs in a name. */
|
|
68624
69541
|
function hasControlCharacter(value) {
|
|
@@ -69201,35 +70118,21 @@ var tamperCapability = {
|
|
|
69201
70118
|
* entries with `device_class: temperature` and any future native
|
|
69202
70119
|
* thermometer.
|
|
69203
70120
|
*
|
|
69204
|
-
*
|
|
69205
|
-
*
|
|
69206
|
-
* (climate-control's `currentTemp`, energy analytics) can compose
|
|
69207
|
-
* without per-source unit fixups. The canonical display unit (`°C`) is
|
|
69208
|
-
* a descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
69209
|
-
* `sourceInfo`.
|
|
70121
|
+
* The number is Celsius (D944). Providers convert on ingest. A failed
|
|
70122
|
+
* conversion is `null`, never a guess. Display unit is a read-time preference.
|
|
69210
70123
|
*
|
|
69211
70124
|
* Status `lastFetchedAt` lets staleness-aware consumers detect a
|
|
69212
70125
|
* frozen feed (provider hung) distinct from a "temperature hasn't
|
|
69213
70126
|
* changed" steady state.
|
|
69214
70127
|
*/
|
|
69215
70128
|
var TemperatureSensorStatusSchema = object({
|
|
69216
|
-
/** Current temperature in Celsius. */
|
|
69217
|
-
celsius: number().meta({
|
|
69218
|
-
|
|
70129
|
+
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
70130
|
+
celsius: number().nullable().meta(readingMeta("temperature", {
|
|
70131
|
+
precision: 1,
|
|
69219
70132
|
label: "Temperature"
|
|
69220
|
-
}),
|
|
70133
|
+
})),
|
|
69221
70134
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
69222
|
-
lastFetchedAt: number()
|
|
69223
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
69224
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
69225
|
-
* role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
|
|
69226
|
-
* Absent → the UI falls back to the role's canonical unit. */
|
|
69227
|
-
unit: string().optional(),
|
|
69228
|
-
/** Suggested decimal places for numeric display.
|
|
69229
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
69230
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
69231
|
-
* auto-formatting when absent. */
|
|
69232
|
-
precision: number().int().min(0).max(10).optional()
|
|
70135
|
+
lastFetchedAt: number()
|
|
69233
70136
|
});
|
|
69234
70137
|
var temperatureSensorCapability = {
|
|
69235
70138
|
name: "temperature-sensor",
|
|
@@ -69260,12 +70163,7 @@ var temperatureSensorCapability = {
|
|
|
69260
70163
|
} } },
|
|
69261
70164
|
/** Clock fields: written, but excluded from the compare that decides
|
|
69262
70165
|
* whether persisting is worth a SQLite commit. */
|
|
69263
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
69264
|
-
customize: {
|
|
69265
|
-
version: 1,
|
|
69266
|
-
advanced: "derive",
|
|
69267
|
-
targets: []
|
|
69268
|
-
}
|
|
70166
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
69269
70167
|
};
|
|
69270
70168
|
/**
|
|
69271
70169
|
* toast — system-scoped singleton capability that streams toast
|
|
@@ -69548,16 +70446,16 @@ var VacuumControlStatusSchema = object({
|
|
|
69548
70446
|
]),
|
|
69549
70447
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
69550
70448
|
* reading. */
|
|
69551
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
70449
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
69552
70450
|
/** Current fan-speed token (provider-verbatim). Null when unknown or
|
|
69553
70451
|
* the vacuum has no speed control. */
|
|
69554
70452
|
fanSpeed: string().nullable(),
|
|
69555
70453
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
69556
70454
|
availableFanSpeeds: array(string()),
|
|
69557
70455
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
69558
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
70456
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
69559
70457
|
/** Current error code (0 / null = no error). */
|
|
69560
|
-
errorCode: number().nullable(),
|
|
70458
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
69561
70459
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
69562
70460
|
errorLabel: string().nullable(),
|
|
69563
70461
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -69635,7 +70533,7 @@ var ValveStatusSchema = object({
|
|
|
69635
70533
|
]),
|
|
69636
70534
|
/** 0 = fully closed, 100 = fully open. Null when the device has no
|
|
69637
70535
|
* intermediate position surface. */
|
|
69638
|
-
position: number().min(0).max(100).nullable(),
|
|
70536
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
69639
70537
|
/** Ms epoch when the slice was last updated. */
|
|
69640
70538
|
lastChangedAt: number()
|
|
69641
70539
|
});
|
|
@@ -69743,9 +70641,9 @@ var vibrationCapability = {
|
|
|
69743
70641
|
*/
|
|
69744
70642
|
var WaterHeaterStatusSchema = object({
|
|
69745
70643
|
/** Current measured temperature. Null when not reported. */
|
|
69746
|
-
currentTemp: number().nullable(),
|
|
70644
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
69747
70645
|
/** Target temperature setpoint. Null when no setpoint surface. */
|
|
69748
|
-
targetTemp: number().nullable(),
|
|
70646
|
+
targetTemp: number().nullable().meta(readingMeta("temperature")),
|
|
69749
70647
|
/** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
|
|
69750
70648
|
* `heat_pump` / `high_demand` / `performance` / `off`). Null when the
|
|
69751
70649
|
* device reports an unknown state. */
|
|
@@ -69756,9 +70654,9 @@ var WaterHeaterStatusSchema = object({
|
|
|
69756
70654
|
* has no away surface. */
|
|
69757
70655
|
away: boolean().nullable(),
|
|
69758
70656
|
/** HA `min_temp` attribute. Null when not reported. */
|
|
69759
|
-
minTemp: number().nullable(),
|
|
70657
|
+
minTemp: number().nullable().meta(readingMeta("temperature")),
|
|
69760
70658
|
/** HA `max_temp` attribute. Null when not reported. */
|
|
69761
|
-
maxTemp: number().nullable(),
|
|
70659
|
+
maxTemp: number().nullable().meta(readingMeta("temperature")),
|
|
69762
70660
|
/** Ms epoch when the slice was last updated. */
|
|
69763
70661
|
lastChangedAt: number()
|
|
69764
70662
|
});
|
|
@@ -69843,25 +70741,18 @@ var waterHeaterCapability = {
|
|
|
69843
70741
|
* Forecast deferred — current conditions only for v1.
|
|
69844
70742
|
*/
|
|
69845
70743
|
var WeatherStatusSchema = object({
|
|
69846
|
-
/** Verbatim
|
|
69847
|
-
* when no condition has been reported yet. */
|
|
70744
|
+
/** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
|
|
69848
70745
|
condition: string().nullable(),
|
|
69849
|
-
/** Current temperature in
|
|
69850
|
-
temperature: number().nullable(),
|
|
69851
|
-
/** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
|
|
69852
|
-
temperatureUnit: string().nullable(),
|
|
70746
|
+
/** Current temperature in Celsius. Null when not provided or refused. */
|
|
70747
|
+
temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
|
|
69853
70748
|
/** Relative humidity (0..100). Null when not provided. */
|
|
69854
|
-
humidity: number().min(0).max(100).nullable(),
|
|
69855
|
-
/** Barometric pressure in
|
|
69856
|
-
pressure: number().nullable(),
|
|
69857
|
-
/**
|
|
69858
|
-
|
|
69859
|
-
/** Wind speed in the reported unit. Null when not provided. */
|
|
69860
|
-
windSpeed: number().nullable(),
|
|
69861
|
-
/** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
|
|
69862
|
-
windSpeedUnit: string().nullable(),
|
|
70749
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
70750
|
+
/** Barometric pressure in hPa. Null when not provided or refused. */
|
|
70751
|
+
pressure: number().nullable().meta(readingMeta("pressure")),
|
|
70752
|
+
/** Wind speed in metres per second. Null when not provided or refused. */
|
|
70753
|
+
windSpeed: number().nullable().meta(readingMeta("speed")),
|
|
69863
70754
|
/** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
|
|
69864
|
-
windBearing: number().nullable(),
|
|
70755
|
+
windBearing: number().nullable().meta(readingMeta("angle")),
|
|
69865
70756
|
/** Ms epoch when the slice was last updated. */
|
|
69866
70757
|
lastFetchedAt: number()
|
|
69867
70758
|
});
|
|
@@ -69889,12 +70780,12 @@ var weatherCapability = {
|
|
|
69889
70780
|
*/
|
|
69890
70781
|
durability: "session",
|
|
69891
70782
|
/**
|
|
69892
|
-
* History (D850): recorded series.
|
|
70783
|
+
* History (D850): recorded series. Numbers are the quantity base (D944).
|
|
69893
70784
|
*/
|
|
69894
70785
|
history: { fields: {
|
|
69895
70786
|
temperature: {
|
|
69896
70787
|
kind: "gauge",
|
|
69897
|
-
|
|
70788
|
+
unit: "°C",
|
|
69898
70789
|
deadband: .2
|
|
69899
70790
|
},
|
|
69900
70791
|
humidity: {
|
|
@@ -69904,12 +70795,12 @@ var weatherCapability = {
|
|
|
69904
70795
|
},
|
|
69905
70796
|
pressure: {
|
|
69906
70797
|
kind: "gauge",
|
|
69907
|
-
|
|
70798
|
+
unit: "hPa",
|
|
69908
70799
|
deadband: .5
|
|
69909
70800
|
},
|
|
69910
70801
|
windSpeed: {
|
|
69911
70802
|
kind: "gauge",
|
|
69912
|
-
|
|
70803
|
+
unit: "m/s",
|
|
69913
70804
|
deadband: .5
|
|
69914
70805
|
}
|
|
69915
70806
|
} }
|
|
@@ -70943,8 +71834,9 @@ function withClipEventLabels(provider, deps) {
|
|
|
70943
71834
|
listClips
|
|
70944
71835
|
};
|
|
70945
71836
|
}
|
|
71837
|
+
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
70946
71838
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
70947
|
-
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;
|
|
71839
|
+
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;
|
|
70948
71840
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
70949
71841
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
70950
71842
|
new Set(CAP_NAMES);
|
|
@@ -71288,6 +72180,9 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
71288
72180
|
featureProbe: featureProbeCapability,
|
|
71289
72181
|
flood: floodCapability,
|
|
71290
72182
|
gas: gasCapability,
|
|
72183
|
+
groupActive: groupActiveCapability,
|
|
72184
|
+
groupCommands: groupCommandsCapability,
|
|
72185
|
+
groupStatus: groupStatusCapability,
|
|
71291
72186
|
heatingValve: heatingValveCapability,
|
|
71292
72187
|
humidifier: humidifierCapability,
|
|
71293
72188
|
humiditySensor: humiditySensorCapability,
|
|
@@ -71297,6 +72192,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
71297
72192
|
lawnMowerControl: lawnMowerControlCapability,
|
|
71298
72193
|
lockControl: lockControlCapability,
|
|
71299
72194
|
mediaPlayer: mediaPlayerCapability,
|
|
72195
|
+
meter: meterCapability,
|
|
71300
72196
|
motion: motionCapability,
|
|
71301
72197
|
motionTrigger: motionTriggerCapability,
|
|
71302
72198
|
motionZones: motionZonesCapability,
|
|
@@ -72556,7 +73452,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
72556
73452
|
return toDeviceSummary(device, this.addonId);
|
|
72557
73453
|
}
|
|
72558
73454
|
};
|
|
72559
|
-
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;
|
|
73455
|
+
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;
|
|
72560
73456
|
new Set(Object.values(DeviceType));
|
|
72561
73457
|
Object.freeze({
|
|
72562
73458
|
"accessories.getStatus": {
|
|
@@ -72664,6 +73560,13 @@ Object.freeze({
|
|
|
72664
73560
|
access: "view",
|
|
72665
73561
|
role: "admin"
|
|
72666
73562
|
},
|
|
73563
|
+
"addons.getPackageDocs": {
|
|
73564
|
+
capName: "addons",
|
|
73565
|
+
capScope: "system",
|
|
73566
|
+
addonId: null,
|
|
73567
|
+
access: "view",
|
|
73568
|
+
role: "admin"
|
|
73569
|
+
},
|
|
72667
73570
|
"addons.getVersions": {
|
|
72668
73571
|
capName: "addons",
|
|
72669
73572
|
capScope: "system",
|
|
@@ -75772,6 +76675,27 @@ Object.freeze({
|
|
|
75772
76675
|
access: "view",
|
|
75773
76676
|
role: "viewer"
|
|
75774
76677
|
},
|
|
76678
|
+
"groupActive.getStatus": {
|
|
76679
|
+
capName: "group-active",
|
|
76680
|
+
capScope: "device",
|
|
76681
|
+
addonId: null,
|
|
76682
|
+
access: "view",
|
|
76683
|
+
role: "viewer"
|
|
76684
|
+
},
|
|
76685
|
+
"groupCommands.getStatus": {
|
|
76686
|
+
capName: "group-commands",
|
|
76687
|
+
capScope: "device",
|
|
76688
|
+
addonId: null,
|
|
76689
|
+
access: "view",
|
|
76690
|
+
role: "viewer"
|
|
76691
|
+
},
|
|
76692
|
+
"groupStatus.getStatus": {
|
|
76693
|
+
capName: "group-status",
|
|
76694
|
+
capScope: "device",
|
|
76695
|
+
addonId: null,
|
|
76696
|
+
access: "view",
|
|
76697
|
+
role: "viewer"
|
|
76698
|
+
},
|
|
75775
76699
|
"heatingValve.getStatus": {
|
|
75776
76700
|
capName: "heating-valve",
|
|
75777
76701
|
capScope: "device",
|
|
@@ -76724,6 +77648,13 @@ Object.freeze({
|
|
|
76724
77648
|
access: "create",
|
|
76725
77649
|
role: "admin"
|
|
76726
77650
|
},
|
|
77651
|
+
"meter.getStatus": {
|
|
77652
|
+
capName: "meter",
|
|
77653
|
+
capScope: "device",
|
|
77654
|
+
addonId: null,
|
|
77655
|
+
access: "view",
|
|
77656
|
+
role: "viewer"
|
|
77657
|
+
},
|
|
76727
77658
|
"metricsProvider.collectSnapshot": {
|
|
76728
77659
|
capName: "metrics-provider",
|
|
76729
77660
|
capScope: "system",
|
|
@@ -84822,7 +85753,39 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
84822
85753
|
phrase: t("When {device} finishes", "Quando {device} finisce"),
|
|
84823
85754
|
icon: "square"
|
|
84824
85755
|
}
|
|
84825
|
-
}
|
|
85756
|
+
},
|
|
85757
|
+
overrides: { byType: {
|
|
85758
|
+
[DeviceType.Washer]: {
|
|
85759
|
+
state: { running: {
|
|
85760
|
+
whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
|
|
85761
|
+
whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
|
|
85762
|
+
} },
|
|
85763
|
+
events: {
|
|
85764
|
+
onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
|
|
85765
|
+
onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
|
|
85766
|
+
}
|
|
85767
|
+
},
|
|
85768
|
+
[DeviceType.Dryer]: {
|
|
85769
|
+
state: { running: {
|
|
85770
|
+
whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
|
|
85771
|
+
whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
|
|
85772
|
+
} },
|
|
85773
|
+
events: {
|
|
85774
|
+
onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
|
|
85775
|
+
onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
|
|
85776
|
+
}
|
|
85777
|
+
},
|
|
85778
|
+
[DeviceType.Dishwasher]: {
|
|
85779
|
+
state: { running: {
|
|
85780
|
+
whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
|
|
85781
|
+
whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
|
|
85782
|
+
} },
|
|
85783
|
+
events: {
|
|
85784
|
+
onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
|
|
85785
|
+
onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
|
|
85786
|
+
}
|
|
85787
|
+
}
|
|
85788
|
+
} }
|
|
84826
85789
|
},
|
|
84827
85790
|
update: {
|
|
84828
85791
|
scope: "device",
|
|
@@ -85594,6 +86557,41 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85594
86557
|
icon: "flame"
|
|
85595
86558
|
}]
|
|
85596
86559
|
},
|
|
86560
|
+
"group-active": {
|
|
86561
|
+
scope: "device",
|
|
86562
|
+
noun: t("Group state", "Stato del gruppo")
|
|
86563
|
+
},
|
|
86564
|
+
"group-commands": {
|
|
86565
|
+
scope: "device",
|
|
86566
|
+
noun: t("Group command", "Comando di gruppo")
|
|
86567
|
+
},
|
|
86568
|
+
"group-status": {
|
|
86569
|
+
scope: "device",
|
|
86570
|
+
noun: t("Group", "Gruppo"),
|
|
86571
|
+
state: [
|
|
86572
|
+
{
|
|
86573
|
+
kind: "numeric",
|
|
86574
|
+
field: "included",
|
|
86575
|
+
quantity: t("the members reporting", "i membri che rispondono"),
|
|
86576
|
+
label: t("Reporting", "Rispondono"),
|
|
86577
|
+
icon: "users"
|
|
86578
|
+
},
|
|
86579
|
+
{
|
|
86580
|
+
kind: "numeric",
|
|
86581
|
+
field: "excluded",
|
|
86582
|
+
quantity: t("the members left out", "i membri esclusi"),
|
|
86583
|
+
label: t("Excluded", "Esclusi"),
|
|
86584
|
+
icon: "users"
|
|
86585
|
+
},
|
|
86586
|
+
{
|
|
86587
|
+
kind: "numeric",
|
|
86588
|
+
field: "total",
|
|
86589
|
+
quantity: t("the member count", "il numero di membri"),
|
|
86590
|
+
label: t("Members", "Membri"),
|
|
86591
|
+
icon: "sigma"
|
|
86592
|
+
}
|
|
86593
|
+
]
|
|
86594
|
+
},
|
|
85597
86595
|
"humidity-sensor": {
|
|
85598
86596
|
scope: "device",
|
|
85599
86597
|
noun: t("Humidity", "Umidità"),
|
|
@@ -85619,6 +86617,17 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85619
86617
|
}],
|
|
85620
86618
|
events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
|
|
85621
86619
|
},
|
|
86620
|
+
meter: {
|
|
86621
|
+
scope: "device",
|
|
86622
|
+
noun: t("Meter", "Contatore"),
|
|
86623
|
+
state: [{
|
|
86624
|
+
kind: "numeric",
|
|
86625
|
+
field: "value",
|
|
86626
|
+
quantity: t("the total", "il totale"),
|
|
86627
|
+
label: t("Total", "Totale"),
|
|
86628
|
+
icon: "gauge"
|
|
86629
|
+
}]
|
|
86630
|
+
},
|
|
85622
86631
|
"numeric-sensor": {
|
|
85623
86632
|
scope: "device",
|
|
85624
86633
|
noun: t("Sensor", "Sensore"),
|
|
@@ -85627,7 +86636,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85627
86636
|
field: "value",
|
|
85628
86637
|
quantity: t("the reading", "il valore"),
|
|
85629
86638
|
label: t("Reading", "Valore"),
|
|
85630
|
-
unitField: "unit",
|
|
85631
86639
|
icon: "gauge"
|
|
85632
86640
|
}]
|
|
85633
86641
|
},
|
|
@@ -85763,7 +86771,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85763
86771
|
field: "temperature",
|
|
85764
86772
|
quantity: t("the outdoor temperature", "la temperatura esterna"),
|
|
85765
86773
|
label: t("Temperature", "Temperatura"),
|
|
85766
|
-
|
|
86774
|
+
unit: "°C",
|
|
85767
86775
|
icon: "thermometer"
|
|
85768
86776
|
},
|
|
85769
86777
|
{
|
|
@@ -85779,7 +86787,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85779
86787
|
field: "windSpeed",
|
|
85780
86788
|
quantity: t("the wind speed", "la velocità del vento"),
|
|
85781
86789
|
label: t("Wind", "Vento"),
|
|
85782
|
-
|
|
86790
|
+
unit: "m/s",
|
|
85783
86791
|
icon: "wind"
|
|
85784
86792
|
},
|
|
85785
86793
|
{
|
|
@@ -85787,7 +86795,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85787
86795
|
field: "pressure",
|
|
85788
86796
|
quantity: t("the air pressure", "la pressione atmosferica"),
|
|
85789
86797
|
label: t("Pressure", "Pressione"),
|
|
85790
|
-
|
|
86798
|
+
unit: "hPa",
|
|
85791
86799
|
icon: "gauge"
|
|
85792
86800
|
}
|
|
85793
86801
|
]
|
|
@@ -86092,8 +87100,47 @@ function projectAction(cap, action) {
|
|
|
86092
87100
|
};
|
|
86093
87101
|
}
|
|
86094
87102
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
86095
|
-
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;
|
|
87103
|
+
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;
|
|
86096
87104
|
DeviceFeature.BatteryOperated;
|
|
87105
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
87106
|
+
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.";
|
|
87107
|
+
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.";
|
|
87108
|
+
var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
|
|
87109
|
+
var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
|
|
87110
|
+
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.";
|
|
87111
|
+
function withheld(reason) {
|
|
87112
|
+
return {
|
|
87113
|
+
[DeviceType.Washer]: {
|
|
87114
|
+
exported: false,
|
|
87115
|
+
reason
|
|
87116
|
+
},
|
|
87117
|
+
[DeviceType.Dryer]: {
|
|
87118
|
+
exported: false,
|
|
87119
|
+
reason
|
|
87120
|
+
},
|
|
87121
|
+
[DeviceType.Dishwasher]: {
|
|
87122
|
+
exported: false,
|
|
87123
|
+
reason
|
|
87124
|
+
}
|
|
87125
|
+
};
|
|
87126
|
+
}
|
|
87127
|
+
function offered(received) {
|
|
87128
|
+
return {
|
|
87129
|
+
[DeviceType.Washer]: {
|
|
87130
|
+
exported: true,
|
|
87131
|
+
as: received
|
|
87132
|
+
},
|
|
87133
|
+
[DeviceType.Dryer]: {
|
|
87134
|
+
exported: true,
|
|
87135
|
+
as: received
|
|
87136
|
+
},
|
|
87137
|
+
[DeviceType.Dishwasher]: {
|
|
87138
|
+
exported: true,
|
|
87139
|
+
as: received
|
|
87140
|
+
}
|
|
87141
|
+
};
|
|
87142
|
+
}
|
|
87143
|
+
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
86097
87144
|
var LAST_FETCHED_FIELD = "lastFetchedAt";
|
|
86098
87145
|
/**
|
|
86099
87146
|
* How long a bridge stops re-attempting a refresh that did not land.
|
|
@@ -87212,6 +88259,21 @@ Object.freeze({
|
|
|
87212
88259
|
form: "single",
|
|
87213
88260
|
optional: false
|
|
87214
88261
|
}],
|
|
88262
|
+
"groupActive.getStatus": [{
|
|
88263
|
+
name: "deviceId",
|
|
88264
|
+
form: "single",
|
|
88265
|
+
optional: false
|
|
88266
|
+
}],
|
|
88267
|
+
"groupCommands.getStatus": [{
|
|
88268
|
+
name: "deviceId",
|
|
88269
|
+
form: "single",
|
|
88270
|
+
optional: false
|
|
88271
|
+
}],
|
|
88272
|
+
"groupStatus.getStatus": [{
|
|
88273
|
+
name: "deviceId",
|
|
88274
|
+
form: "single",
|
|
88275
|
+
optional: false
|
|
88276
|
+
}],
|
|
87215
88277
|
"heatingValve.getStatus": [{
|
|
87216
88278
|
name: "deviceId",
|
|
87217
88279
|
form: "single",
|
|
@@ -87412,6 +88474,11 @@ Object.freeze({
|
|
|
87412
88474
|
form: "single",
|
|
87413
88475
|
optional: false
|
|
87414
88476
|
}],
|
|
88477
|
+
"meter.getStatus": [{
|
|
88478
|
+
name: "deviceId",
|
|
88479
|
+
form: "single",
|
|
88480
|
+
optional: false
|
|
88481
|
+
}],
|
|
87415
88482
|
"motion.getStatus": [{
|
|
87416
88483
|
name: "deviceId",
|
|
87417
88484
|
form: "single",
|
|
@@ -89019,7 +90086,6 @@ Object.freeze({
|
|
|
89019
90086
|
"network-access": "ingress",
|
|
89020
90087
|
"smtp-provider": "email"
|
|
89021
90088
|
});
|
|
89022
|
-
DeviceType.Camera;
|
|
89023
90089
|
/**
|
|
89024
90090
|
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
89025
90091
|
* `z.infer`, they do not restate the shape.
|
|
@@ -89196,6 +90262,8 @@ new Map(Object.entries({
|
|
|
89196
90262
|
"detection-blind": "device-events",
|
|
89197
90263
|
"motion-noisy": "device-events",
|
|
89198
90264
|
"motion-calm": "device-events",
|
|
90265
|
+
"package-llm-silent": "device-events",
|
|
90266
|
+
"package-llm-answered": "device-events",
|
|
89199
90267
|
"recording-stalled": "device-events",
|
|
89200
90268
|
"recording-healthy": "device-events",
|
|
89201
90269
|
"recording-degraded": "device-events",
|
|
@@ -89279,6 +90347,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
89279
90347
|
label: "Motion without a subject",
|
|
89280
90348
|
kinds: ["motion-noisy", "motion-calm"]
|
|
89281
90349
|
},
|
|
90350
|
+
{
|
|
90351
|
+
id: "package-llm",
|
|
90352
|
+
family: "device-events",
|
|
90353
|
+
label: "Package confirm",
|
|
90354
|
+
kinds: ["package-llm-silent", "package-llm-answered"]
|
|
90355
|
+
},
|
|
89282
90356
|
{
|
|
89283
90357
|
id: "recording",
|
|
89284
90358
|
family: "device-events",
|
|
@@ -89460,6 +90534,8 @@ var DEVICE_EVENTS = [
|
|
|
89460
90534
|
"detection-blind",
|
|
89461
90535
|
"motion-noisy",
|
|
89462
90536
|
"motion-calm",
|
|
90537
|
+
"package-llm-silent",
|
|
90538
|
+
"package-llm-answered",
|
|
89463
90539
|
"recording-stalled",
|
|
89464
90540
|
"recording-healthy",
|
|
89465
90541
|
"recording-degraded",
|
|
@@ -89728,6 +90804,15 @@ new Map([
|
|
|
89728
90804
|
snapshotLabel: "Cameras with constant motion now",
|
|
89729
90805
|
snapshotSection: null
|
|
89730
90806
|
},
|
|
90807
|
+
{
|
|
90808
|
+
id: "package-llm-confirm",
|
|
90809
|
+
family: "device-events",
|
|
90810
|
+
label: "Package confirm (not answering / answering again)",
|
|
90811
|
+
bad: ["package-llm-silent"],
|
|
90812
|
+
recovery: ["package-llm-answered"],
|
|
90813
|
+
snapshotLabel: "Cameras whose package confirm is not answering",
|
|
90814
|
+
snapshotSection: null
|
|
90815
|
+
},
|
|
89731
90816
|
{
|
|
89732
90817
|
id: "recording-health",
|
|
89733
90818
|
family: "device-events",
|