@camstack/addon-provider-ecowitt 0.2.146 → 0.2.148
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +18 -0
- package/dist/addon.js +1594 -557
- package/dist/addon.mjs +1594 -557
- package/package.json +4 -2
package/dist/addon.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createServer } from "http";
|
|
2
2
|
import { EventEmitter } from "events";
|
|
3
3
|
import { createSocket } from "dgram";
|
|
4
|
-
//#region ../types/dist/event-category-
|
|
4
|
+
//#region ../types/dist/event-category-CT-hz5Rb.mjs
|
|
5
5
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
6
6
|
EventCategory["SystemBoot"] = "system.boot";
|
|
7
7
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -327,6 +327,14 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
327
327
|
/** The recovery edge of {@link PipelineMotionNoisy}: the motion fell back, or a detection arrived. */
|
|
328
328
|
EventCategory["PipelineMotionCalm"] = "pipeline.motion-calm";
|
|
329
329
|
/**
|
|
330
|
+
* A camera's package LLM confirm has failed closed three times in a row
|
|
331
|
+
* (D931, 2026-10-26). One edge per camera until
|
|
332
|
+
* {@link PipelinePackageLlmAnswered}.
|
|
333
|
+
*/
|
|
334
|
+
EventCategory["PipelinePackageLlmSilent"] = "pipeline.package-llm-silent";
|
|
335
|
+
/** The recovery edge of {@link PipelinePackageLlmSilent}: a confirm returned yes or no. */
|
|
336
|
+
EventCategory["PipelinePackageLlmAnswered"] = "pipeline.package-llm-answered";
|
|
337
|
+
/**
|
|
330
338
|
* Per-camera pipeline config was mutated by the orchestrator
|
|
331
339
|
* (a settings change via `setAgentInferenceDevices` /
|
|
332
340
|
* `setCameraStepToggle` / `setCameraStepSettings`). Orchestrator
|
|
@@ -5436,7 +5444,7 @@ var ZodIssueCode$1 = {
|
|
|
5436
5444
|
var ZodFirstPartyTypeKind$1;
|
|
5437
5445
|
ZodFirstPartyTypeKind$1 || (ZodFirstPartyTypeKind$1 = {});
|
|
5438
5446
|
//#endregion
|
|
5439
|
-
//#region ../types/dist/sleep-
|
|
5447
|
+
//#region ../types/dist/sleep-bykKrTgu.mjs
|
|
5440
5448
|
/**
|
|
5441
5449
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5442
5450
|
* window to float samples (D455).
|
|
@@ -7990,6 +7998,99 @@ var RecordKindDeclarationSchema = object({
|
|
|
7990
7998
|
}).strict();
|
|
7991
7999
|
/** The kind names: lower-case, dash separated, short enough for an index name. */
|
|
7992
8000
|
var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
|
|
8001
|
+
var READING_QUANTITIES = [
|
|
8002
|
+
"temperature",
|
|
8003
|
+
"pressure",
|
|
8004
|
+
"speed",
|
|
8005
|
+
"precipitation",
|
|
8006
|
+
"precipitation-rate",
|
|
8007
|
+
"length",
|
|
8008
|
+
"area",
|
|
8009
|
+
"illuminance",
|
|
8010
|
+
"irradiance",
|
|
8011
|
+
"power",
|
|
8012
|
+
"energy",
|
|
8013
|
+
"voltage",
|
|
8014
|
+
"current",
|
|
8015
|
+
"apparent-power",
|
|
8016
|
+
"humidity",
|
|
8017
|
+
"percentage",
|
|
8018
|
+
"concentration-volume",
|
|
8019
|
+
"concentration-mass",
|
|
8020
|
+
"concentration-ppb",
|
|
8021
|
+
"volume",
|
|
8022
|
+
"duration",
|
|
8023
|
+
"duration-ms",
|
|
8024
|
+
"information",
|
|
8025
|
+
"sound-level",
|
|
8026
|
+
"signal-level",
|
|
8027
|
+
"colour-temperature",
|
|
8028
|
+
"angle",
|
|
8029
|
+
"bitrate",
|
|
8030
|
+
"rate",
|
|
8031
|
+
"mass",
|
|
8032
|
+
"index",
|
|
8033
|
+
"ratio",
|
|
8034
|
+
"count",
|
|
8035
|
+
"none",
|
|
8036
|
+
"device"
|
|
8037
|
+
];
|
|
8038
|
+
var ReadingQuantitySchema = _enum(READING_QUANTITIES);
|
|
8039
|
+
new Set(READING_QUANTITIES);
|
|
8040
|
+
/**
|
|
8041
|
+
* Stored unit symbol for a quantity. `none` means the number is dimensionless
|
|
8042
|
+
* (or an index / ratio / count). `device` means the cap has no single unit.
|
|
8043
|
+
*/
|
|
8044
|
+
var QUANTITY_BASE = {
|
|
8045
|
+
temperature: "°C",
|
|
8046
|
+
pressure: "hPa",
|
|
8047
|
+
speed: "m/s",
|
|
8048
|
+
precipitation: "mm",
|
|
8049
|
+
"precipitation-rate": "mm/h",
|
|
8050
|
+
length: "m",
|
|
8051
|
+
area: "m²",
|
|
8052
|
+
illuminance: "lx",
|
|
8053
|
+
irradiance: "W/m²",
|
|
8054
|
+
power: "W",
|
|
8055
|
+
energy: "kWh",
|
|
8056
|
+
voltage: "V",
|
|
8057
|
+
current: "A",
|
|
8058
|
+
"apparent-power": "VA",
|
|
8059
|
+
humidity: "%",
|
|
8060
|
+
percentage: "%",
|
|
8061
|
+
"concentration-volume": "ppm",
|
|
8062
|
+
"concentration-mass": "µg/m³",
|
|
8063
|
+
"concentration-ppb": "ppb",
|
|
8064
|
+
volume: "L",
|
|
8065
|
+
duration: "s",
|
|
8066
|
+
"duration-ms": "ms",
|
|
8067
|
+
information: "B",
|
|
8068
|
+
"sound-level": "dBFS",
|
|
8069
|
+
"signal-level": "dBm",
|
|
8070
|
+
"colour-temperature": "mired",
|
|
8071
|
+
angle: "°",
|
|
8072
|
+
bitrate: "kbps",
|
|
8073
|
+
rate: "fps",
|
|
8074
|
+
mass: "kg",
|
|
8075
|
+
index: "none",
|
|
8076
|
+
ratio: "none",
|
|
8077
|
+
count: "none",
|
|
8078
|
+
none: "none",
|
|
8079
|
+
device: "device"
|
|
8080
|
+
};
|
|
8081
|
+
/** The stored unit, or `undefined` when the quantity has no unit symbol. */
|
|
8082
|
+
function baseUnitOf(quantity) {
|
|
8083
|
+
const symbol = QUANTITY_BASE[quantity];
|
|
8084
|
+
if (symbol === "none" || symbol === "device") return void 0;
|
|
8085
|
+
return symbol;
|
|
8086
|
+
}
|
|
8087
|
+
function readingMeta(quantity, extras) {
|
|
8088
|
+
return {
|
|
8089
|
+
quantity,
|
|
8090
|
+
...extras?.precision !== void 0 ? { precision: extras.precision } : {},
|
|
8091
|
+
...extras?.label !== void 0 ? { label: extras.label } : {}
|
|
8092
|
+
};
|
|
8093
|
+
}
|
|
7993
8094
|
/** Shared Zod schemas used across streaming capabilities. */
|
|
7994
8095
|
var CamProfileSchema = _enum([
|
|
7995
8096
|
"high",
|
|
@@ -8017,8 +8118,8 @@ var CamStreamKindSchema = _enum([
|
|
|
8017
8118
|
"derived"
|
|
8018
8119
|
]);
|
|
8019
8120
|
var CamStreamResolutionSchema = object({
|
|
8020
|
-
width: number().int().positive(),
|
|
8021
|
-
height: number().int().positive()
|
|
8121
|
+
width: number().int().positive().meta(readingMeta("none")),
|
|
8122
|
+
height: number().int().positive().meta(readingMeta("none"))
|
|
8022
8123
|
});
|
|
8023
8124
|
var CameraStreamSchema = object({
|
|
8024
8125
|
/** Stable, provider-assigned id unique within the (deviceId) scope. */
|
|
@@ -8536,6 +8637,8 @@ var CAP_NAMES = Object.freeze([
|
|
|
8536
8637
|
"filesystem-browse",
|
|
8537
8638
|
"flood",
|
|
8538
8639
|
"gas",
|
|
8640
|
+
"group-commands",
|
|
8641
|
+
"group-status",
|
|
8539
8642
|
"heating-valve",
|
|
8540
8643
|
"humidifier",
|
|
8541
8644
|
"humidity-sensor",
|
|
@@ -8665,14 +8768,6 @@ Object.freeze({
|
|
|
8665
8768
|
{
|
|
8666
8769
|
"path": "aqi",
|
|
8667
8770
|
"valueType": "number"
|
|
8668
|
-
},
|
|
8669
|
-
{
|
|
8670
|
-
"path": "unit",
|
|
8671
|
-
"valueType": "string"
|
|
8672
|
-
},
|
|
8673
|
-
{
|
|
8674
|
-
"path": "precision",
|
|
8675
|
-
"valueType": "number"
|
|
8676
8771
|
}
|
|
8677
8772
|
],
|
|
8678
8773
|
"alarm-panel": [
|
|
@@ -8708,20 +8803,10 @@ Object.freeze({
|
|
|
8708
8803
|
"valueType": "boolean"
|
|
8709
8804
|
}
|
|
8710
8805
|
],
|
|
8711
|
-
"ambient-light-sensor": [
|
|
8712
|
-
|
|
8713
|
-
|
|
8714
|
-
|
|
8715
|
-
},
|
|
8716
|
-
{
|
|
8717
|
-
"path": "unit",
|
|
8718
|
-
"valueType": "string"
|
|
8719
|
-
},
|
|
8720
|
-
{
|
|
8721
|
-
"path": "precision",
|
|
8722
|
-
"valueType": "number"
|
|
8723
|
-
}
|
|
8724
|
-
],
|
|
8806
|
+
"ambient-light-sensor": [{
|
|
8807
|
+
"path": "lux",
|
|
8808
|
+
"valueType": "number"
|
|
8809
|
+
}],
|
|
8725
8810
|
"audio-metrics": [
|
|
8726
8811
|
{
|
|
8727
8812
|
"path": "ts",
|
|
@@ -8801,6 +8886,10 @@ Object.freeze({
|
|
|
8801
8886
|
"path": "lastUpdated",
|
|
8802
8887
|
"valueType": "number"
|
|
8803
8888
|
},
|
|
8889
|
+
{
|
|
8890
|
+
"path": "volts",
|
|
8891
|
+
"valueType": "number"
|
|
8892
|
+
},
|
|
8804
8893
|
{
|
|
8805
8894
|
"path": "lastContactAt",
|
|
8806
8895
|
"valueType": "number"
|
|
@@ -9043,10 +9132,6 @@ Object.freeze({
|
|
|
9043
9132
|
"datetime"
|
|
9044
9133
|
]
|
|
9045
9134
|
},
|
|
9046
|
-
{
|
|
9047
|
-
"path": "unit",
|
|
9048
|
-
"valueType": "string"
|
|
9049
|
-
},
|
|
9050
9135
|
{
|
|
9051
9136
|
"path": "min",
|
|
9052
9137
|
"valueType": "number"
|
|
@@ -9058,19 +9143,6 @@ Object.freeze({
|
|
|
9058
9143
|
{
|
|
9059
9144
|
"path": "step",
|
|
9060
9145
|
"valueType": "number"
|
|
9061
|
-
},
|
|
9062
|
-
{
|
|
9063
|
-
"path": "precision",
|
|
9064
|
-
"valueType": "number"
|
|
9065
|
-
},
|
|
9066
|
-
{
|
|
9067
|
-
"path": "format",
|
|
9068
|
-
"valueType": "enum",
|
|
9069
|
-
"enumValues": [
|
|
9070
|
-
"date",
|
|
9071
|
-
"time",
|
|
9072
|
-
"datetime"
|
|
9073
|
-
]
|
|
9074
9146
|
}
|
|
9075
9147
|
],
|
|
9076
9148
|
"cover": [
|
|
@@ -9311,13 +9383,8 @@ Object.freeze({
|
|
|
9311
9383
|
"path": "value",
|
|
9312
9384
|
"valueType": "string"
|
|
9313
9385
|
}, {
|
|
9314
|
-
"path": "
|
|
9315
|
-
"valueType": "
|
|
9316
|
-
"enumValues": [
|
|
9317
|
-
"date",
|
|
9318
|
-
"time",
|
|
9319
|
-
"datetime"
|
|
9320
|
-
]
|
|
9386
|
+
"path": "timestamp",
|
|
9387
|
+
"valueType": "boolean"
|
|
9321
9388
|
}],
|
|
9322
9389
|
"event-emitter": [
|
|
9323
9390
|
{
|
|
@@ -9390,6 +9457,20 @@ Object.freeze({
|
|
|
9390
9457
|
"path": "detected",
|
|
9391
9458
|
"valueType": "boolean"
|
|
9392
9459
|
}],
|
|
9460
|
+
"group-status": [
|
|
9461
|
+
{
|
|
9462
|
+
"path": "included",
|
|
9463
|
+
"valueType": "number"
|
|
9464
|
+
},
|
|
9465
|
+
{
|
|
9466
|
+
"path": "excluded",
|
|
9467
|
+
"valueType": "number"
|
|
9468
|
+
},
|
|
9469
|
+
{
|
|
9470
|
+
"path": "total",
|
|
9471
|
+
"valueType": "number"
|
|
9472
|
+
}
|
|
9473
|
+
],
|
|
9393
9474
|
"heating-valve": [{
|
|
9394
9475
|
"path": "position",
|
|
9395
9476
|
"valueType": "number"
|
|
@@ -9424,20 +9505,10 @@ Object.freeze({
|
|
|
9424
9505
|
"valueType": "number"
|
|
9425
9506
|
}
|
|
9426
9507
|
],
|
|
9427
|
-
"humidity-sensor": [
|
|
9428
|
-
|
|
9429
|
-
|
|
9430
|
-
|
|
9431
|
-
},
|
|
9432
|
-
{
|
|
9433
|
-
"path": "unit",
|
|
9434
|
-
"valueType": "string"
|
|
9435
|
-
},
|
|
9436
|
-
{
|
|
9437
|
-
"path": "precision",
|
|
9438
|
-
"valueType": "number"
|
|
9439
|
-
}
|
|
9440
|
-
],
|
|
9508
|
+
"humidity-sensor": [{
|
|
9509
|
+
"path": "percent",
|
|
9510
|
+
"valueType": "number"
|
|
9511
|
+
}],
|
|
9441
9512
|
"image": [{
|
|
9442
9513
|
"path": "url",
|
|
9443
9514
|
"valueType": "string"
|
|
@@ -9776,20 +9847,10 @@ Object.freeze({
|
|
|
9776
9847
|
"valueType": "boolean"
|
|
9777
9848
|
}
|
|
9778
9849
|
],
|
|
9779
|
-
"numeric-sensor": [
|
|
9780
|
-
|
|
9781
|
-
|
|
9782
|
-
|
|
9783
|
-
},
|
|
9784
|
-
{
|
|
9785
|
-
"path": "unit",
|
|
9786
|
-
"valueType": "string"
|
|
9787
|
-
},
|
|
9788
|
-
{
|
|
9789
|
-
"path": "precision",
|
|
9790
|
-
"valueType": "number"
|
|
9791
|
-
}
|
|
9792
|
-
],
|
|
9850
|
+
"numeric-sensor": [{
|
|
9851
|
+
"path": "value",
|
|
9852
|
+
"valueType": "number"
|
|
9853
|
+
}],
|
|
9793
9854
|
"pet-feeder": [
|
|
9794
9855
|
{
|
|
9795
9856
|
"path": "foodLevel",
|
|
@@ -9881,14 +9942,6 @@ Object.freeze({
|
|
|
9881
9942
|
{
|
|
9882
9943
|
"path": "powerFactor",
|
|
9883
9944
|
"valueType": "number"
|
|
9884
|
-
},
|
|
9885
|
-
{
|
|
9886
|
-
"path": "unit",
|
|
9887
|
-
"valueType": "string"
|
|
9888
|
-
},
|
|
9889
|
-
{
|
|
9890
|
-
"path": "precision",
|
|
9891
|
-
"valueType": "number"
|
|
9892
9945
|
}
|
|
9893
9946
|
],
|
|
9894
9947
|
"presence": [
|
|
@@ -9917,20 +9970,10 @@ Object.freeze({
|
|
|
9917
9970
|
"valueType": "number"
|
|
9918
9971
|
}
|
|
9919
9972
|
],
|
|
9920
|
-
"pressure-sensor": [
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
},
|
|
9925
|
-
{
|
|
9926
|
-
"path": "unit",
|
|
9927
|
-
"valueType": "string"
|
|
9928
|
-
},
|
|
9929
|
-
{
|
|
9930
|
-
"path": "precision",
|
|
9931
|
-
"valueType": "number"
|
|
9932
|
-
}
|
|
9933
|
-
],
|
|
9973
|
+
"pressure-sensor": [{
|
|
9974
|
+
"path": "hpa",
|
|
9975
|
+
"valueType": "number"
|
|
9976
|
+
}],
|
|
9934
9977
|
"privacy-mask": [{
|
|
9935
9978
|
"path": "enabled",
|
|
9936
9979
|
"valueType": "boolean"
|
|
@@ -10241,20 +10284,10 @@ Object.freeze({
|
|
|
10241
10284
|
"path": "tampered",
|
|
10242
10285
|
"valueType": "boolean"
|
|
10243
10286
|
}],
|
|
10244
|
-
"temperature-sensor": [
|
|
10245
|
-
|
|
10246
|
-
|
|
10247
|
-
|
|
10248
|
-
},
|
|
10249
|
-
{
|
|
10250
|
-
"path": "unit",
|
|
10251
|
-
"valueType": "string"
|
|
10252
|
-
},
|
|
10253
|
-
{
|
|
10254
|
-
"path": "precision",
|
|
10255
|
-
"valueType": "number"
|
|
10256
|
-
}
|
|
10257
|
-
],
|
|
10287
|
+
"temperature-sensor": [{
|
|
10288
|
+
"path": "celsius",
|
|
10289
|
+
"valueType": "number"
|
|
10290
|
+
}],
|
|
10258
10291
|
"update": [
|
|
10259
10292
|
{
|
|
10260
10293
|
"path": "currentVersion",
|
|
@@ -10365,10 +10398,6 @@ Object.freeze({
|
|
|
10365
10398
|
"path": "temperature",
|
|
10366
10399
|
"valueType": "number"
|
|
10367
10400
|
},
|
|
10368
|
-
{
|
|
10369
|
-
"path": "temperatureUnit",
|
|
10370
|
-
"valueType": "string"
|
|
10371
|
-
},
|
|
10372
10401
|
{
|
|
10373
10402
|
"path": "humidity",
|
|
10374
10403
|
"valueType": "number"
|
|
@@ -10377,18 +10406,10 @@ Object.freeze({
|
|
|
10377
10406
|
"path": "pressure",
|
|
10378
10407
|
"valueType": "number"
|
|
10379
10408
|
},
|
|
10380
|
-
{
|
|
10381
|
-
"path": "pressureUnit",
|
|
10382
|
-
"valueType": "string"
|
|
10383
|
-
},
|
|
10384
10409
|
{
|
|
10385
10410
|
"path": "windSpeed",
|
|
10386
10411
|
"valueType": "number"
|
|
10387
10412
|
},
|
|
10388
|
-
{
|
|
10389
|
-
"path": "windSpeedUnit",
|
|
10390
|
-
"valueType": "string"
|
|
10391
|
-
},
|
|
10392
10413
|
{
|
|
10393
10414
|
"path": "windBearing",
|
|
10394
10415
|
"valueType": "number"
|
|
@@ -10805,7 +10826,7 @@ function errMsg(err) {
|
|
|
10805
10826
|
return String(err);
|
|
10806
10827
|
}
|
|
10807
10828
|
//#endregion
|
|
10808
|
-
//#region ../types/dist/composition-
|
|
10829
|
+
//#region ../types/dist/composition-OhpuespA.mjs
|
|
10809
10830
|
/**
|
|
10810
10831
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
10811
10832
|
*
|
|
@@ -11560,15 +11581,74 @@ function validateExpressionSource(src) {
|
|
|
11560
11581
|
}
|
|
11561
11582
|
return null;
|
|
11562
11583
|
}
|
|
11563
|
-
/**
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11584
|
+
/**
|
|
11585
|
+
* A composition source identity: `(addonId, stableId)`, never a numeric device
|
|
11586
|
+
* id. Split out so helper schemas can name it without a cycle through
|
|
11587
|
+
* `composition.ts` (D659).
|
|
11588
|
+
*/
|
|
11567
11589
|
var CompositionSourceRefSchema = object({
|
|
11568
11590
|
/** The owning addon; `stableId` is unique only within it. */
|
|
11569
11591
|
addonId: string().min(1),
|
|
11570
11592
|
stableId: string().min(1)
|
|
11571
11593
|
});
|
|
11594
|
+
var GROUP_TEMPLATE_IDS = [
|
|
11595
|
+
"temperature",
|
|
11596
|
+
"humidity",
|
|
11597
|
+
"pressure",
|
|
11598
|
+
"illuminance",
|
|
11599
|
+
"power",
|
|
11600
|
+
"energy",
|
|
11601
|
+
"battery",
|
|
11602
|
+
"numeric",
|
|
11603
|
+
"motion",
|
|
11604
|
+
"contact",
|
|
11605
|
+
"leak",
|
|
11606
|
+
"smoke",
|
|
11607
|
+
"occupancy",
|
|
11608
|
+
"binary",
|
|
11609
|
+
"switch",
|
|
11610
|
+
"light"
|
|
11611
|
+
];
|
|
11612
|
+
var NUMERIC_AGGREGATES = [
|
|
11613
|
+
"mean",
|
|
11614
|
+
"median",
|
|
11615
|
+
"min",
|
|
11616
|
+
"max",
|
|
11617
|
+
"sum"
|
|
11618
|
+
];
|
|
11619
|
+
var BINARY_AGGREGATES = ["any", "all"];
|
|
11620
|
+
/** How an actuator group decides it is on. Unknown members never count as off. */
|
|
11621
|
+
var ACTIVE_CRITERIA = ["at-least-one", "all"];
|
|
11622
|
+
var AGGREGATE_REDUCERS = [
|
|
11623
|
+
"mean",
|
|
11624
|
+
"median",
|
|
11625
|
+
"min",
|
|
11626
|
+
"max",
|
|
11627
|
+
"sum",
|
|
11628
|
+
"any",
|
|
11629
|
+
"all"
|
|
11630
|
+
];
|
|
11631
|
+
var GroupTemplateIdSchema = _enum(GROUP_TEMPLATE_IDS);
|
|
11632
|
+
var AggregateReducerSchema = _enum(AGGREGATE_REDUCERS);
|
|
11633
|
+
var ActiveCriterionSchema = _enum(ACTIVE_CRITERIA);
|
|
11634
|
+
/** One member. The field is stored so occupancy can name motion or binary. */
|
|
11635
|
+
var HelperMemberSchema = object({
|
|
11636
|
+
source: CompositionSourceRefSchema,
|
|
11637
|
+
cap: string().min(1),
|
|
11638
|
+
fieldPath: string().min(1)
|
|
11639
|
+
});
|
|
11640
|
+
var HelperSpecSchema = discriminatedUnion("kind", [object({
|
|
11641
|
+
kind: literal("group"),
|
|
11642
|
+
templateId: GroupTemplateIdSchema,
|
|
11643
|
+
aggregate: AggregateReducerSchema,
|
|
11644
|
+
members: array(HelperMemberSchema).min(1).max(64),
|
|
11645
|
+
/** Actuator groups only. Readings leave this unset. */
|
|
11646
|
+
active: ActiveCriterionSchema.optional()
|
|
11647
|
+
})]);
|
|
11648
|
+
/** A composed device's stableId is this prefix plus the block id. */
|
|
11649
|
+
var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
|
|
11650
|
+
/** Longest snippet a `code` source may carry (slice 4). */
|
|
11651
|
+
var MAX_COMPOSITION_CODE_LENGTH = 2e4;
|
|
11572
11652
|
/** One field of one capability of one source device. */
|
|
11573
11653
|
var CompositionFieldReadSchema = object({
|
|
11574
11654
|
source: CompositionSourceRefSchema,
|
|
@@ -11612,7 +11692,33 @@ var CompositionCodeSourceSchema = object({
|
|
|
11612
11692
|
var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
11613
11693
|
CompositionFromSourceSchema,
|
|
11614
11694
|
CompositionExpressionSourceSchema,
|
|
11615
|
-
CompositionCodeSourceSchema
|
|
11695
|
+
CompositionCodeSourceSchema,
|
|
11696
|
+
object({
|
|
11697
|
+
kind: literal("aggregate"),
|
|
11698
|
+
reducer: _enum([
|
|
11699
|
+
"mean",
|
|
11700
|
+
"median",
|
|
11701
|
+
"min",
|
|
11702
|
+
"max",
|
|
11703
|
+
"sum",
|
|
11704
|
+
"any",
|
|
11705
|
+
"all"
|
|
11706
|
+
]),
|
|
11707
|
+
reads: array(CompositionFieldReadSchema).min(1).max(64),
|
|
11708
|
+
/** `value` is the reading; the others are the member counts. */
|
|
11709
|
+
publish: _enum([
|
|
11710
|
+
"value",
|
|
11711
|
+
"included",
|
|
11712
|
+
"excluded",
|
|
11713
|
+
"total"
|
|
11714
|
+
]),
|
|
11715
|
+
/** What a `value` publish does when nobody is included. Counts ignore this. */
|
|
11716
|
+
empty: _enum([
|
|
11717
|
+
"null",
|
|
11718
|
+
"omit",
|
|
11719
|
+
"offline"
|
|
11720
|
+
])
|
|
11721
|
+
})
|
|
11616
11722
|
]);
|
|
11617
11723
|
var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
|
|
11618
11724
|
kind: literal("forward"),
|
|
@@ -11658,7 +11764,13 @@ var CompositionSchema = object({
|
|
|
11658
11764
|
kind: literal("existing"),
|
|
11659
11765
|
device: CompositionSourceRefSchema
|
|
11660
11766
|
})]),
|
|
11661
|
-
features: array(CompositionFeatureSchema).min(1).max(16)
|
|
11767
|
+
features: array(CompositionFeatureSchema).min(1).max(16),
|
|
11768
|
+
/**
|
|
11769
|
+
* Present when the block is a helper template. Absent on a free
|
|
11770
|
+
* composition (the room example, device 4603). The editor reads this,
|
|
11771
|
+
* never the compiled features.
|
|
11772
|
+
*/
|
|
11773
|
+
helper: HelperSpecSchema.optional()
|
|
11662
11774
|
}).superRefine((composition, ctx) => {
|
|
11663
11775
|
const seen = /* @__PURE__ */ new Set();
|
|
11664
11776
|
composition.features.forEach((feature, index) => {
|
|
@@ -17866,7 +17978,11 @@ var CompositionProblemSchema = object({
|
|
|
17866
17978
|
"item-field-reserved",
|
|
17867
17979
|
"target-missing",
|
|
17868
17980
|
"target-unreadable",
|
|
17869
|
-
"not-replaceable"
|
|
17981
|
+
"not-replaceable",
|
|
17982
|
+
"native-capability-replace",
|
|
17983
|
+
"mixed-quantity",
|
|
17984
|
+
"helper-mismatch",
|
|
17985
|
+
"member-constraint"
|
|
17870
17986
|
]),
|
|
17871
17987
|
path: string(),
|
|
17872
17988
|
message: string()
|
|
@@ -17887,7 +18003,8 @@ var CompositionUnavailableModeSchema = _enum([
|
|
|
17887
18003
|
"null",
|
|
17888
18004
|
"offline",
|
|
17889
18005
|
"unknown",
|
|
17890
|
-
"release"
|
|
18006
|
+
"release",
|
|
18007
|
+
"omit"
|
|
17891
18008
|
]);
|
|
17892
18009
|
/**
|
|
17893
18010
|
* `derived` reads a source · `clock` reads only `now`, recomputed when a sibling
|
|
@@ -17924,9 +18041,11 @@ var CompositionFieldPlanSchema = object({
|
|
|
17924
18041
|
reads: array(CompositionFieldReadSchema)
|
|
17925
18042
|
});
|
|
17926
18043
|
/**
|
|
17927
|
-
* What a feature does to its target (D663): `new` builds a capability on a
|
|
17928
|
-
* device; on an existing device it `add`s a capability the device lacks
|
|
17929
|
-
*
|
|
18044
|
+
* What a feature does to its target (D663, D944): `new` builds a capability on a
|
|
18045
|
+
* new device; on an existing device it `add`s a capability the device lacks.
|
|
18046
|
+
* Replacing a capability the device already serves is refused
|
|
18047
|
+
* (`native-capability-replace`), not planned. `replace` remains on the claim
|
|
18048
|
+
* wire for a claim the hub already holds.
|
|
17930
18049
|
*/
|
|
17931
18050
|
var CompositionFeatureModeSchema = _enum([
|
|
17932
18051
|
"new",
|
|
@@ -18099,6 +18218,275 @@ Object.freeze({
|
|
|
18099
18218
|
result: ["number", "null"]
|
|
18100
18219
|
}
|
|
18101
18220
|
});
|
|
18221
|
+
/**
|
|
18222
|
+
* One catalog row per base reading a group helper can build (D944 §3).
|
|
18223
|
+
* Data, not a form default written into the block.
|
|
18224
|
+
*/
|
|
18225
|
+
var NOW = {
|
|
18226
|
+
fieldPath: "lastFetchedAt",
|
|
18227
|
+
expr: "now"
|
|
18228
|
+
};
|
|
18229
|
+
var CHANGED = {
|
|
18230
|
+
fieldPath: "lastChangedAt",
|
|
18231
|
+
expr: "now"
|
|
18232
|
+
};
|
|
18233
|
+
function numeric(input) {
|
|
18234
|
+
return {
|
|
18235
|
+
...input,
|
|
18236
|
+
family: "numeric",
|
|
18237
|
+
deviceType: DeviceType.Sensor,
|
|
18238
|
+
memberDeviceType: DeviceType.Sensor,
|
|
18239
|
+
commandCaps: [],
|
|
18240
|
+
baseUnit: baseUnitOf(input.quantity),
|
|
18241
|
+
aggregates: NUMERIC_AGGREGATES,
|
|
18242
|
+
accept: [{
|
|
18243
|
+
cap: input.outputCap,
|
|
18244
|
+
fieldPath: input.outputField
|
|
18245
|
+
}]
|
|
18246
|
+
};
|
|
18247
|
+
}
|
|
18248
|
+
function binary(input) {
|
|
18249
|
+
return {
|
|
18250
|
+
...input,
|
|
18251
|
+
family: "binary",
|
|
18252
|
+
deviceType: DeviceType.Sensor,
|
|
18253
|
+
memberDeviceType: DeviceType.Sensor,
|
|
18254
|
+
commandCaps: [],
|
|
18255
|
+
quantity: "none",
|
|
18256
|
+
baseUnit: void 0,
|
|
18257
|
+
aggregates: BINARY_AGGREGATES,
|
|
18258
|
+
empty: "offline"
|
|
18259
|
+
};
|
|
18260
|
+
}
|
|
18261
|
+
var GROUP_TEMPLATES = [
|
|
18262
|
+
numeric({
|
|
18263
|
+
id: "temperature",
|
|
18264
|
+
summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
|
|
18265
|
+
role: DeviceRole.TemperatureSensor,
|
|
18266
|
+
outputCap: "temperature-sensor",
|
|
18267
|
+
outputField: "celsius",
|
|
18268
|
+
quantity: "temperature",
|
|
18269
|
+
defaultAggregate: "mean",
|
|
18270
|
+
empty: "null",
|
|
18271
|
+
companions: [NOW]
|
|
18272
|
+
}),
|
|
18273
|
+
numeric({
|
|
18274
|
+
id: "humidity",
|
|
18275
|
+
summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18276
|
+
role: DeviceRole.HumiditySensor,
|
|
18277
|
+
outputCap: "humidity-sensor",
|
|
18278
|
+
outputField: "percent",
|
|
18279
|
+
quantity: "humidity",
|
|
18280
|
+
defaultAggregate: "mean",
|
|
18281
|
+
empty: "null",
|
|
18282
|
+
companions: [NOW]
|
|
18283
|
+
}),
|
|
18284
|
+
numeric({
|
|
18285
|
+
id: "pressure",
|
|
18286
|
+
summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
|
|
18287
|
+
role: DeviceRole.PressureSensor,
|
|
18288
|
+
outputCap: "pressure-sensor",
|
|
18289
|
+
outputField: "hpa",
|
|
18290
|
+
quantity: "pressure",
|
|
18291
|
+
defaultAggregate: "mean",
|
|
18292
|
+
empty: "null",
|
|
18293
|
+
companions: [NOW]
|
|
18294
|
+
}),
|
|
18295
|
+
numeric({
|
|
18296
|
+
id: "illuminance",
|
|
18297
|
+
summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
|
|
18298
|
+
role: DeviceRole.AmbientLightSensor,
|
|
18299
|
+
outputCap: "ambient-light-sensor",
|
|
18300
|
+
outputField: "lux",
|
|
18301
|
+
quantity: "illuminance",
|
|
18302
|
+
defaultAggregate: "mean",
|
|
18303
|
+
empty: "null",
|
|
18304
|
+
companions: [NOW]
|
|
18305
|
+
}),
|
|
18306
|
+
numeric({
|
|
18307
|
+
id: "power",
|
|
18308
|
+
summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
|
|
18309
|
+
role: DeviceRole.PowerSensor,
|
|
18310
|
+
outputCap: "power-meter",
|
|
18311
|
+
outputField: "watts",
|
|
18312
|
+
quantity: "power",
|
|
18313
|
+
defaultAggregate: "sum",
|
|
18314
|
+
empty: "omit",
|
|
18315
|
+
companions: [NOW]
|
|
18316
|
+
}),
|
|
18317
|
+
numeric({
|
|
18318
|
+
id: "energy",
|
|
18319
|
+
summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
|
|
18320
|
+
role: DeviceRole.EnergySensor,
|
|
18321
|
+
outputCap: "power-meter",
|
|
18322
|
+
outputField: "kwhTotal",
|
|
18323
|
+
quantity: "energy",
|
|
18324
|
+
defaultAggregate: "sum",
|
|
18325
|
+
empty: "omit",
|
|
18326
|
+
companions: [NOW]
|
|
18327
|
+
}),
|
|
18328
|
+
numeric({
|
|
18329
|
+
id: "battery",
|
|
18330
|
+
summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18331
|
+
role: DeviceRole.BatterySensor,
|
|
18332
|
+
outputCap: "battery",
|
|
18333
|
+
outputField: "percentage",
|
|
18334
|
+
quantity: "percentage",
|
|
18335
|
+
defaultAggregate: "mean",
|
|
18336
|
+
empty: "null",
|
|
18337
|
+
companions: [
|
|
18338
|
+
{
|
|
18339
|
+
fieldPath: "charging",
|
|
18340
|
+
expr: "\"unknown\""
|
|
18341
|
+
},
|
|
18342
|
+
{
|
|
18343
|
+
fieldPath: "sleeping",
|
|
18344
|
+
expr: "false"
|
|
18345
|
+
},
|
|
18346
|
+
{
|
|
18347
|
+
fieldPath: "lastUpdated",
|
|
18348
|
+
expr: "now"
|
|
18349
|
+
}
|
|
18350
|
+
]
|
|
18351
|
+
}),
|
|
18352
|
+
numeric({
|
|
18353
|
+
id: "numeric",
|
|
18354
|
+
summary: "A numeric reading in the members’ shared quantity. Mixed quantities are refused. Nobody reporting publishes null, not 0.",
|
|
18355
|
+
role: DeviceRole.NumericSensor,
|
|
18356
|
+
outputCap: "numeric-sensor",
|
|
18357
|
+
outputField: "value",
|
|
18358
|
+
quantity: "device",
|
|
18359
|
+
defaultAggregate: "mean",
|
|
18360
|
+
empty: "null",
|
|
18361
|
+
companions: [NOW]
|
|
18362
|
+
}),
|
|
18363
|
+
binary({
|
|
18364
|
+
id: "motion",
|
|
18365
|
+
summary: "Any or all motion. Offline and unknown members are left out, never read as off. Nobody reporting takes the device offline.",
|
|
18366
|
+
role: DeviceRole.MotionSensor,
|
|
18367
|
+
outputCap: "motion",
|
|
18368
|
+
outputField: "detected",
|
|
18369
|
+
defaultAggregate: "any",
|
|
18370
|
+
accept: [{
|
|
18371
|
+
cap: "motion",
|
|
18372
|
+
fieldPath: "detected"
|
|
18373
|
+
}],
|
|
18374
|
+
companions: [{
|
|
18375
|
+
fieldPath: "lastDetectedAt",
|
|
18376
|
+
expr: "null"
|
|
18377
|
+
}, {
|
|
18378
|
+
fieldPath: "autoClearAfterMs",
|
|
18379
|
+
expr: "null"
|
|
18380
|
+
}]
|
|
18381
|
+
}),
|
|
18382
|
+
binary({
|
|
18383
|
+
id: "contact",
|
|
18384
|
+
summary: "Any or all open contacts. Offline and unknown members are left out, never read as closed.",
|
|
18385
|
+
role: DeviceRole.ContactSensor,
|
|
18386
|
+
outputCap: "contact",
|
|
18387
|
+
outputField: "entryOpen",
|
|
18388
|
+
defaultAggregate: "any",
|
|
18389
|
+
accept: [{
|
|
18390
|
+
cap: "contact",
|
|
18391
|
+
fieldPath: "entryOpen"
|
|
18392
|
+
}],
|
|
18393
|
+
companions: [CHANGED]
|
|
18394
|
+
}),
|
|
18395
|
+
binary({
|
|
18396
|
+
id: "leak",
|
|
18397
|
+
summary: "Any or all leak detections. Offline and unknown members are left out, never read as dry.",
|
|
18398
|
+
role: DeviceRole.LeakSensor,
|
|
18399
|
+
outputCap: "flood",
|
|
18400
|
+
outputField: "flooded",
|
|
18401
|
+
defaultAggregate: "any",
|
|
18402
|
+
accept: [{
|
|
18403
|
+
cap: "flood",
|
|
18404
|
+
fieldPath: "flooded"
|
|
18405
|
+
}],
|
|
18406
|
+
companions: [CHANGED]
|
|
18407
|
+
}),
|
|
18408
|
+
binary({
|
|
18409
|
+
id: "smoke",
|
|
18410
|
+
summary: "Any or all smoke detections. Offline and unknown members are left out, never read as clear.",
|
|
18411
|
+
role: DeviceRole.SmokeSensor,
|
|
18412
|
+
outputCap: "smoke",
|
|
18413
|
+
outputField: "detected",
|
|
18414
|
+
defaultAggregate: "any",
|
|
18415
|
+
accept: [{
|
|
18416
|
+
cap: "smoke",
|
|
18417
|
+
fieldPath: "detected"
|
|
18418
|
+
}],
|
|
18419
|
+
companions: [CHANGED]
|
|
18420
|
+
}),
|
|
18421
|
+
binary({
|
|
18422
|
+
id: "occupancy",
|
|
18423
|
+
summary: "Any or all of motion or binary sensors, published as a binary. Presence zones are not aggregated. Nobody reporting is offline, not off.",
|
|
18424
|
+
role: DeviceRole.BinarySensor,
|
|
18425
|
+
outputCap: "binary",
|
|
18426
|
+
outputField: "on",
|
|
18427
|
+
defaultAggregate: "any",
|
|
18428
|
+
accept: [{
|
|
18429
|
+
cap: "motion",
|
|
18430
|
+
fieldPath: "detected"
|
|
18431
|
+
}, {
|
|
18432
|
+
cap: "binary",
|
|
18433
|
+
fieldPath: "on"
|
|
18434
|
+
}],
|
|
18435
|
+
companions: [CHANGED]
|
|
18436
|
+
}),
|
|
18437
|
+
binary({
|
|
18438
|
+
id: "binary",
|
|
18439
|
+
summary: "Any or all generic binary sensors. Offline and unknown members are left out, never read as off.",
|
|
18440
|
+
role: DeviceRole.BinarySensor,
|
|
18441
|
+
outputCap: "binary",
|
|
18442
|
+
outputField: "on",
|
|
18443
|
+
defaultAggregate: "any",
|
|
18444
|
+
accept: [{
|
|
18445
|
+
cap: "binary",
|
|
18446
|
+
fieldPath: "on"
|
|
18447
|
+
}],
|
|
18448
|
+
companions: [CHANGED]
|
|
18449
|
+
}),
|
|
18450
|
+
actuator({
|
|
18451
|
+
id: "switch",
|
|
18452
|
+
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.",
|
|
18453
|
+
deviceType: DeviceType.Switch,
|
|
18454
|
+
outputCap: "switch",
|
|
18455
|
+
commandCaps: ["switch"]
|
|
18456
|
+
}),
|
|
18457
|
+
actuator({
|
|
18458
|
+
id: "light",
|
|
18459
|
+
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.",
|
|
18460
|
+
deviceType: DeviceType.Light,
|
|
18461
|
+
outputCap: "switch",
|
|
18462
|
+
commandCaps: [
|
|
18463
|
+
"switch",
|
|
18464
|
+
"brightness",
|
|
18465
|
+
"color"
|
|
18466
|
+
]
|
|
18467
|
+
})
|
|
18468
|
+
];
|
|
18469
|
+
function actuator(input) {
|
|
18470
|
+
return {
|
|
18471
|
+
...input,
|
|
18472
|
+
family: "actuator",
|
|
18473
|
+
role: void 0,
|
|
18474
|
+
memberDeviceType: input.deviceType,
|
|
18475
|
+
outputField: "on",
|
|
18476
|
+
quantity: "none",
|
|
18477
|
+
baseUnit: void 0,
|
|
18478
|
+
aggregates: BINARY_AGGREGATES,
|
|
18479
|
+
defaultAggregate: "any",
|
|
18480
|
+
empty: "offline",
|
|
18481
|
+
accept: [{
|
|
18482
|
+
cap: "switch",
|
|
18483
|
+
fieldPath: "on"
|
|
18484
|
+
}],
|
|
18485
|
+
companions: [CHANGED]
|
|
18486
|
+
};
|
|
18487
|
+
}
|
|
18488
|
+
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
18489
|
+
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
18102
18490
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
18103
18491
|
/**
|
|
18104
18492
|
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
@@ -19925,12 +20313,12 @@ function buildDeviceAdminUrl(parts) {
|
|
|
19925
20313
|
* providers detect renames by matching `uniqueId` and patch `id`
|
|
19926
20314
|
* in-place so dispatch keeps working.
|
|
19927
20315
|
*
|
|
19928
|
-
* **
|
|
19929
|
-
*
|
|
19930
|
-
*
|
|
19931
|
-
*
|
|
19932
|
-
* (`ha-domain-mapping`)
|
|
19933
|
-
* stored after that point.
|
|
20316
|
+
* **Display unit and precision are NOT stored here.** They are a read-time
|
|
20317
|
+
* preference (D944). A polymorphic reading stores its `quantity` here once,
|
|
20318
|
+
* at adopt, and a date/time value stores `dateTimeShape` the same way —
|
|
20319
|
+
* neither is repeated on each sample. The `device_class` tag is an
|
|
20320
|
+
* HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
|
|
20321
|
+
* to derive the CamStack `DeviceRole`; it is never stored after that point.
|
|
19934
20322
|
*
|
|
19935
20323
|
* The `raw` escape hatch carries provider-specific attributes that don't
|
|
19936
20324
|
* fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
|
|
@@ -19944,6 +20332,14 @@ var SourceInfoSchema = object({
|
|
|
19944
20332
|
/** Immutable upstream identifier when available (HA `unique_id`, … ).
|
|
19945
20333
|
* Used to detect rename when `id` changes. */
|
|
19946
20334
|
uniqueId: string().optional(),
|
|
20335
|
+
/** Quantity of a polymorphic reading, written once at adopt (D944). */
|
|
20336
|
+
quantity: ReadingQuantitySchema.optional(),
|
|
20337
|
+
/** Date / time / datetime shape of a string reading, written once at adopt. */
|
|
20338
|
+
dateTimeShape: _enum([
|
|
20339
|
+
"date",
|
|
20340
|
+
"time",
|
|
20341
|
+
"datetime"
|
|
20342
|
+
]).optional(),
|
|
19947
20343
|
/** Provider-specific extras that don't fit the structured slots.
|
|
19948
20344
|
* Whitelist what goes here per provider — do NOT dump entire HA
|
|
19949
20345
|
* attribute blobs (would explode the meta JSON column). */
|
|
@@ -19989,6 +20385,80 @@ function mergeSourceInfo(prev, patch) {
|
|
|
19989
20385
|
return next;
|
|
19990
20386
|
}
|
|
19991
20387
|
/**
|
|
20388
|
+
* Coerce common unit-of-measurement string variants to a canonical form
|
|
20389
|
+
* so the UI's display templates can rely on stable strings. Pure
|
|
20390
|
+
* passthrough for unknown units — kept conservative on purpose, the goal
|
|
20391
|
+
* is to normalise the long tail of HA `unit_of_measurement` values that
|
|
20392
|
+
* have multiple historical spellings (`'°C'` vs `'C'` vs `'celsius'`).
|
|
20393
|
+
*
|
|
20394
|
+
* Extend the map as we encounter real-world data. Returning the raw
|
|
20395
|
+
* input for unknown values is intentional: providers should never
|
|
20396
|
+
* lose information silently here.
|
|
20397
|
+
*/
|
|
20398
|
+
function normalizeUnit(raw) {
|
|
20399
|
+
if (raw == null) return void 0;
|
|
20400
|
+
const trimmed = raw.trim();
|
|
20401
|
+
if (trimmed === "") return void 0;
|
|
20402
|
+
const lower = trimmed.toLowerCase();
|
|
20403
|
+
if (lower === "°c" || lower === "c" || lower === "celsius") return "°C";
|
|
20404
|
+
if (lower === "°f" || lower === "f" || lower === "fahrenheit") return "°F";
|
|
20405
|
+
if (lower === "k" || lower === "kelvin") return "K";
|
|
20406
|
+
if (lower === "%" || lower === "percent" || lower === "percentage") return "%";
|
|
20407
|
+
if (lower === "lx" || lower === "lux") return "lx";
|
|
20408
|
+
if (lower === "hpa" || lower === "hectopascal") return "hPa";
|
|
20409
|
+
if (lower === "kpa") return "kPa";
|
|
20410
|
+
if (lower === "pa") return "Pa";
|
|
20411
|
+
if (lower === "mbar" || lower === "millibar") return "mbar";
|
|
20412
|
+
if (lower === "w" || lower === "watt") return "W";
|
|
20413
|
+
if (lower === "kw" || lower === "kilowatt") return "kW";
|
|
20414
|
+
if (lower === "wh" || lower === "watt-hour") return "Wh";
|
|
20415
|
+
if (lower === "kwh" || lower === "kilowatt-hour") return "kWh";
|
|
20416
|
+
if (lower === "v" || lower === "volt") return "V";
|
|
20417
|
+
if (lower === "a" || lower === "amp" || lower === "ampere") return "A";
|
|
20418
|
+
if (lower === "ma" || lower === "milliampere") return "mA";
|
|
20419
|
+
if (lower === "va") return "VA";
|
|
20420
|
+
if (lower === "ppm") return "ppm";
|
|
20421
|
+
if (lower === "ppb") return "ppb";
|
|
20422
|
+
if (lower === "µg/m³" || lower === "ug/m3" || lower === "µg/m3") return "µg/m³";
|
|
20423
|
+
if (lower === "km/h" || lower === "kph") return "km/h";
|
|
20424
|
+
if (lower === "mph") return "mph";
|
|
20425
|
+
if (lower === "m/s") return "m/s";
|
|
20426
|
+
if (lower === "m") return "m";
|
|
20427
|
+
if (lower === "m²" || lower === "m2") return "m²";
|
|
20428
|
+
if (lower === "mm") return "mm";
|
|
20429
|
+
if (lower === "cm") return "cm";
|
|
20430
|
+
if (lower === "km") return "km";
|
|
20431
|
+
if (lower === "in" || lower === "inch" || lower === "inches") return "in";
|
|
20432
|
+
if (lower === "ft" || lower === "foot" || lower === "feet") return "ft";
|
|
20433
|
+
if (lower === "kg") return "kg";
|
|
20434
|
+
if (lower === "g") return "g";
|
|
20435
|
+
if (lower === "l" || lower === "liter" || lower === "litre" || lower === "liters" || lower === "litres") return "L";
|
|
20436
|
+
if (lower === "ml") return "mL";
|
|
20437
|
+
if (lower === "gal" || lower === "gallon" || lower === "gallons") return "gal";
|
|
20438
|
+
if (lower === "s" || lower === "sec" || lower === "second" || lower === "seconds") return "s";
|
|
20439
|
+
if (lower === "ms") return "ms";
|
|
20440
|
+
if (lower === "min" || lower === "minute" || lower === "minutes") return "min";
|
|
20441
|
+
if (lower === "h" || lower === "hr" || lower === "hour" || lower === "hours") return "h";
|
|
20442
|
+
if (lower === "d" || lower === "day" || lower === "days") return "d";
|
|
20443
|
+
if (lower === "b" || lower === "byte" || lower === "bytes") return "B";
|
|
20444
|
+
if (lower === "kib") return "KiB";
|
|
20445
|
+
if (lower === "mib") return "MiB";
|
|
20446
|
+
if (lower === "gib") return "GiB";
|
|
20447
|
+
if (lower === "tib") return "TiB";
|
|
20448
|
+
if (lower === "kb") return "kB";
|
|
20449
|
+
if (lower === "mb") return "MB";
|
|
20450
|
+
if (lower === "gb") return "GB";
|
|
20451
|
+
if (lower === "dbm") return "dBm";
|
|
20452
|
+
if (lower === "dbfs") return "dBFS";
|
|
20453
|
+
if (lower === "inhg") return "inHg";
|
|
20454
|
+
if (lower === "mm/h") return "mm/h";
|
|
20455
|
+
if (lower === "w/m²" || lower === "w/m2") return "W/m²";
|
|
20456
|
+
if (lower === "°" || lower === "deg" || lower === "degree" || lower === "degrees") return "°";
|
|
20457
|
+
if (lower === "kbps") return "kbps";
|
|
20458
|
+
if (lower === "fps") return "fps";
|
|
20459
|
+
return trimmed;
|
|
20460
|
+
}
|
|
20461
|
+
/**
|
|
19992
20462
|
* Key under which `SourceInfo` lives inside `DeviceMeta.metadata`. The
|
|
19993
20463
|
* blob is a free-form `Record<string, unknown>` shared with hardware-
|
|
19994
20464
|
* identity fields (`manufacturer`, `model`, `firmware`, …); SourceInfo
|
|
@@ -41048,7 +41518,8 @@ method(object({
|
|
|
41048
41518
|
"enum"
|
|
41049
41519
|
]),
|
|
41050
41520
|
enumValues: array(string()).optional(),
|
|
41051
|
-
item: boolean().optional()
|
|
41521
|
+
item: boolean().optional(),
|
|
41522
|
+
quantity: ReadingQuantitySchema.optional()
|
|
41052
41523
|
})).readonly(),
|
|
41053
41524
|
itemArray: object({
|
|
41054
41525
|
path: string(),
|
|
@@ -44892,8 +45363,8 @@ var MaskPolygonShapeSchema = object({
|
|
|
44892
45363
|
/** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
|
|
44893
45364
|
var MaskGridShapeSchema = object({
|
|
44894
45365
|
kind: literal("grid"),
|
|
44895
|
-
gridWidth: number(),
|
|
44896
|
-
gridHeight: number(),
|
|
45366
|
+
gridWidth: number().meta(readingMeta("none")),
|
|
45367
|
+
gridHeight: number().meta(readingMeta("none")),
|
|
44897
45368
|
cells: array(boolean())
|
|
44898
45369
|
});
|
|
44899
45370
|
discriminatedUnion("kind", [
|
|
@@ -45005,6 +45476,8 @@ var NcSystemEventKindSchema = _enum([
|
|
|
45005
45476
|
"detection-blind",
|
|
45006
45477
|
"motion-noisy",
|
|
45007
45478
|
"motion-calm",
|
|
45479
|
+
"package-llm-silent",
|
|
45480
|
+
"package-llm-answered",
|
|
45008
45481
|
"recording-stalled",
|
|
45009
45482
|
"recording-healthy",
|
|
45010
45483
|
"recording-degraded",
|
|
@@ -56096,6 +56569,28 @@ var PackageVersionInfoSchema = object({
|
|
|
56096
56569
|
deprecated: string().optional(),
|
|
56097
56570
|
distTags: array(string()).readonly()
|
|
56098
56571
|
});
|
|
56572
|
+
/**
|
|
56573
|
+
* One published document. `ok` is markdown from the registry. `missing` means
|
|
56574
|
+
* the registry answered and the file was not there. `error` is a registry or
|
|
56575
|
+
* tarball failure — never an empty string standing in for either.
|
|
56576
|
+
*/
|
|
56577
|
+
var PackageDocBodySchema = discriminatedUnion("status", [
|
|
56578
|
+
object({
|
|
56579
|
+
status: literal("ok"),
|
|
56580
|
+
markdown: string()
|
|
56581
|
+
}),
|
|
56582
|
+
object({ status: literal("missing") }),
|
|
56583
|
+
object({
|
|
56584
|
+
status: literal("error"),
|
|
56585
|
+
message: string()
|
|
56586
|
+
})
|
|
56587
|
+
]);
|
|
56588
|
+
var PackageDocsSchema = object({
|
|
56589
|
+
name: string(),
|
|
56590
|
+
version: string(),
|
|
56591
|
+
readme: PackageDocBodySchema,
|
|
56592
|
+
changelog: PackageDocBodySchema
|
|
56593
|
+
});
|
|
56099
56594
|
var SearchResultSchema = object({
|
|
56100
56595
|
name: string(),
|
|
56101
56596
|
version: string(),
|
|
@@ -56314,7 +56809,10 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
56314
56809
|
}), object({ success: literal(true) }), {
|
|
56315
56810
|
kind: "mutation",
|
|
56316
56811
|
auth: "admin"
|
|
56317
|
-
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
|
|
56812
|
+
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
|
|
56813
|
+
name: string().min(1),
|
|
56814
|
+
version: string().min(1).optional()
|
|
56815
|
+
}), PackageDocsSchema, { auth: "admin" }), method(object({ addonId: string() }), RestartAddonResultSchema, {
|
|
56318
56816
|
kind: "mutation",
|
|
56319
56817
|
auth: "admin"
|
|
56320
56818
|
}), method(object({ packageName: string() }), object({ success: literal(true) }), {
|
|
@@ -56373,27 +56871,17 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
56373
56871
|
*/
|
|
56374
56872
|
var AirQualitySensorStatusSchema = object({
|
|
56375
56873
|
/** Carbon dioxide concentration in ppm. */
|
|
56376
|
-
co2Ppm: number().min(0).optional(),
|
|
56874
|
+
co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
56377
56875
|
/** Total volatile organic compounds in ppb. */
|
|
56378
|
-
vocPpb: number().min(0).optional(),
|
|
56876
|
+
vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
56379
56877
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
56380
|
-
pm25: number().min(0).optional(),
|
|
56878
|
+
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56381
56879
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
56382
|
-
pm10: number().min(0).optional(),
|
|
56880
|
+
pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56383
56881
|
/** Composite AQI value (typically 0..500). */
|
|
56384
|
-
aqi: number().optional(),
|
|
56882
|
+
aqi: number().optional().meta(readingMeta("index")),
|
|
56385
56883
|
/** Ms epoch when the slice was last updated. */
|
|
56386
|
-
lastFetchedAt: number()
|
|
56387
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
56388
|
-
* `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
|
|
56389
|
-
* upstream `sensor.*` entity surfaces ONE device_class, so one unit
|
|
56390
|
-
* per slice is unambiguous. */
|
|
56391
|
-
unit: string().optional(),
|
|
56392
|
-
/** Suggested decimal places for numeric display.
|
|
56393
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
56394
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
56395
|
-
* auto-formatting when absent. */
|
|
56396
|
-
precision: number().int().min(0).max(10).optional()
|
|
56884
|
+
lastFetchedAt: number()
|
|
56397
56885
|
});
|
|
56398
56886
|
var airQualitySensorCapability = {
|
|
56399
56887
|
name: "air-quality-sensor",
|
|
@@ -56430,12 +56918,12 @@ var airQualitySensorCapability = {
|
|
|
56430
56918
|
},
|
|
56431
56919
|
pm25: {
|
|
56432
56920
|
kind: "gauge",
|
|
56433
|
-
unit: "
|
|
56921
|
+
unit: "µg/m³",
|
|
56434
56922
|
deadband: 1
|
|
56435
56923
|
},
|
|
56436
56924
|
pm10: {
|
|
56437
56925
|
kind: "gauge",
|
|
56438
|
-
unit: "
|
|
56926
|
+
unit: "µg/m³",
|
|
56439
56927
|
deadband: 1
|
|
56440
56928
|
},
|
|
56441
56929
|
aqi: {
|
|
@@ -56453,22 +56941,13 @@ var airQualitySensorCapability = {
|
|
|
56453
56941
|
* entries with `device_class: illuminance`.
|
|
56454
56942
|
*/
|
|
56455
56943
|
var AmbientLightSensorStatusSchema = object({
|
|
56456
|
-
/** Current illuminance in lux (lx). */
|
|
56457
|
-
lux: number().min(0).meta({
|
|
56458
|
-
|
|
56944
|
+
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
56945
|
+
lux: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
56946
|
+
precision: 0,
|
|
56459
56947
|
label: "Illuminance"
|
|
56460
|
-
}),
|
|
56948
|
+
})),
|
|
56461
56949
|
/** Ms epoch when the slice was last updated. */
|
|
56462
|
-
lastFetchedAt: number()
|
|
56463
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
56464
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
56465
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
56466
|
-
unit: string().optional(),
|
|
56467
|
-
/** Suggested decimal places for numeric display.
|
|
56468
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
56469
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
56470
|
-
* auto-formatting when absent. */
|
|
56471
|
-
precision: number().int().min(0).max(10).optional()
|
|
56950
|
+
lastFetchedAt: number()
|
|
56472
56951
|
});
|
|
56473
56952
|
var ambientLightSensorCapability = {
|
|
56474
56953
|
name: "ambient-light-sensor",
|
|
@@ -56500,12 +56979,7 @@ var ambientLightSensorCapability = {
|
|
|
56500
56979
|
} } },
|
|
56501
56980
|
/** Clock fields: written, but excluded from the compare that decides
|
|
56502
56981
|
* whether persisting is worth a SQLite commit. */
|
|
56503
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
56504
|
-
customize: {
|
|
56505
|
-
version: 1,
|
|
56506
|
-
advanced: "derive",
|
|
56507
|
-
targets: []
|
|
56508
|
-
}
|
|
56982
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
56509
56983
|
};
|
|
56510
56984
|
/**
|
|
56511
56985
|
* Per-class audio metrics aggregated over a sliding window.
|
|
@@ -56732,10 +57206,7 @@ var BatteryStatusSchema = object({
|
|
|
56732
57206
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
56733
57207
|
* declines to band a non-finite reading, which is the correct shape.
|
|
56734
57208
|
*/
|
|
56735
|
-
percentage: number().min(0).max(100).nullable().meta({
|
|
56736
|
-
unit: "%",
|
|
56737
|
-
label: "Charge"
|
|
56738
|
-
}),
|
|
57209
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
56739
57210
|
/**
|
|
56740
57211
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
56741
57212
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -56762,7 +57233,14 @@ var BatteryStatusSchema = object({
|
|
|
56762
57233
|
*/
|
|
56763
57234
|
sleeping: boolean().meta({ label: "Sleeping" }),
|
|
56764
57235
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
56765
|
-
lastUpdated: number(),
|
|
57236
|
+
lastUpdated: number().meta(readingMeta("none")),
|
|
57237
|
+
/**
|
|
57238
|
+
* Battery voltage in volts, for a cell the firmware reports as voltage
|
|
57239
|
+
* rather than a percentage (Ecowitt capacitor packs). Absent on cameras
|
|
57240
|
+
* that only report charge. A second reading on this cap, not a second
|
|
57241
|
+
* `numeric-sensor` on the same device.
|
|
57242
|
+
*/
|
|
57243
|
+
volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
56766
57244
|
/**
|
|
56767
57245
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
56768
57246
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -56814,7 +57292,6 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
56814
57292
|
/** Generic Customize target for the percentage reading (D936). */
|
|
56815
57293
|
var batteryCustomize = {
|
|
56816
57294
|
version: 1,
|
|
56817
|
-
advanced: "derive",
|
|
56818
57295
|
targets: [{
|
|
56819
57296
|
id: "percentage",
|
|
56820
57297
|
cardinality: "scalar",
|
|
@@ -56823,7 +57300,7 @@ var batteryCustomize = {
|
|
|
56823
57300
|
stateField: "percentage"
|
|
56824
57301
|
},
|
|
56825
57302
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
56826
|
-
apply: "add
|
|
57303
|
+
apply: "add",
|
|
56827
57304
|
path: "percentage",
|
|
56828
57305
|
sources: [{
|
|
56829
57306
|
kind: "field",
|
|
@@ -56944,10 +57421,16 @@ onStatusChanged: { data: object({
|
|
|
56944
57421
|
/**
|
|
56945
57422
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
56946
57423
|
*/
|
|
56947
|
-
history: { fields: {
|
|
56948
|
-
|
|
56949
|
-
|
|
56950
|
-
|
|
57424
|
+
history: { fields: {
|
|
57425
|
+
percentage: {
|
|
57426
|
+
kind: "gauge",
|
|
57427
|
+
unit: "%"
|
|
57428
|
+
},
|
|
57429
|
+
volts: {
|
|
57430
|
+
kind: "gauge",
|
|
57431
|
+
unit: "V"
|
|
57432
|
+
}
|
|
57433
|
+
} },
|
|
56951
57434
|
customize: batteryCustomize,
|
|
56952
57435
|
/** Clock fields: written, but excluded from the compare that decides
|
|
56953
57436
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -56994,12 +57477,7 @@ var binaryCapability = {
|
|
|
56994
57477
|
history: { fields: { on: { kind: "boolean" } } },
|
|
56995
57478
|
/** Clock fields: written, but excluded from the compare that decides
|
|
56996
57479
|
* whether persisting is worth a SQLite commit. */
|
|
56997
|
-
volatileStateFields: ["lastChangedAt"]
|
|
56998
|
-
customize: {
|
|
56999
|
-
version: 1,
|
|
57000
|
-
advanced: "derive",
|
|
57001
|
-
targets: []
|
|
57002
|
-
}
|
|
57480
|
+
volatileStateFields: ["lastChangedAt"]
|
|
57003
57481
|
};
|
|
57004
57482
|
/**
|
|
57005
57483
|
* Dimmable-light brightness control. Co-exists with `switch` on the
|
|
@@ -57015,7 +57493,7 @@ var binaryCapability = {
|
|
|
57015
57493
|
*/
|
|
57016
57494
|
var BrightnessStatusSchema = object({
|
|
57017
57495
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
57018
|
-
percentage: number().min(0).max(100),
|
|
57496
|
+
percentage: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
57019
57497
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
57020
57498
|
lastChangedAt: number()
|
|
57021
57499
|
});
|
|
@@ -57469,21 +57947,21 @@ var ClimateControlStatusSchema = object({
|
|
|
57469
57947
|
/** Single setpoint in Celsius. Used by single-target modes
|
|
57470
57948
|
* (heat / cool / dry). Null when the device is in a range mode
|
|
57471
57949
|
* (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
|
|
57472
|
-
target: number().nullable(),
|
|
57950
|
+
target: number().nullable().meta(readingMeta("temperature")),
|
|
57473
57951
|
/** Upper bound of the dual setpoint in Celsius. Populated only
|
|
57474
57952
|
* in `heat_cool` mode. */
|
|
57475
|
-
targetHigh: number().nullable(),
|
|
57953
|
+
targetHigh: number().nullable().meta(readingMeta("temperature")),
|
|
57476
57954
|
/** Lower bound of the dual setpoint in Celsius. Populated only
|
|
57477
57955
|
* in `heat_cool` mode. */
|
|
57478
|
-
targetLow: number().nullable(),
|
|
57956
|
+
targetLow: number().nullable().meta(readingMeta("temperature")),
|
|
57479
57957
|
/** Target relative humidity (0..100). Null when the device has no
|
|
57480
57958
|
* humidity control. */
|
|
57481
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
57959
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
57482
57960
|
/** Current measured temperature in Celsius. Read-only — pushed by
|
|
57483
57961
|
* the device's internal sensor. */
|
|
57484
|
-
currentTemp: number().nullable(),
|
|
57962
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
57485
57963
|
/** Current measured relative humidity (0..100). Read-only. */
|
|
57486
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
57964
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
57487
57965
|
/** Vertical louver swing on/off. Null when the device has no vertical
|
|
57488
57966
|
* swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
|
|
57489
57967
|
swingVertical: boolean().nullable(),
|
|
@@ -57491,11 +57969,11 @@ var ClimateControlStatusSchema = object({
|
|
|
57491
57969
|
* horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
|
|
57492
57970
|
swingHorizontal: boolean().nullable(),
|
|
57493
57971
|
/** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
57494
|
-
minTemp: number().nullable().optional(),
|
|
57972
|
+
minTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
57495
57973
|
/** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
57496
|
-
maxTemp: number().nullable().optional(),
|
|
57974
|
+
maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
57497
57975
|
/** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
|
|
57498
|
-
tempStep: number().positive().nullable().optional(),
|
|
57976
|
+
tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
|
|
57499
57977
|
/** Ms epoch when the slice was last updated (push or command). */
|
|
57500
57978
|
lastFetchedAt: number()
|
|
57501
57979
|
});
|
|
@@ -57637,17 +58115,17 @@ var climateControlCapability = {
|
|
|
57637
58115
|
* modes to expose in the picker without a slice fetch.
|
|
57638
58116
|
*/
|
|
57639
58117
|
var RgbTripletSchema = object({
|
|
57640
|
-
r: number().int().min(0).max(255),
|
|
57641
|
-
g: number().int().min(0).max(255),
|
|
57642
|
-
b: number().int().min(0).max(255)
|
|
58118
|
+
r: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
58119
|
+
g: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
58120
|
+
b: number().int().min(0).max(255).meta(readingMeta("none"))
|
|
57643
58121
|
});
|
|
57644
58122
|
var HsvTripletSchema = object({
|
|
57645
58123
|
/** Hue in degrees, 0..360. */
|
|
57646
|
-
h: number().min(0).max(360),
|
|
58124
|
+
h: number().min(0).max(360).meta(readingMeta("none")),
|
|
57647
58125
|
/** Saturation as 0..100 inclusive. */
|
|
57648
|
-
s: number().min(0).max(100),
|
|
58126
|
+
s: number().min(0).max(100).meta(readingMeta("none")),
|
|
57649
58127
|
/** Value/brightness as 0..100 inclusive. */
|
|
57650
|
-
v: number().min(0).max(100)
|
|
58128
|
+
v: number().min(0).max(100).meta(readingMeta("none"))
|
|
57651
58129
|
});
|
|
57652
58130
|
var ColorInputSchema = discriminatedUnion("mode", [
|
|
57653
58131
|
object({
|
|
@@ -57676,7 +58154,7 @@ var ColorStatusSchema = object({
|
|
|
57676
58154
|
/** Populated when `mode === 'hsv'`. */
|
|
57677
58155
|
hsv: HsvTripletSchema.optional(),
|
|
57678
58156
|
/** Populated when `mode === 'mired'`. */
|
|
57679
|
-
mireds: number().int().optional(),
|
|
58157
|
+
mireds: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
57680
58158
|
/** Ms epoch of the last operator-driven change. */
|
|
57681
58159
|
lastChangedAt: number()
|
|
57682
58160
|
});
|
|
@@ -57871,18 +58349,12 @@ _enum([
|
|
|
57871
58349
|
"empty",
|
|
57872
58350
|
"full"
|
|
57873
58351
|
]);
|
|
57874
|
-
/**
|
|
57875
|
-
|
|
57876
|
-
|
|
57877
|
-
"days",
|
|
57878
|
-
"hours",
|
|
57879
|
-
"cycles"
|
|
57880
|
-
]);
|
|
57881
|
-
/** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
|
|
57882
|
-
* days). `value` null = unknown (D393). */
|
|
58352
|
+
/** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
|
|
58353
|
+
var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
|
|
58354
|
+
/** Remaining life. `value` null = unknown (D393). Duration is seconds. */
|
|
57883
58355
|
var ConsumableRemainingSchema = object({
|
|
57884
|
-
value: number().min(0).nullable(),
|
|
57885
|
-
|
|
58356
|
+
value: number().min(0).nullable().meta(readingMeta("device")),
|
|
58357
|
+
kind: ConsumableRemainingKindSchema
|
|
57886
58358
|
});
|
|
57887
58359
|
/** A single consumable item. Either a continuous `level` (remaining
|
|
57888
58360
|
* life %, or fill % for a `supply` / `waste` item) or a discrete `status`
|
|
@@ -57895,7 +58367,7 @@ var ConsumableItemSchema = object({
|
|
|
57895
58367
|
/** Display name. */
|
|
57896
58368
|
label: string().min(1),
|
|
57897
58369
|
/** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
|
|
57898
|
-
level: number().min(0).max(100).nullable(),
|
|
58370
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
57899
58371
|
/** The provider's discrete verdict when known (binary mode). */
|
|
57900
58372
|
status: ConsumableStatusSchema.nullable(),
|
|
57901
58373
|
/** What the item is — decides whether low or full is bad. Absent = `wear`. */
|
|
@@ -57977,8 +58449,8 @@ var consumablesCustomize = {
|
|
|
57977
58449
|
transforms: [{ kind: "identity" }]
|
|
57978
58450
|
}],
|
|
57979
58451
|
literals: [{
|
|
57980
|
-
path: "remaining.
|
|
57981
|
-
values: [...
|
|
58452
|
+
path: "remaining.kind",
|
|
58453
|
+
values: [...ConsumableRemainingKindSchema.options]
|
|
57982
58454
|
}]
|
|
57983
58455
|
},
|
|
57984
58456
|
{
|
|
@@ -58107,7 +58579,6 @@ reset: method(object({
|
|
|
58107
58579
|
},
|
|
58108
58580
|
"remaining.value": {
|
|
58109
58581
|
kind: "gauge",
|
|
58110
|
-
unitFrom: "remaining.unit",
|
|
58111
58582
|
deadband: 1,
|
|
58112
58583
|
displayName: "Remaining"
|
|
58113
58584
|
}
|
|
@@ -58177,10 +58648,9 @@ var contactCapability = {
|
|
|
58177
58648
|
* (empty array for the other kinds). It lives in the slice because
|
|
58178
58649
|
* HA `select.*` entities may update their options at runtime.
|
|
58179
58650
|
*
|
|
58180
|
-
*
|
|
58181
|
-
*
|
|
58182
|
-
*
|
|
58183
|
-
* push — consistent with `options` and the sensor-unit approach.
|
|
58651
|
+
* Numeric `min` / `max` / `step` live in the slice and share the device
|
|
58652
|
+
* quantity (`SourceInfo.quantity`, D944). Display precision is a read-time
|
|
58653
|
+
* preference. A datetime control's shape is `SourceInfo.dateTimeShape`.
|
|
58184
58654
|
*/
|
|
58185
58655
|
var ControlStatusSchema = object({
|
|
58186
58656
|
kind: _enum([
|
|
@@ -58192,39 +58662,17 @@ var ControlStatusSchema = object({
|
|
|
58192
58662
|
/** Current value. Numeric kind → number; select/text/datetime → string.
|
|
58193
58663
|
* Datetime values are ISO 8601 strings (full timestamp, date-only,
|
|
58194
58664
|
* or time-only — HA accepts all three forms). */
|
|
58195
|
-
value: union([number(), string()]),
|
|
58665
|
+
value: union([number().meta(readingMeta("device")), string()]),
|
|
58196
58666
|
/** Acceptable values when `kind === 'select'`. Empty for other kinds. */
|
|
58197
58667
|
options: array(string()),
|
|
58198
58668
|
/** Ms epoch when the slice was last updated. */
|
|
58199
58669
|
lastChangedAt: number(),
|
|
58200
|
-
/**
|
|
58201
|
-
|
|
58202
|
-
|
|
58203
|
-
|
|
58204
|
-
/**
|
|
58205
|
-
|
|
58206
|
-
min: number().optional(),
|
|
58207
|
-
/** Maximum allowed value. Populated live from HA `attributes.max`.
|
|
58208
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58209
|
-
max: number().optional(),
|
|
58210
|
-
/** Step increment. Populated live from HA `attributes.step`.
|
|
58211
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58212
|
-
step: number().optional(),
|
|
58213
|
-
/** Suggested decimal places for numeric display. Populated live from HA
|
|
58214
|
-
* `attributes.suggested_display_precision`, or derived from `step` when the
|
|
58215
|
-
* HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
|
|
58216
|
-
* other kinds. Falls back to auto-formatting when absent. */
|
|
58217
|
-
precision: number().int().min(0).max(10).optional(),
|
|
58218
|
-
/** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
|
|
58219
|
-
* 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
|
|
58220
|
-
* (UI defaults to 'datetime'). Derived live from the device's HA domain
|
|
58221
|
-
* (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
|
|
58222
|
-
* `has_date`/`has_time` attributes on each push. */
|
|
58223
|
-
format: _enum([
|
|
58224
|
-
"date",
|
|
58225
|
-
"time",
|
|
58226
|
-
"datetime"
|
|
58227
|
-
]).optional()
|
|
58670
|
+
/** Minimum allowed value, in the device quantity's base unit. */
|
|
58671
|
+
min: number().optional().meta(readingMeta("device")),
|
|
58672
|
+
/** Maximum allowed value, in the device quantity's base unit. */
|
|
58673
|
+
max: number().optional().meta(readingMeta("device")),
|
|
58674
|
+
/** Step increment, in the device quantity's base unit. */
|
|
58675
|
+
step: number().optional().meta(readingMeta("device"))
|
|
58228
58676
|
});
|
|
58229
58677
|
var ControlSetValueInputSchema = discriminatedUnion("kind", [
|
|
58230
58678
|
object({
|
|
@@ -58290,9 +58738,9 @@ var CoverStatusSchema = object({
|
|
|
58290
58738
|
]),
|
|
58291
58739
|
/** 0 = fully closed, 100 = fully open. Null when the device has
|
|
58292
58740
|
* no intermediate position surface. */
|
|
58293
|
-
position: number().min(0).max(100).nullable(),
|
|
58741
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58294
58742
|
/** 0 = closed slats, 100 = open slats. Null when no tilt support. */
|
|
58295
|
-
tiltPosition: number().min(0).max(100).nullable(),
|
|
58743
|
+
tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58296
58744
|
/** Ms epoch when the slice was last updated. */
|
|
58297
58745
|
lastChangedAt: number()
|
|
58298
58746
|
});
|
|
@@ -58395,9 +58843,9 @@ var NormalizedRangeSchema$1 = object({
|
|
|
58395
58843
|
var DayNightStatusSchema = object({
|
|
58396
58844
|
mode: DayNightModeSchema,
|
|
58397
58845
|
/** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
|
|
58398
|
-
sensitivity: number().optional(),
|
|
58846
|
+
sensitivity: number().optional().meta(readingMeta("none")),
|
|
58399
58847
|
/** Delay before the IR-cut filter flips, in seconds. */
|
|
58400
|
-
switchDelaySec: number().optional(),
|
|
58848
|
+
switchDelaySec: number().optional().meta(readingMeta("duration")),
|
|
58401
58849
|
lastFetchedAt: number()
|
|
58402
58850
|
});
|
|
58403
58851
|
/**
|
|
@@ -58615,11 +59063,11 @@ var DishwasherStatusSchema = object({
|
|
|
58615
59063
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
58616
59064
|
availablePrograms: array(string()),
|
|
58617
59065
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
58618
|
-
remainingSeconds: number().int().nonnegative().nullable(),
|
|
59066
|
+
remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
58619
59067
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
58620
|
-
delayedStartSeconds: number().int().nonnegative().nullable(),
|
|
59068
|
+
delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
58621
59069
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
58622
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
59070
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58623
59071
|
/** True when the door is reported open; null when the door state is not known. */
|
|
58624
59072
|
doorOpen: boolean().nullable(),
|
|
58625
59073
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -58643,7 +59091,7 @@ var DishwasherStatusSchema = object({
|
|
|
58643
59091
|
/** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
|
|
58644
59092
|
machineCareDue: boolean().nullable(),
|
|
58645
59093
|
/** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
|
|
58646
|
-
machineCareRemainingRuns: number().int().nonnegative().nullable(),
|
|
59094
|
+
machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
|
|
58647
59095
|
/** Option states: null when the appliance does not expose the option or has not said. */
|
|
58648
59096
|
halfLoad: boolean().nullable(),
|
|
58649
59097
|
hygienePlus: boolean().nullable(),
|
|
@@ -58658,7 +59106,7 @@ var DishwasherStatusSchema = object({
|
|
|
58658
59106
|
/** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
|
|
58659
59107
|
activeEvents: array(string()),
|
|
58660
59108
|
/** Numeric error code when the appliance reports one; null otherwise. */
|
|
58661
|
-
errorCode: number().nullable(),
|
|
59109
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
58662
59110
|
/** Name of the raised alert-level event while the appliance is in error; null otherwise. */
|
|
58663
59111
|
errorLabel: string().nullable(),
|
|
58664
59112
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
@@ -58889,7 +59337,7 @@ var DoorbellStatusSchema = object({
|
|
|
58889
59337
|
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
58890
59338
|
lastPressedAt: number().nullable(),
|
|
58891
59339
|
/** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
|
|
58892
|
-
pressCountSinceStart: number()
|
|
59340
|
+
pressCountSinceStart: number().meta(readingMeta("count"))
|
|
58893
59341
|
});
|
|
58894
59342
|
var DoorbellPressEventSchema = object({
|
|
58895
59343
|
deviceId: number(),
|
|
@@ -58954,17 +59402,7 @@ onPressed: {
|
|
|
58954
59402
|
history: "none",
|
|
58955
59403
|
customize: doorbellCustomize
|
|
58956
59404
|
};
|
|
58957
|
-
|
|
58958
|
-
* Enum-state sensor — a string value picked from a finite option set.
|
|
58959
|
-
* Drives HA `sensor` entries with `state_class: enum` (HVAC action
|
|
58960
|
-
* states, weather conditions, contact-source channels, mode strings).
|
|
58961
|
-
* The option set is stable across observations; it is persisted in the
|
|
58962
|
-
* device config blob (via the control slice at adoption) and is NOT
|
|
58963
|
-
* stored in `sourceInfo`.
|
|
58964
|
-
*/
|
|
58965
|
-
/** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
|
|
58966
|
-
* enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
|
|
58967
|
-
var EnumSensorDateTimeFormatSchema = _enum([
|
|
59405
|
+
_enum([
|
|
58968
59406
|
"date",
|
|
58969
59407
|
"time",
|
|
58970
59408
|
"datetime"
|
|
@@ -58972,12 +59410,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
|
|
|
58972
59410
|
var EnumSensorStatusSchema = object({
|
|
58973
59411
|
value: string(),
|
|
58974
59412
|
/**
|
|
58975
|
-
* Set
|
|
58976
|
-
* as
|
|
58977
|
-
*
|
|
58978
|
-
* a time-only sensor → `'time'`.
|
|
59413
|
+
* Set when `value` is a timestamp rather than an enum state, so history
|
|
59414
|
+
* does not learn ISO strings as states. The display shape (date / time /
|
|
59415
|
+
* datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
|
|
58979
59416
|
*/
|
|
58980
|
-
|
|
59417
|
+
timestamp: boolean().optional(),
|
|
58981
59418
|
/** Ms epoch when the slice was last updated. */
|
|
58982
59419
|
lastFetchedAt: number()
|
|
58983
59420
|
});
|
|
@@ -59004,11 +59441,11 @@ var enumSensorCapability = {
|
|
|
59004
59441
|
* History (D850): the value as an `enum` series (an appliance's program or
|
|
59005
59442
|
* operation state, an HVAC action). The option set is per device, so the
|
|
59006
59443
|
* value list is LEARNED as values are first seen (bounded). A sensor with a
|
|
59007
|
-
* `
|
|
59444
|
+
* `timestamp` carries a clock, not a state, and is not recorded.
|
|
59008
59445
|
*/
|
|
59009
59446
|
history: { fields: { value: {
|
|
59010
59447
|
kind: "enum",
|
|
59011
|
-
skipWhenSet: "
|
|
59448
|
+
skipWhenSet: "timestamp"
|
|
59012
59449
|
} } },
|
|
59013
59450
|
/** Clock fields: written, but excluded from the compare that decides
|
|
59014
59451
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -59028,13 +59465,13 @@ var EventFireSchema = object({
|
|
|
59028
59465
|
deviceId: number(),
|
|
59029
59466
|
eventType: string(),
|
|
59030
59467
|
data: record(string(), unknown()).nullable(),
|
|
59031
|
-
timestamp: number(),
|
|
59032
|
-
seq: number()
|
|
59468
|
+
timestamp: number().meta(readingMeta("none")),
|
|
59469
|
+
seq: number().meta(readingMeta("count"))
|
|
59033
59470
|
});
|
|
59034
59471
|
var EventEmitterStatusSchema = object({
|
|
59035
59472
|
eventTypes: array(string()),
|
|
59036
59473
|
lastEvent: EventFireSchema.nullable(),
|
|
59037
|
-
eventCountSinceStart: number()
|
|
59474
|
+
eventCountSinceStart: number().meta(readingMeta("count"))
|
|
59038
59475
|
});
|
|
59039
59476
|
var eventEmitterCapability = {
|
|
59040
59477
|
name: "event-emitter",
|
|
@@ -59447,10 +59884,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
59447
59884
|
var FanControlStatusSchema = object({
|
|
59448
59885
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
59449
59886
|
* speed surface (single-speed fan). */
|
|
59450
|
-
percentage: number().min(0).max(100).nullable(),
|
|
59887
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59451
59888
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
59452
59889
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
59453
|
-
percentageStep: number().positive().optional(),
|
|
59890
|
+
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
59454
59891
|
/** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
|
|
59455
59892
|
* — empty string when no preset is active or supported. */
|
|
59456
59893
|
preset: string(),
|
|
@@ -59571,7 +60008,7 @@ var FeatureProbeStatusSchema = object({
|
|
|
59571
60008
|
/** Camera/firmware model string. `null` when the firmware doesn't expose it. */
|
|
59572
60009
|
model: string().nullable(),
|
|
59573
60010
|
/** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
|
|
59574
|
-
channelCount: number().nullable(),
|
|
60011
|
+
channelCount: number().nullable().meta(readingMeta("count")),
|
|
59575
60012
|
/**
|
|
59576
60013
|
* Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
|
|
59577
60014
|
* completes — drivers' `getAccessoryChildren()` should treat zero as
|
|
@@ -59694,9 +60131,91 @@ var gasCapability = {
|
|
|
59694
60131
|
* whether persisting is worth a SQLite commit. */
|
|
59695
60132
|
volatileStateFields: ["lastChangedAt"]
|
|
59696
60133
|
};
|
|
60134
|
+
var GroupCommandsSchema = object({
|
|
60135
|
+
/** One row per member that has been commanded. A later command replaces that member's row. */
|
|
60136
|
+
results: array(object({
|
|
60137
|
+
deviceId: number().int().nonnegative(),
|
|
60138
|
+
cap: string().min(1),
|
|
60139
|
+
method: string().min(1),
|
|
60140
|
+
ok: boolean(),
|
|
60141
|
+
error: string().optional(),
|
|
60142
|
+
/** Ms epoch of the attempt. */
|
|
60143
|
+
at: number()
|
|
60144
|
+
})) });
|
|
60145
|
+
var groupCommandsCapability = {
|
|
60146
|
+
name: "group-commands",
|
|
60147
|
+
scope: "device",
|
|
60148
|
+
deviceNative: true,
|
|
60149
|
+
mode: "singleton",
|
|
60150
|
+
deviceTypes: [DeviceType.Switch, DeviceType.Light],
|
|
60151
|
+
methods: {},
|
|
60152
|
+
status: {
|
|
60153
|
+
schema: GroupCommandsSchema,
|
|
60154
|
+
kind: "push"
|
|
60155
|
+
},
|
|
60156
|
+
runtimeState: GroupCommandsSchema,
|
|
60157
|
+
/**
|
|
60158
|
+
* Runtime-state durability: **session** — the last attempt. A restored
|
|
60159
|
+
* failure would outlive the command that caused it.
|
|
60160
|
+
*
|
|
60161
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
60162
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
60163
|
+
*/
|
|
60164
|
+
durability: "session",
|
|
60165
|
+
history: "none"
|
|
60166
|
+
};
|
|
60167
|
+
/**
|
|
60168
|
+
* How many members of a helper group answered (D944). Counts are integers,
|
|
60169
|
+
* not measurements: zero included is a real count, never a stand-in reading.
|
|
60170
|
+
* The reading itself stays on the output capability. History is none — the
|
|
60171
|
+
* series that later statistics read is the output cap's own history block.
|
|
60172
|
+
*/
|
|
60173
|
+
var GroupStatusSchema = object({
|
|
60174
|
+
/** Members whose current value entered the aggregate. */
|
|
60175
|
+
included: number().int().nonnegative().meta(readingMeta("count", {
|
|
60176
|
+
precision: 0,
|
|
60177
|
+
label: "Reporting"
|
|
60178
|
+
})),
|
|
60179
|
+
/** Members left out: offline, unknown, or not a finite reading (D393). */
|
|
60180
|
+
excluded: number().int().nonnegative().meta(readingMeta("count", {
|
|
60181
|
+
precision: 0,
|
|
60182
|
+
label: "Excluded"
|
|
60183
|
+
})),
|
|
60184
|
+
/** `included` + `excluded`. */
|
|
60185
|
+
total: number().int().nonnegative().meta(readingMeta("count", {
|
|
60186
|
+
precision: 0,
|
|
60187
|
+
label: "Members"
|
|
60188
|
+
}))
|
|
60189
|
+
});
|
|
60190
|
+
var groupStatusCapability = {
|
|
60191
|
+
name: "group-status",
|
|
60192
|
+
scope: "device",
|
|
60193
|
+
deviceNative: true,
|
|
60194
|
+
mode: "singleton",
|
|
60195
|
+
deviceTypes: [
|
|
60196
|
+
DeviceType.Sensor,
|
|
60197
|
+
DeviceType.Switch,
|
|
60198
|
+
DeviceType.Light
|
|
60199
|
+
],
|
|
60200
|
+
methods: {},
|
|
60201
|
+
status: {
|
|
60202
|
+
schema: GroupStatusSchema,
|
|
60203
|
+
kind: "push"
|
|
60204
|
+
},
|
|
60205
|
+
runtimeState: GroupStatusSchema,
|
|
60206
|
+
/**
|
|
60207
|
+
* Runtime-state durability: **session** — recomputed from members on the
|
|
60208
|
+
* next source event; a restored count would outlive the readings it named.
|
|
60209
|
+
*
|
|
60210
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
60211
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
60212
|
+
*/
|
|
60213
|
+
durability: "session",
|
|
60214
|
+
history: "none"
|
|
60215
|
+
};
|
|
59697
60216
|
var HeatingValveStatusSchema = object({
|
|
59698
60217
|
/** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
|
|
59699
|
-
position: number().min(0).max(100).nullable(),
|
|
60218
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59700
60219
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
59701
60220
|
lastFetchedAt: number()
|
|
59702
60221
|
});
|
|
@@ -59739,9 +60258,9 @@ var HumidifierStatusSchema = object({
|
|
|
59739
60258
|
/** Whether the humidifier is currently on. */
|
|
59740
60259
|
on: boolean(),
|
|
59741
60260
|
/** Current measured relative humidity (0..100). Null when not reported. */
|
|
59742
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
60261
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
59743
60262
|
/** Target relative humidity (0..100). Null when no setpoint surface. */
|
|
59744
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
60263
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
59745
60264
|
/** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
|
|
59746
60265
|
* device has no mode surface. */
|
|
59747
60266
|
mode: string().nullable(),
|
|
@@ -59751,9 +60270,9 @@ var HumidifierStatusSchema = object({
|
|
|
59751
60270
|
* `idle` / `off`). Null when the device doesn't report it. */
|
|
59752
60271
|
action: string().nullable(),
|
|
59753
60272
|
/** HA `min_humidity` attribute. Null → UI uses 0. */
|
|
59754
|
-
minHumidity: number().nullable(),
|
|
60273
|
+
minHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
59755
60274
|
/** HA `max_humidity` attribute. Null → UI uses 100. */
|
|
59756
|
-
maxHumidity: number().nullable(),
|
|
60275
|
+
maxHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
59757
60276
|
/** Ms epoch when the slice was last updated. */
|
|
59758
60277
|
lastChangedAt: number()
|
|
59759
60278
|
});
|
|
@@ -59823,22 +60342,13 @@ var humidifierCapability = {
|
|
|
59823
60342
|
* `sourceInfo`.
|
|
59824
60343
|
*/
|
|
59825
60344
|
var HumiditySensorStatusSchema = object({
|
|
59826
|
-
/** Current relative humidity, 0..100. */
|
|
59827
|
-
percent: number().min(0).max(100).meta({
|
|
59828
|
-
|
|
60345
|
+
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
60346
|
+
percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
60347
|
+
precision: 0,
|
|
59829
60348
|
label: "Humidity"
|
|
59830
|
-
}),
|
|
60349
|
+
})),
|
|
59831
60350
|
/** Ms epoch when the slice was last updated. */
|
|
59832
|
-
lastFetchedAt: number()
|
|
59833
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
59834
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
59835
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
59836
|
-
unit: string().optional(),
|
|
59837
|
-
/** Suggested decimal places for numeric display.
|
|
59838
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
59839
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
59840
|
-
* auto-formatting when absent. */
|
|
59841
|
-
precision: number().int().min(0).max(10).optional()
|
|
60351
|
+
lastFetchedAt: number()
|
|
59842
60352
|
});
|
|
59843
60353
|
var humiditySensorCapability = {
|
|
59844
60354
|
name: "humidity-sensor",
|
|
@@ -59869,12 +60379,7 @@ var humiditySensorCapability = {
|
|
|
59869
60379
|
} } },
|
|
59870
60380
|
/** Clock fields: written, but excluded from the compare that decides
|
|
59871
60381
|
* whether persisting is worth a SQLite commit. */
|
|
59872
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
59873
|
-
customize: {
|
|
59874
|
-
version: 1,
|
|
59875
|
-
advanced: "derive",
|
|
59876
|
-
targets: []
|
|
59877
|
-
}
|
|
60382
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
59878
60383
|
};
|
|
59879
60384
|
/**
|
|
59880
60385
|
* Image display cap. Models a single still image exposed by an integration —
|
|
@@ -59895,7 +60400,7 @@ var ImageStatusSchema = object({
|
|
|
59895
60400
|
* entity exposes no `entity_picture` (yet). */
|
|
59896
60401
|
url: string().nullable(),
|
|
59897
60402
|
/** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
|
|
59898
|
-
lastUpdated: number().nullable()
|
|
60403
|
+
lastUpdated: number().nullable().meta(readingMeta("none"))
|
|
59899
60404
|
});
|
|
59900
60405
|
var imageCapability = {
|
|
59901
60406
|
name: "image",
|
|
@@ -59983,19 +60488,19 @@ var NormalizedRangeSchema = object({
|
|
|
59983
60488
|
*/
|
|
59984
60489
|
var ImageSettingsStatusSchema = object({
|
|
59985
60490
|
/** Normalized 0–100. */
|
|
59986
|
-
brightness: number().optional(),
|
|
60491
|
+
brightness: number().optional().meta(readingMeta("none")),
|
|
59987
60492
|
/** Normalized 0–100. */
|
|
59988
|
-
contrast: number().optional(),
|
|
60493
|
+
contrast: number().optional().meta(readingMeta("none")),
|
|
59989
60494
|
/** Normalized 0–100. */
|
|
59990
|
-
saturation: number().optional(),
|
|
60495
|
+
saturation: number().optional().meta(readingMeta("none")),
|
|
59991
60496
|
/** Normalized 0–100. */
|
|
59992
|
-
sharpness: number().optional(),
|
|
60497
|
+
sharpness: number().optional().meta(readingMeta("none")),
|
|
59993
60498
|
mirror: boolean().optional(),
|
|
59994
60499
|
flip: boolean().optional(),
|
|
59995
60500
|
rotate: ImageRotateSchema.optional(),
|
|
59996
60501
|
whiteBalance: WhiteBalanceModeSchema.optional(),
|
|
59997
60502
|
/** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
|
|
59998
|
-
warmth: number().optional(),
|
|
60503
|
+
warmth: number().optional().meta(readingMeta("none")),
|
|
59999
60504
|
exposureMode: ExposureModeSchema.optional(),
|
|
60000
60505
|
backlightMode: BacklightModeSchema.optional(),
|
|
60001
60506
|
lastFetchedAt: number()
|
|
@@ -60274,10 +60779,10 @@ var IntercomAbilitySchema = object({
|
|
|
60274
60779
|
/** Codecs accepted by firmware, in preference order. Driver-authored strings. */
|
|
60275
60780
|
codecs: array(string()),
|
|
60276
60781
|
/** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
|
|
60277
|
-
sampleRate: number(),
|
|
60782
|
+
sampleRate: number().meta(readingMeta("none")),
|
|
60278
60783
|
duplex: _enum(["half", "full"]),
|
|
60279
60784
|
/** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
|
|
60280
|
-
maxBacklogMs: number()
|
|
60785
|
+
maxBacklogMs: number().meta(readingMeta("duration-ms"))
|
|
60281
60786
|
});
|
|
60282
60787
|
var IntercomStatusSchema = object({
|
|
60283
60788
|
/** True while a talk session is open. */
|
|
@@ -60504,13 +61009,13 @@ var LawnMowerControlStatusSchema = object({
|
|
|
60504
61009
|
activity: LawnMowerActivitySchema,
|
|
60505
61010
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
60506
61011
|
* reading. */
|
|
60507
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
61012
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60508
61013
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
60509
61014
|
* task is active / progress is unavailable. */
|
|
60510
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
61015
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60511
61016
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
60512
61017
|
* or null when unknown. */
|
|
60513
|
-
currentCode: number().nullable(),
|
|
61018
|
+
currentCode: number().nullable().meta(readingMeta("none")),
|
|
60514
61019
|
/** Human label for {@link currentCode}, or null when undecodable. */
|
|
60515
61020
|
currentCodeLabel: string().nullable(),
|
|
60516
61021
|
/** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
|
|
@@ -61043,7 +61548,7 @@ var MediaPlayerStatusSchema = object({
|
|
|
61043
61548
|
state: MediaPlayerStateSchema,
|
|
61044
61549
|
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
61045
61550
|
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
61046
|
-
volumeLevel: number().min(0).max(100).nullable(),
|
|
61551
|
+
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61047
61552
|
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
61048
61553
|
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
61049
61554
|
isMuted: boolean().nullable(),
|
|
@@ -61056,10 +61561,10 @@ var MediaPlayerStatusSchema = object({
|
|
|
61056
61561
|
currentMedia: MediaInfoSchema.nullable(),
|
|
61057
61562
|
/** Current playback position in ms. Null when not seekable or
|
|
61058
61563
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
61059
|
-
positionMs: number().int().nonnegative().nullable(),
|
|
61564
|
+
positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61060
61565
|
/** Total duration of the current media in ms. Null when unknown
|
|
61061
61566
|
* (live stream) or nothing is playing. */
|
|
61062
|
-
durationMs: number().int().nonnegative().nullable(),
|
|
61567
|
+
durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61063
61568
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
61064
61569
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
61065
61570
|
shuffle: boolean().nullable(),
|
|
@@ -61368,7 +61873,7 @@ var motionCustomize = {
|
|
|
61368
61873
|
eventName: "onMotionChanged"
|
|
61369
61874
|
},
|
|
61370
61875
|
deviceTypes: [DeviceType.Camera],
|
|
61371
|
-
apply: "
|
|
61876
|
+
apply: "add",
|
|
61372
61877
|
applyAuthority: {
|
|
61373
61878
|
kind: "camera-sensors",
|
|
61374
61879
|
timeoutField: "motionCooldownMs"
|
|
@@ -61440,7 +61945,7 @@ var MotionStatusSchema = object({
|
|
|
61440
61945
|
* it, every provider seeded it at boot, and every session left it
|
|
61441
61946
|
* `detected: true` after its detach.
|
|
61442
61947
|
*/
|
|
61443
|
-
autoClearAfterMs: number().nullable()
|
|
61948
|
+
autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
|
|
61444
61949
|
});
|
|
61445
61950
|
/**
|
|
61446
61951
|
* Payload of `motion.onMotionChanged` event + the corresponding bus
|
|
@@ -61477,12 +61982,18 @@ var motionCapability = {
|
|
|
61477
61982
|
methods: {
|
|
61478
61983
|
/**
|
|
61479
61984
|
* Pull the current motion state synchronously. Convenience shortcut
|
|
61480
|
-
* for consumers that don't need the full status object
|
|
61481
|
-
*
|
|
61482
|
-
* `getStatus` once the auto-injected status surface lands in every
|
|
61483
|
-
* consumer.
|
|
61985
|
+
* for consumers that don't need the full status object. A composed
|
|
61986
|
+
* group answers from its slice (D944): unknown throws, it is not off.
|
|
61484
61987
|
*/
|
|
61485
61988
|
isDetected: method(object({ deviceId: number() }), boolean()) },
|
|
61989
|
+
/**
|
|
61990
|
+
* A composed motion group serves `isDetected` from `detected` (D944).
|
|
61991
|
+
* Without this the cap stays uncomposable (`has-commands`).
|
|
61992
|
+
*/
|
|
61993
|
+
composedMethods: { isDetected: {
|
|
61994
|
+
kind: "slice",
|
|
61995
|
+
field: "detected"
|
|
61996
|
+
} },
|
|
61486
61997
|
events: {
|
|
61487
61998
|
/**
|
|
61488
61999
|
* Fires every time the runner transitions a camera between
|
|
@@ -61622,7 +62133,7 @@ var MotionZoneRegionSchema = object({
|
|
|
61622
62133
|
* the grid region(s). */
|
|
61623
62134
|
var MotionZoneStatusSchema = object({
|
|
61624
62135
|
enabled: boolean(),
|
|
61625
|
-
sensitivity: number(),
|
|
62136
|
+
sensitivity: number().meta(readingMeta("none")),
|
|
61626
62137
|
/** Grid region(s). Today exactly one `grid` shape. */
|
|
61627
62138
|
regions: array(MotionZoneRegionSchema),
|
|
61628
62139
|
lastFetchedAt: number()
|
|
@@ -61992,7 +62503,7 @@ var NavigationStatusSchema = object({
|
|
|
61992
62503
|
following: boolean(),
|
|
61993
62504
|
flash: boolean(),
|
|
61994
62505
|
lightMode: NavigationLightModeSchema,
|
|
61995
|
-
lightLevel: number().min(40).max(100),
|
|
62506
|
+
lightLevel: number().min(40).max(100).meta(readingMeta("none")),
|
|
61996
62507
|
/** Ms epoch when the slice was last updated. */
|
|
61997
62508
|
lastChangedAt: number()
|
|
61998
62509
|
});
|
|
@@ -62126,13 +62637,13 @@ var NetworkLinkStatusSchema = object({
|
|
|
62126
62637
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
62127
62638
|
* SKIP a null rather than coerce it.
|
|
62128
62639
|
*/
|
|
62129
|
-
signalPercent: number().min(0).max(100).nullable(),
|
|
62640
|
+
signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62130
62641
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
62131
|
-
rssiDbm: number().optional(),
|
|
62642
|
+
rssiDbm: number().optional().meta(readingMeta("signal-level")),
|
|
62132
62643
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
62133
62644
|
ssid: string().optional(),
|
|
62134
62645
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
62135
|
-
lastUpdated: number()
|
|
62646
|
+
lastUpdated: number().meta(readingMeta("none"))
|
|
62136
62647
|
});
|
|
62137
62648
|
var networkLinkCapability = {
|
|
62138
62649
|
name: "network-link",
|
|
@@ -62517,7 +63028,7 @@ var NotifierStatusSchema = object({
|
|
|
62517
63028
|
lastError: string().nullable(),
|
|
62518
63029
|
/** Number of deliveries currently buffered server-side (rate-limited
|
|
62519
63030
|
* notifiers may queue). 0 when the provider sends synchronously. */
|
|
62520
|
-
queueDepth: number().int().nonnegative(),
|
|
63031
|
+
queueDepth: number().int().nonnegative().meta(readingMeta("count")),
|
|
62521
63032
|
/** Per-feature capability matrix — authoritative source for the
|
|
62522
63033
|
* compose-form field gating. */
|
|
62523
63034
|
supports: NotifierSupportsSchema
|
|
@@ -62609,11 +63120,9 @@ onSent: { data: object({
|
|
|
62609
63120
|
* cap fits (HA `sensor` whose `device_class` we don't have a typed cap
|
|
62610
63121
|
* for: water flow, distance, weight, frequency, signal strength, …).
|
|
62611
63122
|
*
|
|
62612
|
-
* The
|
|
62613
|
-
*
|
|
62614
|
-
*
|
|
62615
|
-
* The UI reads them directly from the slice — they are the single source
|
|
62616
|
-
* of truth for generic numeric rendering and do NOT live on `sourceInfo`.
|
|
63123
|
+
* The quantity is not on this slice. It is chosen when the device is
|
|
63124
|
+
* adopted and stored once on `SourceInfo.quantity` (D944). `value` is in
|
|
63125
|
+
* that quantity's base unit. `null` is a refused sample (D393).
|
|
62617
63126
|
*
|
|
62618
63127
|
* Prefer the typed alternatives (`temperature-sensor`, `humidity-sensor`,
|
|
62619
63128
|
* `ambient-light-sensor`, `pressure-sensor`, `power-meter`,
|
|
@@ -62622,15 +63131,8 @@ onSent: { data: object({
|
|
|
62622
63131
|
* numeric values land here.
|
|
62623
63132
|
*/
|
|
62624
63133
|
var NumericSensorStatusSchema = object({
|
|
62625
|
-
|
|
62626
|
-
|
|
62627
|
-
* Populated live from the upstream source on each state push.
|
|
62628
|
-
* Absent for unitless sensors. */
|
|
62629
|
-
unit: string().optional(),
|
|
62630
|
-
/** Suggested decimal places for numeric display.
|
|
62631
|
-
* Populated live from the upstream source when provided.
|
|
62632
|
-
* Falls back to auto-formatting when absent. */
|
|
62633
|
-
precision: number().int().min(0).max(10).optional(),
|
|
63134
|
+
/** Reading in the device quantity's base unit. `null` when refused (D393). */
|
|
63135
|
+
value: number().nullable().meta(readingMeta("device")),
|
|
62634
63136
|
/** Ms epoch when the slice was last updated. */
|
|
62635
63137
|
lastFetchedAt: number()
|
|
62636
63138
|
});
|
|
@@ -62654,11 +63156,11 @@ var numericSensorCapability = {
|
|
|
62654
63156
|
*/
|
|
62655
63157
|
durability: "restored",
|
|
62656
63158
|
/**
|
|
62657
|
-
* History (D850): recorded series. The unit is
|
|
63159
|
+
* History (D850): recorded series. The unit is the device quantity's base,
|
|
63160
|
+
* resolved when the series is read — the cap has no single unit.
|
|
62658
63161
|
*/
|
|
62659
63162
|
history: { fields: { value: {
|
|
62660
63163
|
kind: "gauge",
|
|
62661
|
-
unitFrom: "unit",
|
|
62662
63164
|
minIntervalMs: 3e4
|
|
62663
63165
|
} } },
|
|
62664
63166
|
/** Clock fields: written, but excluded from the compare that decides
|
|
@@ -62950,23 +63452,26 @@ var PetFeederStatusSchema = object({
|
|
|
62950
63452
|
/** Food currently in the bowl (grams). Null when the device has not
|
|
62951
63453
|
* reported a reading yet. On dual-hopper models this is the combined
|
|
62952
63454
|
* bowl reading; per-hopper levels live in `food1`/`food2`. */
|
|
62953
|
-
foodLevel: number().nullable(),
|
|
63455
|
+
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
62954
63456
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
62955
63457
|
* single-hopper models. */
|
|
62956
|
-
food1: number().nullable(),
|
|
63458
|
+
food1: number().nullable().meta(readingMeta("none")),
|
|
62957
63459
|
/** Hopper-2 food level (grams) on dual-hopper feeders; null on
|
|
62958
63460
|
* single-hopper models. */
|
|
62959
|
-
food2: number().nullable(),
|
|
63461
|
+
food2: number().nullable().meta(readingMeta("none")),
|
|
62960
63462
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
62961
63463
|
* (`device_class: problem`, on = low). True when the bowl is empty /
|
|
62962
63464
|
* below the feeder's low threshold. */
|
|
62963
63465
|
lowFood: boolean(),
|
|
62964
63466
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
62965
63467
|
* device has no battery reading. */
|
|
62966
|
-
batteryPower: number().min(0).max(100).nullable(),
|
|
62967
|
-
/**
|
|
62968
|
-
*
|
|
62969
|
-
|
|
63468
|
+
batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63469
|
+
/**
|
|
63470
|
+
* Desiccant life remaining, stored in seconds (duration base, D944).
|
|
63471
|
+
* Providers convert from the days the feeder reports. Null when the
|
|
63472
|
+
* model has no desiccant sensor.
|
|
63473
|
+
*/
|
|
63474
|
+
desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
|
|
62970
63475
|
/** True while a feed is in progress. */
|
|
62971
63476
|
feeding: boolean(),
|
|
62972
63477
|
/** Decoded connectivity / power status (HA petkit device-status enum).
|
|
@@ -62977,7 +63482,7 @@ var PetFeederStatusSchema = object({
|
|
|
62977
63482
|
* with `errorCode` for consumers that want the raw integer. */
|
|
62978
63483
|
error: string().nullable(),
|
|
62979
63484
|
/** Raw device error code (0 / null = no error). */
|
|
62980
|
-
errorCode: number().nullable(),
|
|
63485
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
62981
63486
|
/** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
|
|
62982
63487
|
isDualHopper: boolean(),
|
|
62983
63488
|
/** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
|
|
@@ -62987,7 +63492,7 @@ var PetFeederStatusSchema = object({
|
|
|
62987
63492
|
/** Play a chime when dispensing. */
|
|
62988
63493
|
feedSound: boolean(),
|
|
62989
63494
|
/** Speaker / prompt volume level (device-scaled integer). */
|
|
62990
|
-
volume: number(),
|
|
63495
|
+
volume: number().meta(readingMeta("none")),
|
|
62991
63496
|
/** Ms epoch when the slice was last refreshed from the cloud. */
|
|
62992
63497
|
lastFetchedAt: number()
|
|
62993
63498
|
});
|
|
@@ -63106,7 +63611,7 @@ var petFeederCapability = {
|
|
|
63106
63611
|
},
|
|
63107
63612
|
desiccantLeftDays: {
|
|
63108
63613
|
kind: "gauge",
|
|
63109
|
-
unit: "
|
|
63614
|
+
unit: "s"
|
|
63110
63615
|
}
|
|
63111
63616
|
} }
|
|
63112
63617
|
};
|
|
@@ -63507,36 +64012,19 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
63507
64012
|
*/
|
|
63508
64013
|
var PowerMeterStatusSchema = object({
|
|
63509
64014
|
/** Instantaneous power draw in watts. */
|
|
63510
|
-
watts: number().optional().meta({
|
|
63511
|
-
unit: "W",
|
|
63512
|
-
label: "Power"
|
|
63513
|
-
}),
|
|
64015
|
+
watts: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
63514
64016
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
63515
|
-
kwhTotal: number().optional().meta({
|
|
63516
|
-
unit: "kWh",
|
|
63517
|
-
label: "Energy"
|
|
63518
|
-
}),
|
|
64017
|
+
kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
63519
64018
|
/** Voltage in volts. */
|
|
63520
|
-
volts: number().optional(),
|
|
64019
|
+
volts: number().optional().meta(readingMeta("voltage")),
|
|
63521
64020
|
/** Current in amperes. */
|
|
63522
|
-
amps: number().optional(),
|
|
63523
|
-
/** Apparent power in volt-amperes
|
|
63524
|
-
va: number().optional(),
|
|
64021
|
+
amps: number().optional().meta(readingMeta("current")),
|
|
64022
|
+
/** Apparent power in volt-amperes. */
|
|
64023
|
+
va: number().optional().meta(readingMeta("apparent-power")),
|
|
63525
64024
|
/** Power factor (0..1) if reported. */
|
|
63526
|
-
powerFactor: number().min(0).max(1).optional(),
|
|
64025
|
+
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
63527
64026
|
/** Ms epoch when the slice was last updated. */
|
|
63528
|
-
lastFetchedAt: number()
|
|
63529
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
63530
|
-
* `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
|
|
63531
|
-
* Each upstream `sensor.*` entity surfaces ONE device_class, so one
|
|
63532
|
-
* unit per slice is unambiguous. The UI prefers this over the role's
|
|
63533
|
-
* canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
|
|
63534
|
-
unit: string().optional(),
|
|
63535
|
-
/** Suggested decimal places for numeric display.
|
|
63536
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
63537
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
63538
|
-
* auto-formatting when absent. */
|
|
63539
|
-
precision: number().int().min(0).max(10).optional()
|
|
64027
|
+
lastFetchedAt: number()
|
|
63540
64028
|
});
|
|
63541
64029
|
var powerMeterCapability = {
|
|
63542
64030
|
name: "power-meter",
|
|
@@ -63600,12 +64088,7 @@ var powerMeterCapability = {
|
|
|
63600
64088
|
} },
|
|
63601
64089
|
/** Clock fields: written, but excluded from the compare that decides
|
|
63602
64090
|
* whether persisting is worth a SQLite commit. */
|
|
63603
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
63604
|
-
customize: {
|
|
63605
|
-
version: 1,
|
|
63606
|
-
advanced: "derive",
|
|
63607
|
-
targets: []
|
|
63608
|
-
}
|
|
64091
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
63609
64092
|
};
|
|
63610
64093
|
/**
|
|
63611
64094
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
@@ -63625,11 +64108,11 @@ var powerMeterCapability = {
|
|
|
63625
64108
|
*/
|
|
63626
64109
|
var GpsLocationSchema = object({
|
|
63627
64110
|
/** Latitude in decimal degrees, -90..90. */
|
|
63628
|
-
latitude: number().min(-90).max(90),
|
|
64111
|
+
latitude: number().min(-90).max(90).meta(readingMeta("angle")),
|
|
63629
64112
|
/** Longitude in decimal degrees, -180..180. */
|
|
63630
|
-
longitude: number().min(-180).max(180),
|
|
64113
|
+
longitude: number().min(-180).max(180).meta(readingMeta("angle")),
|
|
63631
64114
|
/** Reported accuracy in meters (lower = better). */
|
|
63632
|
-
accuracyMeters: number().nonnegative()
|
|
64115
|
+
accuracyMeters: number().nonnegative().meta(readingMeta("length"))
|
|
63633
64116
|
});
|
|
63634
64117
|
var PresenceStatusSchema = object({
|
|
63635
64118
|
/** `home` / `not_home` / any user-defined zone name. */
|
|
@@ -63641,7 +64124,7 @@ var PresenceStatusSchema = object({
|
|
|
63641
64124
|
gps: GpsLocationSchema.nullable(),
|
|
63642
64125
|
/** Optional battery level of the tracking device (0..100). Null
|
|
63643
64126
|
* when not reported. */
|
|
63644
|
-
batteryPercent: number().min(0).max(100).nullable(),
|
|
64127
|
+
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63645
64128
|
/** Ms epoch when the slice was last updated. */
|
|
63646
64129
|
lastChangedAt: number()
|
|
63647
64130
|
});
|
|
@@ -63691,19 +64174,10 @@ var presenceCapability = {
|
|
|
63691
64174
|
* `sourceInfo`.
|
|
63692
64175
|
*/
|
|
63693
64176
|
var PressureSensorStatusSchema = object({
|
|
63694
|
-
/** Current pressure in hPa. */
|
|
63695
|
-
hpa: number(),
|
|
64177
|
+
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
64178
|
+
hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
63696
64179
|
/** Ms epoch when the slice was last updated. */
|
|
63697
|
-
lastFetchedAt: number()
|
|
63698
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
63699
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
63700
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
63701
|
-
unit: string().optional(),
|
|
63702
|
-
/** Suggested decimal places for numeric display.
|
|
63703
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
63704
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
63705
|
-
* auto-formatting when absent. */
|
|
63706
|
-
precision: number().int().min(0).max(10).optional()
|
|
64180
|
+
lastFetchedAt: number()
|
|
63707
64181
|
});
|
|
63708
64182
|
var pressureSensorCapability = {
|
|
63709
64183
|
name: "pressure-sensor",
|
|
@@ -64117,8 +64591,8 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
|
|
|
64117
64591
|
}), _void(), { kind: "mutation" });
|
|
64118
64592
|
var PtzAutotrackSettingsSchema = object({
|
|
64119
64593
|
targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
|
|
64120
|
-
stopDelaySeconds: number().int().min(0).max(300),
|
|
64121
|
-
disappearDelaySeconds: number().int().min(0).max(300)
|
|
64594
|
+
stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
|
|
64595
|
+
disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
|
|
64122
64596
|
});
|
|
64123
64597
|
/**
|
|
64124
64598
|
* One option the camera advertises as a valid `targetType`. Drivers
|
|
@@ -64272,7 +64746,7 @@ var RunningStateStatusSchema = object({
|
|
|
64272
64746
|
/** null = unknown — never coerced to false (D393). */
|
|
64273
64747
|
running: boolean().nullable(),
|
|
64274
64748
|
/** Epoch ms the current boolean began; null while `running` is null. */
|
|
64275
|
-
since: number().nullable(),
|
|
64749
|
+
since: number().nullable().meta(readingMeta("none")),
|
|
64276
64750
|
source: RunningStateSourceSchema.meta({ label: "Source" }),
|
|
64277
64751
|
phase: string().nullable(),
|
|
64278
64752
|
/** null until a reading has been published (D393). */
|
|
@@ -64287,7 +64761,6 @@ var RUNNING_DEVICE_TYPES = Object.values(DeviceType);
|
|
|
64287
64761
|
/** Generic Customize target for the running boolean (D936). */
|
|
64288
64762
|
var runningCustomize = {
|
|
64289
64763
|
version: 1,
|
|
64290
|
-
advanced: "derive",
|
|
64291
64764
|
targets: [{
|
|
64292
64765
|
id: "running",
|
|
64293
64766
|
cardinality: "scalar",
|
|
@@ -64307,7 +64780,7 @@ var runningCustomize = {
|
|
|
64307
64780
|
DeviceType.Thermostat,
|
|
64308
64781
|
DeviceType.PetFeeder
|
|
64309
64782
|
],
|
|
64310
|
-
apply: "add
|
|
64783
|
+
apply: "add",
|
|
64311
64784
|
path: "running",
|
|
64312
64785
|
sources: [
|
|
64313
64786
|
{
|
|
@@ -65571,9 +66044,9 @@ var RecordTriggerSchema = _enum([
|
|
|
65571
66044
|
*/
|
|
65572
66045
|
var RecordWindowSchema = object({
|
|
65573
66046
|
trigger: RecordTriggerSchema,
|
|
65574
|
-
day: number().int().min(0).max(6),
|
|
65575
|
-
startMinute: number().int().min(0).max(1439),
|
|
65576
|
-
endMinute: number().int().min(1).max(1440)
|
|
66047
|
+
day: number().int().min(0).max(6).meta(readingMeta("none")),
|
|
66048
|
+
startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
|
|
66049
|
+
endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
|
|
65577
66050
|
});
|
|
65578
66051
|
/** Status of one physical volume, as the camera itself describes it. */
|
|
65579
66052
|
var OnboardStorageVolumeSchema = object({
|
|
@@ -65649,10 +66122,10 @@ var RecordingOnboardStatusSchema = object({
|
|
|
65649
66122
|
/** Master "record to the card at all" switch. */
|
|
65650
66123
|
enabled: boolean().nullable(),
|
|
65651
66124
|
overwriteWhenFull: boolean().nullable(),
|
|
65652
|
-
preRecordSec: number().nullable(),
|
|
65653
|
-
postRecordSec: number().nullable(),
|
|
66125
|
+
preRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
66126
|
+
postRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
65654
66127
|
/** Length of one recorded file, in minutes. */
|
|
65655
|
-
segmentMinutes: number().nullable(),
|
|
66128
|
+
segmentMinutes: number().nullable().meta(readingMeta("none")),
|
|
65656
66129
|
/** The primary track's weekly windows, flattened. */
|
|
65657
66130
|
windows: array(RecordWindowSchema),
|
|
65658
66131
|
/**
|
|
@@ -65668,7 +66141,7 @@ var RecordingOnboardStatusSchema = object({
|
|
|
65668
66141
|
* provider reporting a non-zero count also reports the schedule as not
|
|
65669
66142
|
* writable.
|
|
65670
66143
|
*/
|
|
65671
|
-
unreadableWindows: number(),
|
|
66144
|
+
unreadableWindows: number().meta(readingMeta("count")),
|
|
65672
66145
|
/**
|
|
65673
66146
|
* The camera is scheduled to record and has NO usable storage.
|
|
65674
66147
|
*
|
|
@@ -66513,7 +66986,7 @@ var CamStreamDescriptorSchema = object({
|
|
|
66513
66986
|
url: string().optional(),
|
|
66514
66987
|
codec: string().optional(),
|
|
66515
66988
|
resolution: CamStreamResolutionSchema.optional(),
|
|
66516
|
-
fps: number().positive().optional(),
|
|
66989
|
+
fps: number().positive().optional().meta(readingMeta("rate")),
|
|
66517
66990
|
label: string().optional(),
|
|
66518
66991
|
/** Device-level features (e.g. `battery-operated`) — drives broker policy. */
|
|
66519
66992
|
deviceFeatures: array(string()).optional(),
|
|
@@ -66600,18 +67073,18 @@ var StreamProfileSchema = _enum([
|
|
|
66600
67073
|
]);
|
|
66601
67074
|
/** The current encoding config of one stream profile. */
|
|
66602
67075
|
var StreamProfileConfigSchema = object({
|
|
66603
|
-
width: number(),
|
|
66604
|
-
height: number(),
|
|
67076
|
+
width: number().meta(readingMeta("none")),
|
|
67077
|
+
height: number().meta(readingMeta("none")),
|
|
66605
67078
|
codec: _enum(["h264", "h265"]),
|
|
66606
|
-
framerate: number(),
|
|
66607
|
-
bitrate: number(),
|
|
67079
|
+
framerate: number().meta(readingMeta("rate")),
|
|
67080
|
+
bitrate: number().meta(readingMeta("bitrate")),
|
|
66608
67081
|
bitrateMode: _enum(["vbr", "cbr"]).optional(),
|
|
66609
67082
|
encoderProfile: _enum([
|
|
66610
67083
|
"high",
|
|
66611
67084
|
"main",
|
|
66612
67085
|
"baseline"
|
|
66613
67086
|
]).optional(),
|
|
66614
|
-
gop: number().optional(),
|
|
67087
|
+
gop: number().optional().meta(readingMeta("none")),
|
|
66615
67088
|
/**
|
|
66616
67089
|
* Whether THIS profile currently carries an audio track. READ-ONLY here.
|
|
66617
67090
|
*
|
|
@@ -66832,12 +67305,7 @@ var switchCapability = {
|
|
|
66832
67305
|
label: "On",
|
|
66833
67306
|
kind: "boolean"
|
|
66834
67307
|
}
|
|
66835
|
-
}] }
|
|
66836
|
-
customize: {
|
|
66837
|
-
version: 1,
|
|
66838
|
-
advanced: "derive",
|
|
66839
|
-
targets: []
|
|
66840
|
-
}
|
|
67308
|
+
}] }
|
|
66841
67309
|
};
|
|
66842
67310
|
/** C0 controls, DEL and the C1 range: none of them belongs in a name. */
|
|
66843
67311
|
function hasControlCharacter(value) {
|
|
@@ -67420,35 +67888,21 @@ var tamperCapability = {
|
|
|
67420
67888
|
* entries with `device_class: temperature` and any future native
|
|
67421
67889
|
* thermometer.
|
|
67422
67890
|
*
|
|
67423
|
-
*
|
|
67424
|
-
*
|
|
67425
|
-
* (climate-control's `currentTemp`, energy analytics) can compose
|
|
67426
|
-
* without per-source unit fixups. The canonical display unit (`°C`) is
|
|
67427
|
-
* a descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
67428
|
-
* `sourceInfo`.
|
|
67891
|
+
* The number is Celsius (D944). Providers convert on ingest. A failed
|
|
67892
|
+
* conversion is `null`, never a guess. Display unit is a read-time preference.
|
|
67429
67893
|
*
|
|
67430
67894
|
* Status `lastFetchedAt` lets staleness-aware consumers detect a
|
|
67431
67895
|
* frozen feed (provider hung) distinct from a "temperature hasn't
|
|
67432
67896
|
* changed" steady state.
|
|
67433
67897
|
*/
|
|
67434
67898
|
var TemperatureSensorStatusSchema = object({
|
|
67435
|
-
/** Current temperature in Celsius. */
|
|
67436
|
-
celsius: number().meta({
|
|
67437
|
-
|
|
67899
|
+
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
67900
|
+
celsius: number().nullable().meta(readingMeta("temperature", {
|
|
67901
|
+
precision: 1,
|
|
67438
67902
|
label: "Temperature"
|
|
67439
|
-
}),
|
|
67903
|
+
})),
|
|
67440
67904
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
67441
|
-
lastFetchedAt: number()
|
|
67442
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
67443
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
67444
|
-
* role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
|
|
67445
|
-
* Absent → the UI falls back to the role's canonical unit. */
|
|
67446
|
-
unit: string().optional(),
|
|
67447
|
-
/** Suggested decimal places for numeric display.
|
|
67448
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
67449
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
67450
|
-
* auto-formatting when absent. */
|
|
67451
|
-
precision: number().int().min(0).max(10).optional()
|
|
67905
|
+
lastFetchedAt: number()
|
|
67452
67906
|
});
|
|
67453
67907
|
var temperatureSensorCapability = {
|
|
67454
67908
|
name: "temperature-sensor",
|
|
@@ -67479,12 +67933,7 @@ var temperatureSensorCapability = {
|
|
|
67479
67933
|
} } },
|
|
67480
67934
|
/** Clock fields: written, but excluded from the compare that decides
|
|
67481
67935
|
* whether persisting is worth a SQLite commit. */
|
|
67482
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
67483
|
-
customize: {
|
|
67484
|
-
version: 1,
|
|
67485
|
-
advanced: "derive",
|
|
67486
|
-
targets: []
|
|
67487
|
-
}
|
|
67936
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
67488
67937
|
};
|
|
67489
67938
|
/**
|
|
67490
67939
|
* toast — system-scoped singleton capability that streams toast
|
|
@@ -67767,16 +68216,16 @@ var VacuumControlStatusSchema = object({
|
|
|
67767
68216
|
]),
|
|
67768
68217
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
67769
68218
|
* reading. */
|
|
67770
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
68219
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
67771
68220
|
/** Current fan-speed token (provider-verbatim). Null when unknown or
|
|
67772
68221
|
* the vacuum has no speed control. */
|
|
67773
68222
|
fanSpeed: string().nullable(),
|
|
67774
68223
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
67775
68224
|
availableFanSpeeds: array(string()),
|
|
67776
68225
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
67777
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
68226
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
67778
68227
|
/** Current error code (0 / null = no error). */
|
|
67779
|
-
errorCode: number().nullable(),
|
|
68228
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
67780
68229
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
67781
68230
|
errorLabel: string().nullable(),
|
|
67782
68231
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -67854,7 +68303,7 @@ var ValveStatusSchema = object({
|
|
|
67854
68303
|
]),
|
|
67855
68304
|
/** 0 = fully closed, 100 = fully open. Null when the device has no
|
|
67856
68305
|
* intermediate position surface. */
|
|
67857
|
-
position: number().min(0).max(100).nullable(),
|
|
68306
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
67858
68307
|
/** Ms epoch when the slice was last updated. */
|
|
67859
68308
|
lastChangedAt: number()
|
|
67860
68309
|
});
|
|
@@ -67962,9 +68411,9 @@ var vibrationCapability = {
|
|
|
67962
68411
|
*/
|
|
67963
68412
|
var WaterHeaterStatusSchema = object({
|
|
67964
68413
|
/** Current measured temperature. Null when not reported. */
|
|
67965
|
-
currentTemp: number().nullable(),
|
|
68414
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
67966
68415
|
/** Target temperature setpoint. Null when no setpoint surface. */
|
|
67967
|
-
targetTemp: number().nullable(),
|
|
68416
|
+
targetTemp: number().nullable().meta(readingMeta("temperature")),
|
|
67968
68417
|
/** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
|
|
67969
68418
|
* `heat_pump` / `high_demand` / `performance` / `off`). Null when the
|
|
67970
68419
|
* device reports an unknown state. */
|
|
@@ -67975,9 +68424,9 @@ var WaterHeaterStatusSchema = object({
|
|
|
67975
68424
|
* has no away surface. */
|
|
67976
68425
|
away: boolean().nullable(),
|
|
67977
68426
|
/** HA `min_temp` attribute. Null when not reported. */
|
|
67978
|
-
minTemp: number().nullable(),
|
|
68427
|
+
minTemp: number().nullable().meta(readingMeta("temperature")),
|
|
67979
68428
|
/** HA `max_temp` attribute. Null when not reported. */
|
|
67980
|
-
maxTemp: number().nullable(),
|
|
68429
|
+
maxTemp: number().nullable().meta(readingMeta("temperature")),
|
|
67981
68430
|
/** Ms epoch when the slice was last updated. */
|
|
67982
68431
|
lastChangedAt: number()
|
|
67983
68432
|
});
|
|
@@ -68062,25 +68511,18 @@ var waterHeaterCapability = {
|
|
|
68062
68511
|
* Forecast deferred — current conditions only for v1.
|
|
68063
68512
|
*/
|
|
68064
68513
|
var WeatherStatusSchema = object({
|
|
68065
|
-
/** Verbatim
|
|
68066
|
-
* when no condition has been reported yet. */
|
|
68514
|
+
/** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
|
|
68067
68515
|
condition: string().nullable(),
|
|
68068
|
-
/** Current temperature in
|
|
68069
|
-
temperature: number().nullable(),
|
|
68070
|
-
/** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
|
|
68071
|
-
temperatureUnit: string().nullable(),
|
|
68516
|
+
/** Current temperature in Celsius. Null when not provided or refused. */
|
|
68517
|
+
temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
|
|
68072
68518
|
/** Relative humidity (0..100). Null when not provided. */
|
|
68073
|
-
humidity: number().min(0).max(100).nullable(),
|
|
68074
|
-
/** Barometric pressure in
|
|
68075
|
-
pressure: number().nullable(),
|
|
68076
|
-
/**
|
|
68077
|
-
|
|
68078
|
-
/** Wind speed in the reported unit. Null when not provided. */
|
|
68079
|
-
windSpeed: number().nullable(),
|
|
68080
|
-
/** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
|
|
68081
|
-
windSpeedUnit: string().nullable(),
|
|
68519
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
68520
|
+
/** Barometric pressure in hPa. Null when not provided or refused. */
|
|
68521
|
+
pressure: number().nullable().meta(readingMeta("pressure")),
|
|
68522
|
+
/** Wind speed in metres per second. Null when not provided or refused. */
|
|
68523
|
+
windSpeed: number().nullable().meta(readingMeta("speed")),
|
|
68082
68524
|
/** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
|
|
68083
|
-
windBearing: number().nullable(),
|
|
68525
|
+
windBearing: number().nullable().meta(readingMeta("angle")),
|
|
68084
68526
|
/** Ms epoch when the slice was last updated. */
|
|
68085
68527
|
lastFetchedAt: number()
|
|
68086
68528
|
});
|
|
@@ -68108,12 +68550,12 @@ var weatherCapability = {
|
|
|
68108
68550
|
*/
|
|
68109
68551
|
durability: "session",
|
|
68110
68552
|
/**
|
|
68111
|
-
* History (D850): recorded series.
|
|
68553
|
+
* History (D850): recorded series. Numbers are the quantity base (D944).
|
|
68112
68554
|
*/
|
|
68113
68555
|
history: { fields: {
|
|
68114
68556
|
temperature: {
|
|
68115
68557
|
kind: "gauge",
|
|
68116
|
-
|
|
68558
|
+
unit: "°C",
|
|
68117
68559
|
deadband: .2
|
|
68118
68560
|
},
|
|
68119
68561
|
humidity: {
|
|
@@ -68123,12 +68565,12 @@ var weatherCapability = {
|
|
|
68123
68565
|
},
|
|
68124
68566
|
pressure: {
|
|
68125
68567
|
kind: "gauge",
|
|
68126
|
-
|
|
68568
|
+
unit: "hPa",
|
|
68127
68569
|
deadband: .5
|
|
68128
68570
|
},
|
|
68129
68571
|
windSpeed: {
|
|
68130
68572
|
kind: "gauge",
|
|
68131
|
-
|
|
68573
|
+
unit: "m/s",
|
|
68132
68574
|
deadband: .5
|
|
68133
68575
|
}
|
|
68134
68576
|
} }
|
|
@@ -69029,6 +69471,389 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
69029
69471
|
kind: "mutation",
|
|
69030
69472
|
auth: "admin"
|
|
69031
69473
|
});
|
|
69474
|
+
/**
|
|
69475
|
+
* Frozen unit → spec table. `•` in the doc marks each dimension's canonical
|
|
69476
|
+
* unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
|
|
69477
|
+
* `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
|
|
69478
|
+
* decimals). K derives from `°C = K − 273.15`.
|
|
69479
|
+
*/
|
|
69480
|
+
var UNIT_TABLE = {
|
|
69481
|
+
"°C": {
|
|
69482
|
+
dimension: "temperature",
|
|
69483
|
+
factor: 1,
|
|
69484
|
+
offset: 0
|
|
69485
|
+
},
|
|
69486
|
+
"°F": {
|
|
69487
|
+
dimension: "temperature",
|
|
69488
|
+
factor: 5 / 9,
|
|
69489
|
+
offset: -160 / 9
|
|
69490
|
+
},
|
|
69491
|
+
K: {
|
|
69492
|
+
dimension: "temperature",
|
|
69493
|
+
factor: 1,
|
|
69494
|
+
offset: -273.15
|
|
69495
|
+
},
|
|
69496
|
+
hPa: {
|
|
69497
|
+
dimension: "pressure",
|
|
69498
|
+
factor: 1,
|
|
69499
|
+
offset: 0
|
|
69500
|
+
},
|
|
69501
|
+
kPa: {
|
|
69502
|
+
dimension: "pressure",
|
|
69503
|
+
factor: 10,
|
|
69504
|
+
offset: 0
|
|
69505
|
+
},
|
|
69506
|
+
Pa: {
|
|
69507
|
+
dimension: "pressure",
|
|
69508
|
+
factor: .01,
|
|
69509
|
+
offset: 0
|
|
69510
|
+
},
|
|
69511
|
+
mbar: {
|
|
69512
|
+
dimension: "pressure",
|
|
69513
|
+
factor: 1,
|
|
69514
|
+
offset: 0
|
|
69515
|
+
},
|
|
69516
|
+
bar: {
|
|
69517
|
+
dimension: "pressure",
|
|
69518
|
+
factor: 1e3,
|
|
69519
|
+
offset: 0
|
|
69520
|
+
},
|
|
69521
|
+
inHg: {
|
|
69522
|
+
dimension: "pressure",
|
|
69523
|
+
factor: 33.8639,
|
|
69524
|
+
offset: 0
|
|
69525
|
+
},
|
|
69526
|
+
mmHg: {
|
|
69527
|
+
dimension: "pressure",
|
|
69528
|
+
factor: 1.33322,
|
|
69529
|
+
offset: 0
|
|
69530
|
+
},
|
|
69531
|
+
psi: {
|
|
69532
|
+
dimension: "pressure",
|
|
69533
|
+
factor: 68.9476,
|
|
69534
|
+
offset: 0
|
|
69535
|
+
},
|
|
69536
|
+
"m/s": {
|
|
69537
|
+
dimension: "speed",
|
|
69538
|
+
factor: 1,
|
|
69539
|
+
offset: 0
|
|
69540
|
+
},
|
|
69541
|
+
"km/h": {
|
|
69542
|
+
dimension: "speed",
|
|
69543
|
+
factor: 1 / 3.6,
|
|
69544
|
+
offset: 0
|
|
69545
|
+
},
|
|
69546
|
+
mph: {
|
|
69547
|
+
dimension: "speed",
|
|
69548
|
+
factor: .44704,
|
|
69549
|
+
offset: 0
|
|
69550
|
+
},
|
|
69551
|
+
kn: {
|
|
69552
|
+
dimension: "speed",
|
|
69553
|
+
factor: .514444,
|
|
69554
|
+
offset: 0
|
|
69555
|
+
},
|
|
69556
|
+
"mm/h": {
|
|
69557
|
+
dimension: "precipitation-rate",
|
|
69558
|
+
factor: 1,
|
|
69559
|
+
offset: 0
|
|
69560
|
+
},
|
|
69561
|
+
"in/h": {
|
|
69562
|
+
dimension: "precipitation-rate",
|
|
69563
|
+
factor: 25.4,
|
|
69564
|
+
offset: 0
|
|
69565
|
+
},
|
|
69566
|
+
m: {
|
|
69567
|
+
dimension: "length",
|
|
69568
|
+
factor: 1,
|
|
69569
|
+
offset: 0
|
|
69570
|
+
},
|
|
69571
|
+
mm: {
|
|
69572
|
+
dimension: "length",
|
|
69573
|
+
factor: .001,
|
|
69574
|
+
offset: 0
|
|
69575
|
+
},
|
|
69576
|
+
cm: {
|
|
69577
|
+
dimension: "length",
|
|
69578
|
+
factor: .01,
|
|
69579
|
+
offset: 0
|
|
69580
|
+
},
|
|
69581
|
+
km: {
|
|
69582
|
+
dimension: "length",
|
|
69583
|
+
factor: 1e3,
|
|
69584
|
+
offset: 0
|
|
69585
|
+
},
|
|
69586
|
+
in: {
|
|
69587
|
+
dimension: "length",
|
|
69588
|
+
factor: .0254,
|
|
69589
|
+
offset: 0
|
|
69590
|
+
},
|
|
69591
|
+
ft: {
|
|
69592
|
+
dimension: "length",
|
|
69593
|
+
factor: .3048,
|
|
69594
|
+
offset: 0
|
|
69595
|
+
},
|
|
69596
|
+
mi: {
|
|
69597
|
+
dimension: "length",
|
|
69598
|
+
factor: 1609.344,
|
|
69599
|
+
offset: 0
|
|
69600
|
+
},
|
|
69601
|
+
"m²": {
|
|
69602
|
+
dimension: "area",
|
|
69603
|
+
factor: 1,
|
|
69604
|
+
offset: 0
|
|
69605
|
+
},
|
|
69606
|
+
lx: {
|
|
69607
|
+
dimension: "illuminance",
|
|
69608
|
+
factor: 1,
|
|
69609
|
+
offset: 0
|
|
69610
|
+
},
|
|
69611
|
+
"W/m²": {
|
|
69612
|
+
dimension: "irradiance",
|
|
69613
|
+
factor: 1,
|
|
69614
|
+
offset: 0
|
|
69615
|
+
},
|
|
69616
|
+
W: {
|
|
69617
|
+
dimension: "power",
|
|
69618
|
+
factor: 1,
|
|
69619
|
+
offset: 0
|
|
69620
|
+
},
|
|
69621
|
+
kW: {
|
|
69622
|
+
dimension: "power",
|
|
69623
|
+
factor: 1e3,
|
|
69624
|
+
offset: 0
|
|
69625
|
+
},
|
|
69626
|
+
VA: {
|
|
69627
|
+
dimension: "apparent-power",
|
|
69628
|
+
factor: 1,
|
|
69629
|
+
offset: 0
|
|
69630
|
+
},
|
|
69631
|
+
Wh: {
|
|
69632
|
+
dimension: "energy",
|
|
69633
|
+
factor: 1,
|
|
69634
|
+
offset: 0
|
|
69635
|
+
},
|
|
69636
|
+
kWh: {
|
|
69637
|
+
dimension: "energy",
|
|
69638
|
+
factor: 1e3,
|
|
69639
|
+
offset: 0
|
|
69640
|
+
},
|
|
69641
|
+
MWh: {
|
|
69642
|
+
dimension: "energy",
|
|
69643
|
+
factor: 1e6,
|
|
69644
|
+
offset: 0
|
|
69645
|
+
},
|
|
69646
|
+
V: {
|
|
69647
|
+
dimension: "voltage",
|
|
69648
|
+
factor: 1,
|
|
69649
|
+
offset: 0
|
|
69650
|
+
},
|
|
69651
|
+
mV: {
|
|
69652
|
+
dimension: "voltage",
|
|
69653
|
+
factor: .001,
|
|
69654
|
+
offset: 0
|
|
69655
|
+
},
|
|
69656
|
+
A: {
|
|
69657
|
+
dimension: "current",
|
|
69658
|
+
factor: 1,
|
|
69659
|
+
offset: 0
|
|
69660
|
+
},
|
|
69661
|
+
mA: {
|
|
69662
|
+
dimension: "current",
|
|
69663
|
+
factor: .001,
|
|
69664
|
+
offset: 0
|
|
69665
|
+
},
|
|
69666
|
+
ppm: {
|
|
69667
|
+
dimension: "concentration-volume",
|
|
69668
|
+
factor: 1,
|
|
69669
|
+
offset: 0
|
|
69670
|
+
},
|
|
69671
|
+
ppb: {
|
|
69672
|
+
dimension: "concentration-volume",
|
|
69673
|
+
factor: .001,
|
|
69674
|
+
offset: 0
|
|
69675
|
+
},
|
|
69676
|
+
"µg/m³": {
|
|
69677
|
+
dimension: "concentration-mass",
|
|
69678
|
+
factor: 1,
|
|
69679
|
+
offset: 0
|
|
69680
|
+
},
|
|
69681
|
+
"mg/m³": {
|
|
69682
|
+
dimension: "concentration-mass",
|
|
69683
|
+
factor: 1e3,
|
|
69684
|
+
offset: 0
|
|
69685
|
+
},
|
|
69686
|
+
kg: {
|
|
69687
|
+
dimension: "mass",
|
|
69688
|
+
factor: 1,
|
|
69689
|
+
offset: 0
|
|
69690
|
+
},
|
|
69691
|
+
g: {
|
|
69692
|
+
dimension: "mass",
|
|
69693
|
+
factor: .001,
|
|
69694
|
+
offset: 0
|
|
69695
|
+
},
|
|
69696
|
+
lb: {
|
|
69697
|
+
dimension: "mass",
|
|
69698
|
+
factor: .453592,
|
|
69699
|
+
offset: 0
|
|
69700
|
+
},
|
|
69701
|
+
oz: {
|
|
69702
|
+
dimension: "mass",
|
|
69703
|
+
factor: .0283495,
|
|
69704
|
+
offset: 0
|
|
69705
|
+
},
|
|
69706
|
+
"%": {
|
|
69707
|
+
dimension: "percentage",
|
|
69708
|
+
factor: 1,
|
|
69709
|
+
offset: 0
|
|
69710
|
+
},
|
|
69711
|
+
L: {
|
|
69712
|
+
dimension: "volume",
|
|
69713
|
+
factor: 1,
|
|
69714
|
+
offset: 0
|
|
69715
|
+
},
|
|
69716
|
+
mL: {
|
|
69717
|
+
dimension: "volume",
|
|
69718
|
+
factor: .001,
|
|
69719
|
+
offset: 0
|
|
69720
|
+
},
|
|
69721
|
+
gal: {
|
|
69722
|
+
dimension: "volume",
|
|
69723
|
+
factor: 3.785411784,
|
|
69724
|
+
offset: 0
|
|
69725
|
+
},
|
|
69726
|
+
s: {
|
|
69727
|
+
dimension: "duration",
|
|
69728
|
+
factor: 1,
|
|
69729
|
+
offset: 0
|
|
69730
|
+
},
|
|
69731
|
+
ms: {
|
|
69732
|
+
dimension: "duration",
|
|
69733
|
+
factor: .001,
|
|
69734
|
+
offset: 0
|
|
69735
|
+
},
|
|
69736
|
+
min: {
|
|
69737
|
+
dimension: "duration",
|
|
69738
|
+
factor: 60,
|
|
69739
|
+
offset: 0
|
|
69740
|
+
},
|
|
69741
|
+
h: {
|
|
69742
|
+
dimension: "duration",
|
|
69743
|
+
factor: 3600,
|
|
69744
|
+
offset: 0
|
|
69745
|
+
},
|
|
69746
|
+
d: {
|
|
69747
|
+
dimension: "duration",
|
|
69748
|
+
factor: 86400,
|
|
69749
|
+
offset: 0
|
|
69750
|
+
},
|
|
69751
|
+
B: {
|
|
69752
|
+
dimension: "information",
|
|
69753
|
+
factor: 1,
|
|
69754
|
+
offset: 0
|
|
69755
|
+
},
|
|
69756
|
+
KiB: {
|
|
69757
|
+
dimension: "information",
|
|
69758
|
+
factor: 1024,
|
|
69759
|
+
offset: 0
|
|
69760
|
+
},
|
|
69761
|
+
MiB: {
|
|
69762
|
+
dimension: "information",
|
|
69763
|
+
factor: 1024 ** 2,
|
|
69764
|
+
offset: 0
|
|
69765
|
+
},
|
|
69766
|
+
GiB: {
|
|
69767
|
+
dimension: "information",
|
|
69768
|
+
factor: 1024 ** 3,
|
|
69769
|
+
offset: 0
|
|
69770
|
+
},
|
|
69771
|
+
TiB: {
|
|
69772
|
+
dimension: "information",
|
|
69773
|
+
factor: 1024 ** 4,
|
|
69774
|
+
offset: 0
|
|
69775
|
+
},
|
|
69776
|
+
kB: {
|
|
69777
|
+
dimension: "information",
|
|
69778
|
+
factor: 1e3,
|
|
69779
|
+
offset: 0
|
|
69780
|
+
},
|
|
69781
|
+
MB: {
|
|
69782
|
+
dimension: "information",
|
|
69783
|
+
factor: 1e6,
|
|
69784
|
+
offset: 0
|
|
69785
|
+
},
|
|
69786
|
+
GB: {
|
|
69787
|
+
dimension: "information",
|
|
69788
|
+
factor: 1e9,
|
|
69789
|
+
offset: 0
|
|
69790
|
+
},
|
|
69791
|
+
"°": {
|
|
69792
|
+
dimension: "angle",
|
|
69793
|
+
factor: 1,
|
|
69794
|
+
offset: 0
|
|
69795
|
+
},
|
|
69796
|
+
dBFS: {
|
|
69797
|
+
dimension: "sound-level",
|
|
69798
|
+
factor: 1,
|
|
69799
|
+
offset: 0
|
|
69800
|
+
},
|
|
69801
|
+
dBm: {
|
|
69802
|
+
dimension: "signal-level",
|
|
69803
|
+
factor: 1,
|
|
69804
|
+
offset: 0
|
|
69805
|
+
},
|
|
69806
|
+
mired: {
|
|
69807
|
+
dimension: "colour-temperature",
|
|
69808
|
+
factor: 1,
|
|
69809
|
+
offset: 0
|
|
69810
|
+
},
|
|
69811
|
+
kbps: {
|
|
69812
|
+
dimension: "bitrate",
|
|
69813
|
+
factor: 1,
|
|
69814
|
+
offset: 0
|
|
69815
|
+
},
|
|
69816
|
+
Mbps: {
|
|
69817
|
+
dimension: "bitrate",
|
|
69818
|
+
factor: 1e3,
|
|
69819
|
+
offset: 0
|
|
69820
|
+
},
|
|
69821
|
+
fps: {
|
|
69822
|
+
dimension: "rate",
|
|
69823
|
+
factor: 1,
|
|
69824
|
+
offset: 0
|
|
69825
|
+
}
|
|
69826
|
+
};
|
|
69827
|
+
/** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
|
|
69828
|
+
function specFor(unit) {
|
|
69829
|
+
return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
|
|
69830
|
+
}
|
|
69831
|
+
/** The dimension a unit belongs to, or `undefined` for an unknown unit. */
|
|
69832
|
+
function unitDimension(unit) {
|
|
69833
|
+
return specFor(unit)?.dimension;
|
|
69834
|
+
}
|
|
69835
|
+
/** Value in `spec`'s unit → its dimension's canonical unit. */
|
|
69836
|
+
function toCanonical(value, spec) {
|
|
69837
|
+
return value * spec.factor + spec.offset;
|
|
69838
|
+
}
|
|
69839
|
+
/** Canonical-unit value → the unit described by `spec`. */
|
|
69840
|
+
function fromCanonical(canonical, spec) {
|
|
69841
|
+
return (canonical - spec.offset) / spec.factor;
|
|
69842
|
+
}
|
|
69843
|
+
/**
|
|
69844
|
+
* Graceful variant for the render path: returns the converted value, or
|
|
69845
|
+
* `undefined` when the conversion is refused (unknown unit / cross-dimension).
|
|
69846
|
+
* Never throws — a render caller falls back to the raw value + source unit.
|
|
69847
|
+
*/
|
|
69848
|
+
function tryConvertUnit(value, from, to) {
|
|
69849
|
+
if (from === to) return value;
|
|
69850
|
+
const fromSpec = specFor(from);
|
|
69851
|
+
if (!fromSpec) return void 0;
|
|
69852
|
+
const toSpec = specFor(to);
|
|
69853
|
+
if (!toSpec) return void 0;
|
|
69854
|
+
if (fromSpec.dimension !== toSpec.dimension) return void 0;
|
|
69855
|
+
return fromCanonical(toCanonical(value, fromSpec), toSpec);
|
|
69856
|
+
}
|
|
69032
69857
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
69033
69858
|
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;
|
|
69034
69859
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
@@ -69359,6 +70184,8 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
69359
70184
|
featureProbe: featureProbeCapability,
|
|
69360
70185
|
flood: floodCapability,
|
|
69361
70186
|
gas: gasCapability,
|
|
70187
|
+
groupCommands: groupCommandsCapability,
|
|
70188
|
+
groupStatus: groupStatusCapability,
|
|
69362
70189
|
heatingValve: heatingValveCapability,
|
|
69363
70190
|
humidifier: humidifierCapability,
|
|
69364
70191
|
humiditySensor: humiditySensorCapability,
|
|
@@ -70735,6 +71562,13 @@ Object.freeze({
|
|
|
70735
71562
|
access: "view",
|
|
70736
71563
|
role: "admin"
|
|
70737
71564
|
},
|
|
71565
|
+
"addons.getPackageDocs": {
|
|
71566
|
+
capName: "addons",
|
|
71567
|
+
capScope: "system",
|
|
71568
|
+
addonId: null,
|
|
71569
|
+
access: "view",
|
|
71570
|
+
role: "admin"
|
|
71571
|
+
},
|
|
70738
71572
|
"addons.getVersions": {
|
|
70739
71573
|
capName: "addons",
|
|
70740
71574
|
capScope: "system",
|
|
@@ -73843,6 +74677,20 @@ Object.freeze({
|
|
|
73843
74677
|
access: "view",
|
|
73844
74678
|
role: "viewer"
|
|
73845
74679
|
},
|
|
74680
|
+
"groupCommands.getStatus": {
|
|
74681
|
+
capName: "group-commands",
|
|
74682
|
+
capScope: "device",
|
|
74683
|
+
addonId: null,
|
|
74684
|
+
access: "view",
|
|
74685
|
+
role: "viewer"
|
|
74686
|
+
},
|
|
74687
|
+
"groupStatus.getStatus": {
|
|
74688
|
+
capName: "group-status",
|
|
74689
|
+
capScope: "device",
|
|
74690
|
+
addonId: null,
|
|
74691
|
+
access: "view",
|
|
74692
|
+
role: "viewer"
|
|
74693
|
+
},
|
|
73846
74694
|
"heatingValve.getStatus": {
|
|
73847
74695
|
capName: "heating-valve",
|
|
73848
74696
|
capScope: "device",
|
|
@@ -83665,6 +84513,37 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83665
84513
|
icon: "flame"
|
|
83666
84514
|
}]
|
|
83667
84515
|
},
|
|
84516
|
+
"group-commands": {
|
|
84517
|
+
scope: "device",
|
|
84518
|
+
noun: t("Group command", "Comando di gruppo")
|
|
84519
|
+
},
|
|
84520
|
+
"group-status": {
|
|
84521
|
+
scope: "device",
|
|
84522
|
+
noun: t("Group", "Gruppo"),
|
|
84523
|
+
state: [
|
|
84524
|
+
{
|
|
84525
|
+
kind: "numeric",
|
|
84526
|
+
field: "included",
|
|
84527
|
+
quantity: t("the members reporting", "i membri che rispondono"),
|
|
84528
|
+
label: t("Reporting", "Rispondono"),
|
|
84529
|
+
icon: "users"
|
|
84530
|
+
},
|
|
84531
|
+
{
|
|
84532
|
+
kind: "numeric",
|
|
84533
|
+
field: "excluded",
|
|
84534
|
+
quantity: t("the members left out", "i membri esclusi"),
|
|
84535
|
+
label: t("Excluded", "Esclusi"),
|
|
84536
|
+
icon: "users"
|
|
84537
|
+
},
|
|
84538
|
+
{
|
|
84539
|
+
kind: "numeric",
|
|
84540
|
+
field: "total",
|
|
84541
|
+
quantity: t("the member count", "il numero di membri"),
|
|
84542
|
+
label: t("Members", "Membri"),
|
|
84543
|
+
icon: "sigma"
|
|
84544
|
+
}
|
|
84545
|
+
]
|
|
84546
|
+
},
|
|
83668
84547
|
"humidity-sensor": {
|
|
83669
84548
|
scope: "device",
|
|
83670
84549
|
noun: t("Humidity", "Umidità"),
|
|
@@ -83698,7 +84577,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83698
84577
|
field: "value",
|
|
83699
84578
|
quantity: t("the reading", "il valore"),
|
|
83700
84579
|
label: t("Reading", "Valore"),
|
|
83701
|
-
unitField: "unit",
|
|
83702
84580
|
icon: "gauge"
|
|
83703
84581
|
}]
|
|
83704
84582
|
},
|
|
@@ -83834,7 +84712,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83834
84712
|
field: "temperature",
|
|
83835
84713
|
quantity: t("the outdoor temperature", "la temperatura esterna"),
|
|
83836
84714
|
label: t("Temperature", "Temperatura"),
|
|
83837
|
-
|
|
84715
|
+
unit: "°C",
|
|
83838
84716
|
icon: "thermometer"
|
|
83839
84717
|
},
|
|
83840
84718
|
{
|
|
@@ -83850,7 +84728,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83850
84728
|
field: "windSpeed",
|
|
83851
84729
|
quantity: t("the wind speed", "la velocità del vento"),
|
|
83852
84730
|
label: t("Wind", "Vento"),
|
|
83853
|
-
|
|
84731
|
+
unit: "m/s",
|
|
83854
84732
|
icon: "wind"
|
|
83855
84733
|
},
|
|
83856
84734
|
{
|
|
@@ -83858,7 +84736,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
83858
84736
|
field: "pressure",
|
|
83859
84737
|
quantity: t("the air pressure", "la pressione atmosferica"),
|
|
83860
84738
|
label: t("Pressure", "Pressione"),
|
|
83861
|
-
|
|
84739
|
+
unit: "hPa",
|
|
83862
84740
|
icon: "gauge"
|
|
83863
84741
|
}
|
|
83864
84742
|
]
|
|
@@ -85131,6 +86009,16 @@ Object.freeze({
|
|
|
85131
86009
|
form: "single",
|
|
85132
86010
|
optional: false
|
|
85133
86011
|
}],
|
|
86012
|
+
"groupCommands.getStatus": [{
|
|
86013
|
+
name: "deviceId",
|
|
86014
|
+
form: "single",
|
|
86015
|
+
optional: false
|
|
86016
|
+
}],
|
|
86017
|
+
"groupStatus.getStatus": [{
|
|
86018
|
+
name: "deviceId",
|
|
86019
|
+
form: "single",
|
|
86020
|
+
optional: false
|
|
86021
|
+
}],
|
|
85134
86022
|
"heatingValve.getStatus": [{
|
|
85135
86023
|
name: "deviceId",
|
|
85136
86024
|
form: "single",
|
|
@@ -87115,6 +88003,8 @@ new Map(Object.entries({
|
|
|
87115
88003
|
"detection-blind": "device-events",
|
|
87116
88004
|
"motion-noisy": "device-events",
|
|
87117
88005
|
"motion-calm": "device-events",
|
|
88006
|
+
"package-llm-silent": "device-events",
|
|
88007
|
+
"package-llm-answered": "device-events",
|
|
87118
88008
|
"recording-stalled": "device-events",
|
|
87119
88009
|
"recording-healthy": "device-events",
|
|
87120
88010
|
"recording-degraded": "device-events",
|
|
@@ -87198,6 +88088,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
87198
88088
|
label: "Motion without a subject",
|
|
87199
88089
|
kinds: ["motion-noisy", "motion-calm"]
|
|
87200
88090
|
},
|
|
88091
|
+
{
|
|
88092
|
+
id: "package-llm",
|
|
88093
|
+
family: "device-events",
|
|
88094
|
+
label: "Package confirm",
|
|
88095
|
+
kinds: ["package-llm-silent", "package-llm-answered"]
|
|
88096
|
+
},
|
|
87201
88097
|
{
|
|
87202
88098
|
id: "recording",
|
|
87203
88099
|
family: "device-events",
|
|
@@ -87379,6 +88275,8 @@ var DEVICE_EVENTS = [
|
|
|
87379
88275
|
"detection-blind",
|
|
87380
88276
|
"motion-noisy",
|
|
87381
88277
|
"motion-calm",
|
|
88278
|
+
"package-llm-silent",
|
|
88279
|
+
"package-llm-answered",
|
|
87382
88280
|
"recording-stalled",
|
|
87383
88281
|
"recording-healthy",
|
|
87384
88282
|
"recording-degraded",
|
|
@@ -87647,6 +88545,15 @@ new Map([
|
|
|
87647
88545
|
snapshotLabel: "Cameras with constant motion now",
|
|
87648
88546
|
snapshotSection: null
|
|
87649
88547
|
},
|
|
88548
|
+
{
|
|
88549
|
+
id: "package-llm-confirm",
|
|
88550
|
+
family: "device-events",
|
|
88551
|
+
label: "Package confirm (not answering / answering again)",
|
|
88552
|
+
bad: ["package-llm-silent"],
|
|
88553
|
+
recovery: ["package-llm-answered"],
|
|
88554
|
+
snapshotLabel: "Cameras whose package confirm is not answering",
|
|
88555
|
+
snapshotSection: null
|
|
88556
|
+
},
|
|
87650
88557
|
{
|
|
87651
88558
|
id: "recording-health",
|
|
87652
88559
|
family: "device-events",
|
|
@@ -88223,6 +89130,96 @@ DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
|
|
|
88223
89130
|
DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
88224
89131
|
DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
|
|
88225
89132
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
89133
|
+
/**
|
|
89134
|
+
* Convert an upstream number into the stored base of its quantity (D944 §4).
|
|
89135
|
+
* An unknown unit or a unit from another dimension is a refusal: the caller
|
|
89136
|
+
* logs it and publishes null (D393). This function never guesses.
|
|
89137
|
+
*/
|
|
89138
|
+
var DIMENSION_QUANTITY = {
|
|
89139
|
+
temperature: "temperature",
|
|
89140
|
+
pressure: "pressure",
|
|
89141
|
+
speed: "speed",
|
|
89142
|
+
"precipitation-rate": "precipitation-rate",
|
|
89143
|
+
length: "length",
|
|
89144
|
+
area: "area",
|
|
89145
|
+
illuminance: "illuminance",
|
|
89146
|
+
irradiance: "irradiance",
|
|
89147
|
+
power: "power",
|
|
89148
|
+
"apparent-power": "apparent-power",
|
|
89149
|
+
energy: "energy",
|
|
89150
|
+
voltage: "voltage",
|
|
89151
|
+
current: "current",
|
|
89152
|
+
"concentration-volume": "concentration-volume",
|
|
89153
|
+
"concentration-mass": "concentration-mass",
|
|
89154
|
+
mass: "mass",
|
|
89155
|
+
percentage: "percentage",
|
|
89156
|
+
volume: "volume",
|
|
89157
|
+
duration: "duration",
|
|
89158
|
+
information: "information",
|
|
89159
|
+
angle: "angle",
|
|
89160
|
+
"sound-level": "sound-level",
|
|
89161
|
+
"signal-level": "signal-level",
|
|
89162
|
+
"colour-temperature": "colour-temperature",
|
|
89163
|
+
bitrate: "bitrate",
|
|
89164
|
+
rate: "rate"
|
|
89165
|
+
};
|
|
89166
|
+
/**
|
|
89167
|
+
* Quantity implied by a unit symbol. `ppb` and `ms` are the two symbols
|
|
89168
|
+
* whose stored base is not the dimension's canonical unit. `mm` is length;
|
|
89169
|
+
* precipitation is chosen by the caller (a device class), not by the symbol.
|
|
89170
|
+
*/
|
|
89171
|
+
function quantityFromUnit(rawUnit) {
|
|
89172
|
+
const unit = normalizeUnit(rawUnit);
|
|
89173
|
+
if (unit === void 0) return void 0;
|
|
89174
|
+
if (unit === "ppb") return "concentration-ppb";
|
|
89175
|
+
if (unit === "ms") return "duration-ms";
|
|
89176
|
+
const dimension = unitDimension(unit);
|
|
89177
|
+
if (dimension === void 0) return void 0;
|
|
89178
|
+
return DIMENSION_QUANTITY[dimension];
|
|
89179
|
+
}
|
|
89180
|
+
function ingestReading(input) {
|
|
89181
|
+
if (!Number.isFinite(input.value)) return {
|
|
89182
|
+
ok: false,
|
|
89183
|
+
reason: "non-finite"
|
|
89184
|
+
};
|
|
89185
|
+
if (input.quantity === "device") return {
|
|
89186
|
+
ok: false,
|
|
89187
|
+
reason: "unknown-unit"
|
|
89188
|
+
};
|
|
89189
|
+
const raw = input.rawUnit?.trim();
|
|
89190
|
+
if (raw === void 0 || raw.length === 0) return {
|
|
89191
|
+
ok: true,
|
|
89192
|
+
value: input.value
|
|
89193
|
+
};
|
|
89194
|
+
const unit = normalizeUnit(raw);
|
|
89195
|
+
if (unit === void 0) return {
|
|
89196
|
+
ok: false,
|
|
89197
|
+
reason: "unknown-unit"
|
|
89198
|
+
};
|
|
89199
|
+
const base = baseUnitOf(input.quantity);
|
|
89200
|
+
if (base === void 0) {
|
|
89201
|
+
if (unitDimension(unit) !== void 0) return {
|
|
89202
|
+
ok: false,
|
|
89203
|
+
reason: "cross-dimension"
|
|
89204
|
+
};
|
|
89205
|
+
return {
|
|
89206
|
+
ok: false,
|
|
89207
|
+
reason: "unknown-unit"
|
|
89208
|
+
};
|
|
89209
|
+
}
|
|
89210
|
+
const converted = tryConvertUnit(input.value, unit, base);
|
|
89211
|
+
if (converted === void 0) return unitDimension(unit) === void 0 ? {
|
|
89212
|
+
ok: false,
|
|
89213
|
+
reason: "unknown-unit"
|
|
89214
|
+
} : {
|
|
89215
|
+
ok: false,
|
|
89216
|
+
reason: "cross-dimension"
|
|
89217
|
+
};
|
|
89218
|
+
return {
|
|
89219
|
+
ok: true,
|
|
89220
|
+
value: converted
|
|
89221
|
+
};
|
|
89222
|
+
}
|
|
88226
89223
|
var MB = 1024 * 1024;
|
|
88227
89224
|
1024 * MB, 3072 * MB;
|
|
88228
89225
|
//#endregion
|
|
@@ -94609,6 +95606,23 @@ function capForQuantity(quantity) {
|
|
|
94609
95606
|
* the generic `numeric-sensor` cap but carry a typed role, so the UI renders a
|
|
94610
95607
|
* wind / rain / UV / solar glyph instead of a bare hash.
|
|
94611
95608
|
*/
|
|
95609
|
+
/** Ecowitt quantity → the stored reading quantity. Unknown names return null. */
|
|
95610
|
+
function readingQuantityFor(quantity) {
|
|
95611
|
+
switch (quantity) {
|
|
95612
|
+
case "temperature": return "temperature";
|
|
95613
|
+
case "humidity": return "humidity";
|
|
95614
|
+
case "pressure": return "pressure";
|
|
95615
|
+
case "wind_speed": return "speed";
|
|
95616
|
+
case "wind_direction": return "angle";
|
|
95617
|
+
case "precipitation": return "precipitation";
|
|
95618
|
+
case "precipitation_rate": return "precipitation-rate";
|
|
95619
|
+
case "uv": return "index";
|
|
95620
|
+
case "irradiance": return "irradiance";
|
|
95621
|
+
case "lightning_distance": return "length";
|
|
95622
|
+
case "lightning_count": return "count";
|
|
95623
|
+
default: return null;
|
|
95624
|
+
}
|
|
95625
|
+
}
|
|
94612
95626
|
function roleForQuantity(quantity) {
|
|
94613
95627
|
switch (quantity) {
|
|
94614
95628
|
case "temperature": return DeviceRole.TemperatureSensor;
|
|
@@ -94867,13 +95881,6 @@ var ecowittSensorSchema = object({
|
|
|
94867
95881
|
system: literal("ecowitt").optional(),
|
|
94868
95882
|
integrationId: string().optional()
|
|
94869
95883
|
});
|
|
94870
|
-
/**
|
|
94871
|
-
* Slice key the V-battery `numeric-sensor` cap writes/reads. It is the cap's own
|
|
94872
|
-
* name (`'numeric-sensor'`) — the same key the codegen `getStatus` reads — so
|
|
94873
|
-
* the manual `setCapState` here lands in the slice the framework serves. Only
|
|
94874
|
-
* registered when the PRIMARY cap is NOT itself `numeric-sensor` (no collision).
|
|
94875
|
-
*/
|
|
94876
|
-
var BATTERY_VOLTAGE_CAP = numericSensorCapability.name;
|
|
94877
95884
|
var SUPPORTED_CAPS = new Set([
|
|
94878
95885
|
"temperature-sensor",
|
|
94879
95886
|
"humidity-sensor",
|
|
@@ -94892,16 +95899,10 @@ var SUPPORTED_CAPS = new Set([
|
|
|
94892
95899
|
* library discriminates two battery forms via `batteryUnit`:
|
|
94893
95900
|
* - `'%'` (binary/bar batteries, already decoded to a percent) → the typed
|
|
94894
95901
|
* `battery` cap (which models a 0..100 percentage).
|
|
94895
|
-
* - `'V'` (raw voltage, e.g. WS90/WH90/WS85 capacitor + soil/leaf push) →
|
|
94896
|
-
* `
|
|
94897
|
-
*
|
|
94898
|
-
*
|
|
94899
|
-
*
|
|
94900
|
-
* A child registers AT MOST one battery cap. When the child's PRIMARY sensor cap
|
|
94901
|
-
* is itself `numeric-sensor` (e.g. a precipitation channel) a V-battery cannot
|
|
94902
|
-
* also be hosted as a second `numeric-sensor` on the same device (the cap slice
|
|
94903
|
-
* is keyed by cap name) — that rare case is skipped with a debug note rather
|
|
94904
|
-
* than adding a new cap.
|
|
95902
|
+
* - `'V'` (raw voltage, e.g. WS90/WH90/WS85 capacitor + soil/leaf push) →
|
|
95903
|
+
* `battery.volts`. A voltage has no honest percentage, so `percentage`
|
|
95904
|
+
* stays null. The field lives on `battery`, so a numeric primary sensor
|
|
95905
|
+
* and a voltage battery do not share a slice.
|
|
94905
95906
|
*/
|
|
94906
95907
|
var EcowittSensorDevice = class extends BaseDevice {
|
|
94907
95908
|
features = [];
|
|
@@ -94909,12 +95910,12 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
94909
95910
|
gatewayId;
|
|
94910
95911
|
capName;
|
|
94911
95912
|
valueKey;
|
|
95913
|
+
ecowittQuantity;
|
|
94912
95914
|
/**
|
|
94913
95915
|
* Which battery cap (if any) was registered for this sensor:
|
|
94914
95916
|
* - `'percent'` → the typed `battery` cap (percentage),
|
|
94915
|
-
* - `'voltage'` →
|
|
94916
|
-
* - `'none'` → no battery cap
|
|
94917
|
-
* whose primary cap is already `numeric-sensor`).
|
|
95917
|
+
* - `'voltage'` → `battery.volts` (percentage stays null),
|
|
95918
|
+
* - `'none'` → no battery cap.
|
|
94918
95919
|
*/
|
|
94919
95920
|
batteryMode = "none";
|
|
94920
95921
|
snapshotUnsub = null;
|
|
@@ -94926,10 +95927,13 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
94926
95927
|
const cap = capForQuantity(persisted.quantity);
|
|
94927
95928
|
this.capName = SUPPORTED_CAPS.has(cap) ? cap : "numeric-sensor";
|
|
94928
95929
|
this.valueKey = valueFieldForCap(this.capName);
|
|
95930
|
+
this.ecowittQuantity = String(persisted.quantity);
|
|
94929
95931
|
this.online = true;
|
|
95932
|
+
const named = readingQuantityFor(persisted.quantity);
|
|
94930
95933
|
this.updateSourceInfo({
|
|
94931
95934
|
id: this.sensorId,
|
|
94932
|
-
system: "ecowitt"
|
|
95935
|
+
system: "ecowitt",
|
|
95936
|
+
...this.capName === "numeric-sensor" && named !== null ? { quantity: named } : {}
|
|
94933
95937
|
});
|
|
94934
95938
|
}
|
|
94935
95939
|
resolveSensor() {
|
|
@@ -94973,10 +95977,10 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
94973
95977
|
const sensor = this.resolveSensor();
|
|
94974
95978
|
if (sensor === null) return;
|
|
94975
95979
|
const lastFetchedAt = sensor.lastUpdated || Date.now();
|
|
95980
|
+
const stored = this.toBase(sensor.value, sensor.unit);
|
|
94976
95981
|
const slice = {
|
|
94977
|
-
[this.valueKey]: clampForCap(this.capName,
|
|
94978
|
-
lastFetchedAt
|
|
94979
|
-
...sensor.unit.length > 0 ? { unit: sensor.unit } : {}
|
|
95982
|
+
[this.valueKey]: stored === null ? null : clampForCap(this.capName, stored),
|
|
95983
|
+
lastFetchedAt
|
|
94980
95984
|
};
|
|
94981
95985
|
this.runtimeState.setCapState(this.capName, slice);
|
|
94982
95986
|
if (this.batteryMode === "percent" && sensor.batteryUnit === "%" && typeof sensor.battery === "number") {
|
|
@@ -94990,11 +95994,13 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
94990
95994
|
}
|
|
94991
95995
|
if (this.batteryMode === "voltage" && sensor.batteryUnit === "V" && typeof sensor.battery === "number" && Number.isFinite(sensor.battery)) {
|
|
94992
95996
|
const next = {
|
|
94993
|
-
|
|
94994
|
-
|
|
94995
|
-
|
|
95997
|
+
percentage: null,
|
|
95998
|
+
volts: sensor.battery,
|
|
95999
|
+
charging: "none",
|
|
96000
|
+
sleeping: false,
|
|
96001
|
+
lastUpdated: lastFetchedAt
|
|
94996
96002
|
};
|
|
94997
|
-
this.runtimeState.setCapState(
|
|
96003
|
+
this.runtimeState.setCapState("battery", next);
|
|
94998
96004
|
}
|
|
94999
96005
|
}
|
|
95000
96006
|
registerSensorCap() {
|
|
@@ -95047,11 +96053,8 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
95047
96053
|
}
|
|
95048
96054
|
}
|
|
95049
96055
|
/**
|
|
95050
|
-
* Register the
|
|
95051
|
-
*
|
|
95052
|
-
* - `'V'` → `numeric-sensor` cap carrying volts (unit `'V'`), unless the
|
|
95053
|
-
* sensor's primary cap is already `numeric-sensor` (slice-key collision),
|
|
95054
|
-
* in which case it is skipped with a debug note.
|
|
96056
|
+
* Register the `battery` cap. Percent batteries fill `percentage`. Voltage
|
|
96057
|
+
* batteries fill `volts` and leave `percentage` null.
|
|
95055
96058
|
*/
|
|
95056
96059
|
maybeRegisterBatteryCap() {
|
|
95057
96060
|
const sensor = this.resolveSensor();
|
|
@@ -95078,29 +96081,63 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
95078
96081
|
this.runtimeState.setCapState("battery", COLD);
|
|
95079
96082
|
this.batteryMode = "percent";
|
|
95080
96083
|
}
|
|
95081
|
-
/**
|
|
95082
|
-
* Register a `numeric-sensor` (unit `'V'`) cap for a raw-voltage battery. The
|
|
95083
|
-
* voltage has no honest percentage, so the percentage-shaped `battery` cap is
|
|
95084
|
-
* not used. Skipped when the primary cap already occupies the `numeric-sensor`
|
|
95085
|
-
* slice on this device (a second instance would collide).
|
|
95086
|
-
*/
|
|
96084
|
+
/** Voltage batteries share the `battery` cap. `percentage` stays unknown. */
|
|
95087
96085
|
registerVoltageBatteryCap() {
|
|
95088
|
-
if (this.capName === "numeric-sensor") {
|
|
95089
|
-
this.ctx.logger.debug("ecowitt sensor: V-battery not surfaced — primary cap already numeric-sensor", { meta: { sensorId: this.sensorId } });
|
|
95090
|
-
return;
|
|
95091
|
-
}
|
|
95092
96086
|
const COLD = {
|
|
95093
|
-
|
|
95094
|
-
|
|
95095
|
-
|
|
96087
|
+
percentage: null,
|
|
96088
|
+
volts: null,
|
|
96089
|
+
charging: "unknown",
|
|
96090
|
+
sleeping: false,
|
|
96091
|
+
lastUpdated: 0
|
|
95096
96092
|
};
|
|
95097
|
-
this.ctx.registerNativeCap(
|
|
95098
|
-
|
|
96093
|
+
this.ctx.registerNativeCap(batteryCapability, {
|
|
96094
|
+
getStatus: async () => this.runtimeState.getCapState("battery") ?? COLD,
|
|
96095
|
+
wakeForStream: async () => ({
|
|
96096
|
+
awoke: true,
|
|
96097
|
+
durationMs: 0
|
|
96098
|
+
})
|
|
96099
|
+
});
|
|
96100
|
+
this.runtimeState.setCapState("battery", COLD);
|
|
95099
96101
|
this.batteryMode = "voltage";
|
|
95100
96102
|
}
|
|
95101
96103
|
readSlice(fallback) {
|
|
95102
96104
|
return this.runtimeState.getCapState(this.capName) ?? fallback;
|
|
95103
96105
|
}
|
|
96106
|
+
/** Station number → the quantity's base. An unknown unit publishes null. */
|
|
96107
|
+
toBase(value, unit) {
|
|
96108
|
+
const named = readingQuantityFor(this.ecowittQuantity);
|
|
96109
|
+
const fromUnit = unit.length === 0 ? void 0 : quantityFromUnit(unit);
|
|
96110
|
+
const quantity = named ?? (unit.length === 0 ? "none" : fromUnit ?? null);
|
|
96111
|
+
if (quantity === null || quantity === "device") {
|
|
96112
|
+
this.ctx.logger.warn("numeric reading refused", {
|
|
96113
|
+
tags: { deviceId: this.id },
|
|
96114
|
+
meta: {
|
|
96115
|
+
quantity: "device",
|
|
96116
|
+
unit,
|
|
96117
|
+
reason: "unknown-unit"
|
|
96118
|
+
}
|
|
96119
|
+
});
|
|
96120
|
+
return null;
|
|
96121
|
+
}
|
|
96122
|
+
const result = ingestReading({
|
|
96123
|
+
value,
|
|
96124
|
+
rawUnit: unit.length > 0 ? unit : void 0,
|
|
96125
|
+
quantity
|
|
96126
|
+
});
|
|
96127
|
+
if (!result.ok) {
|
|
96128
|
+
this.ctx.logger.warn("numeric reading refused", {
|
|
96129
|
+
tags: { deviceId: this.id },
|
|
96130
|
+
meta: {
|
|
96131
|
+
quantity,
|
|
96132
|
+
unit,
|
|
96133
|
+
reason: result.reason
|
|
96134
|
+
}
|
|
96135
|
+
});
|
|
96136
|
+
return null;
|
|
96137
|
+
}
|
|
96138
|
+
if (this.capName === "numeric-sensor" && this.sourceInfo.quantity !== quantity) this.updateSourceInfo({ quantity });
|
|
96139
|
+
return result.value;
|
|
96140
|
+
}
|
|
95104
96141
|
};
|
|
95105
96142
|
/** Clamp a raw SI value to the cap's slice constraints (humidity/lux are bounded). */
|
|
95106
96143
|
function clampForCap(cap, value) {
|