@camstack/addon-provider-homematic 1.2.157 → 1.2.159
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 +19 -0
- package/dist/addon.js +1549 -523
- package/dist/addon.mjs +1549 -523
- package/package.json +4 -2
package/dist/addon.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import { join } from "path";
|
|
|
9
9
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
10
10
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
11
11
|
//#endregion
|
|
12
|
-
//#region ../types/dist/event-category-
|
|
12
|
+
//#region ../types/dist/event-category-CT-hz5Rb.mjs
|
|
13
13
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
14
14
|
EventCategory["SystemBoot"] = "system.boot";
|
|
15
15
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -335,6 +335,14 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
335
335
|
/** The recovery edge of {@link PipelineMotionNoisy}: the motion fell back, or a detection arrived. */
|
|
336
336
|
EventCategory["PipelineMotionCalm"] = "pipeline.motion-calm";
|
|
337
337
|
/**
|
|
338
|
+
* A camera's package LLM confirm has failed closed three times in a row
|
|
339
|
+
* (D931, 2026-10-26). One edge per camera until
|
|
340
|
+
* {@link PipelinePackageLlmAnswered}.
|
|
341
|
+
*/
|
|
342
|
+
EventCategory["PipelinePackageLlmSilent"] = "pipeline.package-llm-silent";
|
|
343
|
+
/** The recovery edge of {@link PipelinePackageLlmSilent}: a confirm returned yes or no. */
|
|
344
|
+
EventCategory["PipelinePackageLlmAnswered"] = "pipeline.package-llm-answered";
|
|
345
|
+
/**
|
|
338
346
|
* Per-camera pipeline config was mutated by the orchestrator
|
|
339
347
|
* (a settings change via `setAgentInferenceDevices` /
|
|
340
348
|
* `setCameraStepToggle` / `setCameraStepSettings`). Orchestrator
|
|
@@ -5577,7 +5585,7 @@ var ZodIssueCode = {
|
|
|
5577
5585
|
var ZodFirstPartyTypeKind;
|
|
5578
5586
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5579
5587
|
//#endregion
|
|
5580
|
-
//#region ../types/dist/sleep-
|
|
5588
|
+
//#region ../types/dist/sleep-bykKrTgu.mjs
|
|
5581
5589
|
/**
|
|
5582
5590
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5583
5591
|
* window to float samples (D455).
|
|
@@ -8319,6 +8327,99 @@ function defineEntryKind(name, spec) {
|
|
|
8319
8327
|
if (defined === void 0) throw new DeclarationRefusedError(`entry kind "${name}" was not built`);
|
|
8320
8328
|
return defined;
|
|
8321
8329
|
}
|
|
8330
|
+
var READING_QUANTITIES = [
|
|
8331
|
+
"temperature",
|
|
8332
|
+
"pressure",
|
|
8333
|
+
"speed",
|
|
8334
|
+
"precipitation",
|
|
8335
|
+
"precipitation-rate",
|
|
8336
|
+
"length",
|
|
8337
|
+
"area",
|
|
8338
|
+
"illuminance",
|
|
8339
|
+
"irradiance",
|
|
8340
|
+
"power",
|
|
8341
|
+
"energy",
|
|
8342
|
+
"voltage",
|
|
8343
|
+
"current",
|
|
8344
|
+
"apparent-power",
|
|
8345
|
+
"humidity",
|
|
8346
|
+
"percentage",
|
|
8347
|
+
"concentration-volume",
|
|
8348
|
+
"concentration-mass",
|
|
8349
|
+
"concentration-ppb",
|
|
8350
|
+
"volume",
|
|
8351
|
+
"duration",
|
|
8352
|
+
"duration-ms",
|
|
8353
|
+
"information",
|
|
8354
|
+
"sound-level",
|
|
8355
|
+
"signal-level",
|
|
8356
|
+
"colour-temperature",
|
|
8357
|
+
"angle",
|
|
8358
|
+
"bitrate",
|
|
8359
|
+
"rate",
|
|
8360
|
+
"mass",
|
|
8361
|
+
"index",
|
|
8362
|
+
"ratio",
|
|
8363
|
+
"count",
|
|
8364
|
+
"none",
|
|
8365
|
+
"device"
|
|
8366
|
+
];
|
|
8367
|
+
var ReadingQuantitySchema = _enum(READING_QUANTITIES);
|
|
8368
|
+
new Set(READING_QUANTITIES);
|
|
8369
|
+
/**
|
|
8370
|
+
* Stored unit symbol for a quantity. `none` means the number is dimensionless
|
|
8371
|
+
* (or an index / ratio / count). `device` means the cap has no single unit.
|
|
8372
|
+
*/
|
|
8373
|
+
var QUANTITY_BASE = {
|
|
8374
|
+
temperature: "°C",
|
|
8375
|
+
pressure: "hPa",
|
|
8376
|
+
speed: "m/s",
|
|
8377
|
+
precipitation: "mm",
|
|
8378
|
+
"precipitation-rate": "mm/h",
|
|
8379
|
+
length: "m",
|
|
8380
|
+
area: "m²",
|
|
8381
|
+
illuminance: "lx",
|
|
8382
|
+
irradiance: "W/m²",
|
|
8383
|
+
power: "W",
|
|
8384
|
+
energy: "kWh",
|
|
8385
|
+
voltage: "V",
|
|
8386
|
+
current: "A",
|
|
8387
|
+
"apparent-power": "VA",
|
|
8388
|
+
humidity: "%",
|
|
8389
|
+
percentage: "%",
|
|
8390
|
+
"concentration-volume": "ppm",
|
|
8391
|
+
"concentration-mass": "µg/m³",
|
|
8392
|
+
"concentration-ppb": "ppb",
|
|
8393
|
+
volume: "L",
|
|
8394
|
+
duration: "s",
|
|
8395
|
+
"duration-ms": "ms",
|
|
8396
|
+
information: "B",
|
|
8397
|
+
"sound-level": "dBFS",
|
|
8398
|
+
"signal-level": "dBm",
|
|
8399
|
+
"colour-temperature": "mired",
|
|
8400
|
+
angle: "°",
|
|
8401
|
+
bitrate: "kbps",
|
|
8402
|
+
rate: "fps",
|
|
8403
|
+
mass: "kg",
|
|
8404
|
+
index: "none",
|
|
8405
|
+
ratio: "none",
|
|
8406
|
+
count: "none",
|
|
8407
|
+
none: "none",
|
|
8408
|
+
device: "device"
|
|
8409
|
+
};
|
|
8410
|
+
/** The stored unit, or `undefined` when the quantity has no unit symbol. */
|
|
8411
|
+
function baseUnitOf(quantity) {
|
|
8412
|
+
const symbol = QUANTITY_BASE[quantity];
|
|
8413
|
+
if (symbol === "none" || symbol === "device") return void 0;
|
|
8414
|
+
return symbol;
|
|
8415
|
+
}
|
|
8416
|
+
function readingMeta(quantity, extras) {
|
|
8417
|
+
return {
|
|
8418
|
+
quantity,
|
|
8419
|
+
...extras?.precision !== void 0 ? { precision: extras.precision } : {},
|
|
8420
|
+
...extras?.label !== void 0 ? { label: extras.label } : {}
|
|
8421
|
+
};
|
|
8422
|
+
}
|
|
8322
8423
|
/** Shared Zod schemas used across streaming capabilities. */
|
|
8323
8424
|
var CamProfileSchema = _enum([
|
|
8324
8425
|
"high",
|
|
@@ -8346,8 +8447,8 @@ var CamStreamKindSchema = _enum([
|
|
|
8346
8447
|
"derived"
|
|
8347
8448
|
]);
|
|
8348
8449
|
var CamStreamResolutionSchema = object({
|
|
8349
|
-
width: number().int().positive(),
|
|
8350
|
-
height: number().int().positive()
|
|
8450
|
+
width: number().int().positive().meta(readingMeta("none")),
|
|
8451
|
+
height: number().int().positive().meta(readingMeta("none"))
|
|
8351
8452
|
});
|
|
8352
8453
|
var CameraStreamSchema = object({
|
|
8353
8454
|
/** Stable, provider-assigned id unique within the (deviceId) scope. */
|
|
@@ -8865,6 +8966,8 @@ var CAP_NAMES = Object.freeze([
|
|
|
8865
8966
|
"filesystem-browse",
|
|
8866
8967
|
"flood",
|
|
8867
8968
|
"gas",
|
|
8969
|
+
"group-commands",
|
|
8970
|
+
"group-status",
|
|
8868
8971
|
"heating-valve",
|
|
8869
8972
|
"humidifier",
|
|
8870
8973
|
"humidity-sensor",
|
|
@@ -8994,14 +9097,6 @@ Object.freeze({
|
|
|
8994
9097
|
{
|
|
8995
9098
|
"path": "aqi",
|
|
8996
9099
|
"valueType": "number"
|
|
8997
|
-
},
|
|
8998
|
-
{
|
|
8999
|
-
"path": "unit",
|
|
9000
|
-
"valueType": "string"
|
|
9001
|
-
},
|
|
9002
|
-
{
|
|
9003
|
-
"path": "precision",
|
|
9004
|
-
"valueType": "number"
|
|
9005
9100
|
}
|
|
9006
9101
|
],
|
|
9007
9102
|
"alarm-panel": [
|
|
@@ -9037,20 +9132,10 @@ Object.freeze({
|
|
|
9037
9132
|
"valueType": "boolean"
|
|
9038
9133
|
}
|
|
9039
9134
|
],
|
|
9040
|
-
"ambient-light-sensor": [
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
},
|
|
9045
|
-
{
|
|
9046
|
-
"path": "unit",
|
|
9047
|
-
"valueType": "string"
|
|
9048
|
-
},
|
|
9049
|
-
{
|
|
9050
|
-
"path": "precision",
|
|
9051
|
-
"valueType": "number"
|
|
9052
|
-
}
|
|
9053
|
-
],
|
|
9135
|
+
"ambient-light-sensor": [{
|
|
9136
|
+
"path": "lux",
|
|
9137
|
+
"valueType": "number"
|
|
9138
|
+
}],
|
|
9054
9139
|
"audio-metrics": [
|
|
9055
9140
|
{
|
|
9056
9141
|
"path": "ts",
|
|
@@ -9130,6 +9215,10 @@ Object.freeze({
|
|
|
9130
9215
|
"path": "lastUpdated",
|
|
9131
9216
|
"valueType": "number"
|
|
9132
9217
|
},
|
|
9218
|
+
{
|
|
9219
|
+
"path": "volts",
|
|
9220
|
+
"valueType": "number"
|
|
9221
|
+
},
|
|
9133
9222
|
{
|
|
9134
9223
|
"path": "lastContactAt",
|
|
9135
9224
|
"valueType": "number"
|
|
@@ -9372,10 +9461,6 @@ Object.freeze({
|
|
|
9372
9461
|
"datetime"
|
|
9373
9462
|
]
|
|
9374
9463
|
},
|
|
9375
|
-
{
|
|
9376
|
-
"path": "unit",
|
|
9377
|
-
"valueType": "string"
|
|
9378
|
-
},
|
|
9379
9464
|
{
|
|
9380
9465
|
"path": "min",
|
|
9381
9466
|
"valueType": "number"
|
|
@@ -9387,19 +9472,6 @@ Object.freeze({
|
|
|
9387
9472
|
{
|
|
9388
9473
|
"path": "step",
|
|
9389
9474
|
"valueType": "number"
|
|
9390
|
-
},
|
|
9391
|
-
{
|
|
9392
|
-
"path": "precision",
|
|
9393
|
-
"valueType": "number"
|
|
9394
|
-
},
|
|
9395
|
-
{
|
|
9396
|
-
"path": "format",
|
|
9397
|
-
"valueType": "enum",
|
|
9398
|
-
"enumValues": [
|
|
9399
|
-
"date",
|
|
9400
|
-
"time",
|
|
9401
|
-
"datetime"
|
|
9402
|
-
]
|
|
9403
9475
|
}
|
|
9404
9476
|
],
|
|
9405
9477
|
"cover": [
|
|
@@ -9640,13 +9712,8 @@ Object.freeze({
|
|
|
9640
9712
|
"path": "value",
|
|
9641
9713
|
"valueType": "string"
|
|
9642
9714
|
}, {
|
|
9643
|
-
"path": "
|
|
9644
|
-
"valueType": "
|
|
9645
|
-
"enumValues": [
|
|
9646
|
-
"date",
|
|
9647
|
-
"time",
|
|
9648
|
-
"datetime"
|
|
9649
|
-
]
|
|
9715
|
+
"path": "timestamp",
|
|
9716
|
+
"valueType": "boolean"
|
|
9650
9717
|
}],
|
|
9651
9718
|
"event-emitter": [
|
|
9652
9719
|
{
|
|
@@ -9719,6 +9786,20 @@ Object.freeze({
|
|
|
9719
9786
|
"path": "detected",
|
|
9720
9787
|
"valueType": "boolean"
|
|
9721
9788
|
}],
|
|
9789
|
+
"group-status": [
|
|
9790
|
+
{
|
|
9791
|
+
"path": "included",
|
|
9792
|
+
"valueType": "number"
|
|
9793
|
+
},
|
|
9794
|
+
{
|
|
9795
|
+
"path": "excluded",
|
|
9796
|
+
"valueType": "number"
|
|
9797
|
+
},
|
|
9798
|
+
{
|
|
9799
|
+
"path": "total",
|
|
9800
|
+
"valueType": "number"
|
|
9801
|
+
}
|
|
9802
|
+
],
|
|
9722
9803
|
"heating-valve": [{
|
|
9723
9804
|
"path": "position",
|
|
9724
9805
|
"valueType": "number"
|
|
@@ -9753,20 +9834,10 @@ Object.freeze({
|
|
|
9753
9834
|
"valueType": "number"
|
|
9754
9835
|
}
|
|
9755
9836
|
],
|
|
9756
|
-
"humidity-sensor": [
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
9760
|
-
},
|
|
9761
|
-
{
|
|
9762
|
-
"path": "unit",
|
|
9763
|
-
"valueType": "string"
|
|
9764
|
-
},
|
|
9765
|
-
{
|
|
9766
|
-
"path": "precision",
|
|
9767
|
-
"valueType": "number"
|
|
9768
|
-
}
|
|
9769
|
-
],
|
|
9837
|
+
"humidity-sensor": [{
|
|
9838
|
+
"path": "percent",
|
|
9839
|
+
"valueType": "number"
|
|
9840
|
+
}],
|
|
9770
9841
|
"image": [{
|
|
9771
9842
|
"path": "url",
|
|
9772
9843
|
"valueType": "string"
|
|
@@ -10105,20 +10176,10 @@ Object.freeze({
|
|
|
10105
10176
|
"valueType": "boolean"
|
|
10106
10177
|
}
|
|
10107
10178
|
],
|
|
10108
|
-
"numeric-sensor": [
|
|
10109
|
-
|
|
10110
|
-
|
|
10111
|
-
|
|
10112
|
-
},
|
|
10113
|
-
{
|
|
10114
|
-
"path": "unit",
|
|
10115
|
-
"valueType": "string"
|
|
10116
|
-
},
|
|
10117
|
-
{
|
|
10118
|
-
"path": "precision",
|
|
10119
|
-
"valueType": "number"
|
|
10120
|
-
}
|
|
10121
|
-
],
|
|
10179
|
+
"numeric-sensor": [{
|
|
10180
|
+
"path": "value",
|
|
10181
|
+
"valueType": "number"
|
|
10182
|
+
}],
|
|
10122
10183
|
"pet-feeder": [
|
|
10123
10184
|
{
|
|
10124
10185
|
"path": "foodLevel",
|
|
@@ -10210,14 +10271,6 @@ Object.freeze({
|
|
|
10210
10271
|
{
|
|
10211
10272
|
"path": "powerFactor",
|
|
10212
10273
|
"valueType": "number"
|
|
10213
|
-
},
|
|
10214
|
-
{
|
|
10215
|
-
"path": "unit",
|
|
10216
|
-
"valueType": "string"
|
|
10217
|
-
},
|
|
10218
|
-
{
|
|
10219
|
-
"path": "precision",
|
|
10220
|
-
"valueType": "number"
|
|
10221
10274
|
}
|
|
10222
10275
|
],
|
|
10223
10276
|
"presence": [
|
|
@@ -10246,20 +10299,10 @@ Object.freeze({
|
|
|
10246
10299
|
"valueType": "number"
|
|
10247
10300
|
}
|
|
10248
10301
|
],
|
|
10249
|
-
"pressure-sensor": [
|
|
10250
|
-
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
},
|
|
10254
|
-
{
|
|
10255
|
-
"path": "unit",
|
|
10256
|
-
"valueType": "string"
|
|
10257
|
-
},
|
|
10258
|
-
{
|
|
10259
|
-
"path": "precision",
|
|
10260
|
-
"valueType": "number"
|
|
10261
|
-
}
|
|
10262
|
-
],
|
|
10302
|
+
"pressure-sensor": [{
|
|
10303
|
+
"path": "hpa",
|
|
10304
|
+
"valueType": "number"
|
|
10305
|
+
}],
|
|
10263
10306
|
"privacy-mask": [{
|
|
10264
10307
|
"path": "enabled",
|
|
10265
10308
|
"valueType": "boolean"
|
|
@@ -10570,20 +10613,10 @@ Object.freeze({
|
|
|
10570
10613
|
"path": "tampered",
|
|
10571
10614
|
"valueType": "boolean"
|
|
10572
10615
|
}],
|
|
10573
|
-
"temperature-sensor": [
|
|
10574
|
-
|
|
10575
|
-
|
|
10576
|
-
|
|
10577
|
-
},
|
|
10578
|
-
{
|
|
10579
|
-
"path": "unit",
|
|
10580
|
-
"valueType": "string"
|
|
10581
|
-
},
|
|
10582
|
-
{
|
|
10583
|
-
"path": "precision",
|
|
10584
|
-
"valueType": "number"
|
|
10585
|
-
}
|
|
10586
|
-
],
|
|
10616
|
+
"temperature-sensor": [{
|
|
10617
|
+
"path": "celsius",
|
|
10618
|
+
"valueType": "number"
|
|
10619
|
+
}],
|
|
10587
10620
|
"update": [
|
|
10588
10621
|
{
|
|
10589
10622
|
"path": "currentVersion",
|
|
@@ -10694,10 +10727,6 @@ Object.freeze({
|
|
|
10694
10727
|
"path": "temperature",
|
|
10695
10728
|
"valueType": "number"
|
|
10696
10729
|
},
|
|
10697
|
-
{
|
|
10698
|
-
"path": "temperatureUnit",
|
|
10699
|
-
"valueType": "string"
|
|
10700
|
-
},
|
|
10701
10730
|
{
|
|
10702
10731
|
"path": "humidity",
|
|
10703
10732
|
"valueType": "number"
|
|
@@ -10706,18 +10735,10 @@ Object.freeze({
|
|
|
10706
10735
|
"path": "pressure",
|
|
10707
10736
|
"valueType": "number"
|
|
10708
10737
|
},
|
|
10709
|
-
{
|
|
10710
|
-
"path": "pressureUnit",
|
|
10711
|
-
"valueType": "string"
|
|
10712
|
-
},
|
|
10713
10738
|
{
|
|
10714
10739
|
"path": "windSpeed",
|
|
10715
10740
|
"valueType": "number"
|
|
10716
10741
|
},
|
|
10717
|
-
{
|
|
10718
|
-
"path": "windSpeedUnit",
|
|
10719
|
-
"valueType": "string"
|
|
10720
|
-
},
|
|
10721
10742
|
{
|
|
10722
10743
|
"path": "windBearing",
|
|
10723
10744
|
"valueType": "number"
|
|
@@ -11120,7 +11141,7 @@ function errMsg(err) {
|
|
|
11120
11141
|
return String(err);
|
|
11121
11142
|
}
|
|
11122
11143
|
//#endregion
|
|
11123
|
-
//#region ../types/dist/composition-
|
|
11144
|
+
//#region ../types/dist/composition-OhpuespA.mjs
|
|
11124
11145
|
/**
|
|
11125
11146
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11126
11147
|
*
|
|
@@ -11875,15 +11896,74 @@ function validateExpressionSource(src) {
|
|
|
11875
11896
|
}
|
|
11876
11897
|
return null;
|
|
11877
11898
|
}
|
|
11878
|
-
/**
|
|
11879
|
-
|
|
11880
|
-
|
|
11881
|
-
|
|
11899
|
+
/**
|
|
11900
|
+
* A composition source identity: `(addonId, stableId)`, never a numeric device
|
|
11901
|
+
* id. Split out so helper schemas can name it without a cycle through
|
|
11902
|
+
* `composition.ts` (D659).
|
|
11903
|
+
*/
|
|
11882
11904
|
var CompositionSourceRefSchema = object({
|
|
11883
11905
|
/** The owning addon; `stableId` is unique only within it. */
|
|
11884
11906
|
addonId: string().min(1),
|
|
11885
11907
|
stableId: string().min(1)
|
|
11886
11908
|
});
|
|
11909
|
+
var GROUP_TEMPLATE_IDS = [
|
|
11910
|
+
"temperature",
|
|
11911
|
+
"humidity",
|
|
11912
|
+
"pressure",
|
|
11913
|
+
"illuminance",
|
|
11914
|
+
"power",
|
|
11915
|
+
"energy",
|
|
11916
|
+
"battery",
|
|
11917
|
+
"numeric",
|
|
11918
|
+
"motion",
|
|
11919
|
+
"contact",
|
|
11920
|
+
"leak",
|
|
11921
|
+
"smoke",
|
|
11922
|
+
"occupancy",
|
|
11923
|
+
"binary",
|
|
11924
|
+
"switch",
|
|
11925
|
+
"light"
|
|
11926
|
+
];
|
|
11927
|
+
var NUMERIC_AGGREGATES = [
|
|
11928
|
+
"mean",
|
|
11929
|
+
"median",
|
|
11930
|
+
"min",
|
|
11931
|
+
"max",
|
|
11932
|
+
"sum"
|
|
11933
|
+
];
|
|
11934
|
+
var BINARY_AGGREGATES = ["any", "all"];
|
|
11935
|
+
/** How an actuator group decides it is on. Unknown members never count as off. */
|
|
11936
|
+
var ACTIVE_CRITERIA = ["at-least-one", "all"];
|
|
11937
|
+
var AGGREGATE_REDUCERS = [
|
|
11938
|
+
"mean",
|
|
11939
|
+
"median",
|
|
11940
|
+
"min",
|
|
11941
|
+
"max",
|
|
11942
|
+
"sum",
|
|
11943
|
+
"any",
|
|
11944
|
+
"all"
|
|
11945
|
+
];
|
|
11946
|
+
var GroupTemplateIdSchema = _enum(GROUP_TEMPLATE_IDS);
|
|
11947
|
+
var AggregateReducerSchema = _enum(AGGREGATE_REDUCERS);
|
|
11948
|
+
var ActiveCriterionSchema = _enum(ACTIVE_CRITERIA);
|
|
11949
|
+
/** One member. The field is stored so occupancy can name motion or binary. */
|
|
11950
|
+
var HelperMemberSchema = object({
|
|
11951
|
+
source: CompositionSourceRefSchema,
|
|
11952
|
+
cap: string().min(1),
|
|
11953
|
+
fieldPath: string().min(1)
|
|
11954
|
+
});
|
|
11955
|
+
var HelperSpecSchema = discriminatedUnion("kind", [object({
|
|
11956
|
+
kind: literal("group"),
|
|
11957
|
+
templateId: GroupTemplateIdSchema,
|
|
11958
|
+
aggregate: AggregateReducerSchema,
|
|
11959
|
+
members: array(HelperMemberSchema).min(1).max(64),
|
|
11960
|
+
/** Actuator groups only. Readings leave this unset. */
|
|
11961
|
+
active: ActiveCriterionSchema.optional()
|
|
11962
|
+
})]);
|
|
11963
|
+
/** A composed device's stableId is this prefix plus the block id. */
|
|
11964
|
+
var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
|
|
11965
|
+
/** Longest snippet a `code` source may carry (slice 4). */
|
|
11966
|
+
var MAX_COMPOSITION_CODE_LENGTH = 2e4;
|
|
11887
11967
|
/** One field of one capability of one source device. */
|
|
11888
11968
|
var CompositionFieldReadSchema = object({
|
|
11889
11969
|
source: CompositionSourceRefSchema,
|
|
@@ -11927,7 +12007,33 @@ var CompositionCodeSourceSchema = object({
|
|
|
11927
12007
|
var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
11928
12008
|
CompositionFromSourceSchema,
|
|
11929
12009
|
CompositionExpressionSourceSchema,
|
|
11930
|
-
CompositionCodeSourceSchema
|
|
12010
|
+
CompositionCodeSourceSchema,
|
|
12011
|
+
object({
|
|
12012
|
+
kind: literal("aggregate"),
|
|
12013
|
+
reducer: _enum([
|
|
12014
|
+
"mean",
|
|
12015
|
+
"median",
|
|
12016
|
+
"min",
|
|
12017
|
+
"max",
|
|
12018
|
+
"sum",
|
|
12019
|
+
"any",
|
|
12020
|
+
"all"
|
|
12021
|
+
]),
|
|
12022
|
+
reads: array(CompositionFieldReadSchema).min(1).max(64),
|
|
12023
|
+
/** `value` is the reading; the others are the member counts. */
|
|
12024
|
+
publish: _enum([
|
|
12025
|
+
"value",
|
|
12026
|
+
"included",
|
|
12027
|
+
"excluded",
|
|
12028
|
+
"total"
|
|
12029
|
+
]),
|
|
12030
|
+
/** What a `value` publish does when nobody is included. Counts ignore this. */
|
|
12031
|
+
empty: _enum([
|
|
12032
|
+
"null",
|
|
12033
|
+
"omit",
|
|
12034
|
+
"offline"
|
|
12035
|
+
])
|
|
12036
|
+
})
|
|
11931
12037
|
]);
|
|
11932
12038
|
var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
|
|
11933
12039
|
kind: literal("forward"),
|
|
@@ -11973,7 +12079,13 @@ var CompositionSchema = object({
|
|
|
11973
12079
|
kind: literal("existing"),
|
|
11974
12080
|
device: CompositionSourceRefSchema
|
|
11975
12081
|
})]),
|
|
11976
|
-
features: array(CompositionFeatureSchema).min(1).max(16)
|
|
12082
|
+
features: array(CompositionFeatureSchema).min(1).max(16),
|
|
12083
|
+
/**
|
|
12084
|
+
* Present when the block is a helper template. Absent on a free
|
|
12085
|
+
* composition (the room example, device 4603). The editor reads this,
|
|
12086
|
+
* never the compiled features.
|
|
12087
|
+
*/
|
|
12088
|
+
helper: HelperSpecSchema.optional()
|
|
11977
12089
|
}).superRefine((composition, ctx) => {
|
|
11978
12090
|
const seen = /* @__PURE__ */ new Set();
|
|
11979
12091
|
composition.features.forEach((feature, index) => {
|
|
@@ -18230,7 +18342,11 @@ var CompositionProblemSchema = object({
|
|
|
18230
18342
|
"item-field-reserved",
|
|
18231
18343
|
"target-missing",
|
|
18232
18344
|
"target-unreadable",
|
|
18233
|
-
"not-replaceable"
|
|
18345
|
+
"not-replaceable",
|
|
18346
|
+
"native-capability-replace",
|
|
18347
|
+
"mixed-quantity",
|
|
18348
|
+
"helper-mismatch",
|
|
18349
|
+
"member-constraint"
|
|
18234
18350
|
]),
|
|
18235
18351
|
path: string(),
|
|
18236
18352
|
message: string()
|
|
@@ -18251,7 +18367,8 @@ var CompositionUnavailableModeSchema = _enum([
|
|
|
18251
18367
|
"null",
|
|
18252
18368
|
"offline",
|
|
18253
18369
|
"unknown",
|
|
18254
|
-
"release"
|
|
18370
|
+
"release",
|
|
18371
|
+
"omit"
|
|
18255
18372
|
]);
|
|
18256
18373
|
/**
|
|
18257
18374
|
* `derived` reads a source · `clock` reads only `now`, recomputed when a sibling
|
|
@@ -18288,9 +18405,11 @@ var CompositionFieldPlanSchema = object({
|
|
|
18288
18405
|
reads: array(CompositionFieldReadSchema)
|
|
18289
18406
|
});
|
|
18290
18407
|
/**
|
|
18291
|
-
* What a feature does to its target (D663): `new` builds a capability on a
|
|
18292
|
-
* device; on an existing device it `add`s a capability the device lacks
|
|
18293
|
-
*
|
|
18408
|
+
* What a feature does to its target (D663, D944): `new` builds a capability on a
|
|
18409
|
+
* new device; on an existing device it `add`s a capability the device lacks.
|
|
18410
|
+
* Replacing a capability the device already serves is refused
|
|
18411
|
+
* (`native-capability-replace`), not planned. `replace` remains on the claim
|
|
18412
|
+
* wire for a claim the hub already holds.
|
|
18294
18413
|
*/
|
|
18295
18414
|
var CompositionFeatureModeSchema = _enum([
|
|
18296
18415
|
"new",
|
|
@@ -18463,6 +18582,275 @@ Object.freeze({
|
|
|
18463
18582
|
result: ["number", "null"]
|
|
18464
18583
|
}
|
|
18465
18584
|
});
|
|
18585
|
+
/**
|
|
18586
|
+
* One catalog row per base reading a group helper can build (D944 §3).
|
|
18587
|
+
* Data, not a form default written into the block.
|
|
18588
|
+
*/
|
|
18589
|
+
var NOW = {
|
|
18590
|
+
fieldPath: "lastFetchedAt",
|
|
18591
|
+
expr: "now"
|
|
18592
|
+
};
|
|
18593
|
+
var CHANGED = {
|
|
18594
|
+
fieldPath: "lastChangedAt",
|
|
18595
|
+
expr: "now"
|
|
18596
|
+
};
|
|
18597
|
+
function numeric(input) {
|
|
18598
|
+
return {
|
|
18599
|
+
...input,
|
|
18600
|
+
family: "numeric",
|
|
18601
|
+
deviceType: DeviceType.Sensor,
|
|
18602
|
+
memberDeviceType: DeviceType.Sensor,
|
|
18603
|
+
commandCaps: [],
|
|
18604
|
+
baseUnit: baseUnitOf(input.quantity),
|
|
18605
|
+
aggregates: NUMERIC_AGGREGATES,
|
|
18606
|
+
accept: [{
|
|
18607
|
+
cap: input.outputCap,
|
|
18608
|
+
fieldPath: input.outputField
|
|
18609
|
+
}]
|
|
18610
|
+
};
|
|
18611
|
+
}
|
|
18612
|
+
function binary(input) {
|
|
18613
|
+
return {
|
|
18614
|
+
...input,
|
|
18615
|
+
family: "binary",
|
|
18616
|
+
deviceType: DeviceType.Sensor,
|
|
18617
|
+
memberDeviceType: DeviceType.Sensor,
|
|
18618
|
+
commandCaps: [],
|
|
18619
|
+
quantity: "none",
|
|
18620
|
+
baseUnit: void 0,
|
|
18621
|
+
aggregates: BINARY_AGGREGATES,
|
|
18622
|
+
empty: "offline"
|
|
18623
|
+
};
|
|
18624
|
+
}
|
|
18625
|
+
var GROUP_TEMPLATES = [
|
|
18626
|
+
numeric({
|
|
18627
|
+
id: "temperature",
|
|
18628
|
+
summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
|
|
18629
|
+
role: DeviceRole.TemperatureSensor,
|
|
18630
|
+
outputCap: "temperature-sensor",
|
|
18631
|
+
outputField: "celsius",
|
|
18632
|
+
quantity: "temperature",
|
|
18633
|
+
defaultAggregate: "mean",
|
|
18634
|
+
empty: "null",
|
|
18635
|
+
companions: [NOW]
|
|
18636
|
+
}),
|
|
18637
|
+
numeric({
|
|
18638
|
+
id: "humidity",
|
|
18639
|
+
summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18640
|
+
role: DeviceRole.HumiditySensor,
|
|
18641
|
+
outputCap: "humidity-sensor",
|
|
18642
|
+
outputField: "percent",
|
|
18643
|
+
quantity: "humidity",
|
|
18644
|
+
defaultAggregate: "mean",
|
|
18645
|
+
empty: "null",
|
|
18646
|
+
companions: [NOW]
|
|
18647
|
+
}),
|
|
18648
|
+
numeric({
|
|
18649
|
+
id: "pressure",
|
|
18650
|
+
summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
|
|
18651
|
+
role: DeviceRole.PressureSensor,
|
|
18652
|
+
outputCap: "pressure-sensor",
|
|
18653
|
+
outputField: "hpa",
|
|
18654
|
+
quantity: "pressure",
|
|
18655
|
+
defaultAggregate: "mean",
|
|
18656
|
+
empty: "null",
|
|
18657
|
+
companions: [NOW]
|
|
18658
|
+
}),
|
|
18659
|
+
numeric({
|
|
18660
|
+
id: "illuminance",
|
|
18661
|
+
summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
|
|
18662
|
+
role: DeviceRole.AmbientLightSensor,
|
|
18663
|
+
outputCap: "ambient-light-sensor",
|
|
18664
|
+
outputField: "lux",
|
|
18665
|
+
quantity: "illuminance",
|
|
18666
|
+
defaultAggregate: "mean",
|
|
18667
|
+
empty: "null",
|
|
18668
|
+
companions: [NOW]
|
|
18669
|
+
}),
|
|
18670
|
+
numeric({
|
|
18671
|
+
id: "power",
|
|
18672
|
+
summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
|
|
18673
|
+
role: DeviceRole.PowerSensor,
|
|
18674
|
+
outputCap: "power-meter",
|
|
18675
|
+
outputField: "watts",
|
|
18676
|
+
quantity: "power",
|
|
18677
|
+
defaultAggregate: "sum",
|
|
18678
|
+
empty: "omit",
|
|
18679
|
+
companions: [NOW]
|
|
18680
|
+
}),
|
|
18681
|
+
numeric({
|
|
18682
|
+
id: "energy",
|
|
18683
|
+
summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
|
|
18684
|
+
role: DeviceRole.EnergySensor,
|
|
18685
|
+
outputCap: "power-meter",
|
|
18686
|
+
outputField: "kwhTotal",
|
|
18687
|
+
quantity: "energy",
|
|
18688
|
+
defaultAggregate: "sum",
|
|
18689
|
+
empty: "omit",
|
|
18690
|
+
companions: [NOW]
|
|
18691
|
+
}),
|
|
18692
|
+
numeric({
|
|
18693
|
+
id: "battery",
|
|
18694
|
+
summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18695
|
+
role: DeviceRole.BatterySensor,
|
|
18696
|
+
outputCap: "battery",
|
|
18697
|
+
outputField: "percentage",
|
|
18698
|
+
quantity: "percentage",
|
|
18699
|
+
defaultAggregate: "mean",
|
|
18700
|
+
empty: "null",
|
|
18701
|
+
companions: [
|
|
18702
|
+
{
|
|
18703
|
+
fieldPath: "charging",
|
|
18704
|
+
expr: "\"unknown\""
|
|
18705
|
+
},
|
|
18706
|
+
{
|
|
18707
|
+
fieldPath: "sleeping",
|
|
18708
|
+
expr: "false"
|
|
18709
|
+
},
|
|
18710
|
+
{
|
|
18711
|
+
fieldPath: "lastUpdated",
|
|
18712
|
+
expr: "now"
|
|
18713
|
+
}
|
|
18714
|
+
]
|
|
18715
|
+
}),
|
|
18716
|
+
numeric({
|
|
18717
|
+
id: "numeric",
|
|
18718
|
+
summary: "A numeric reading in the members’ shared quantity. Mixed quantities are refused. Nobody reporting publishes null, not 0.",
|
|
18719
|
+
role: DeviceRole.NumericSensor,
|
|
18720
|
+
outputCap: "numeric-sensor",
|
|
18721
|
+
outputField: "value",
|
|
18722
|
+
quantity: "device",
|
|
18723
|
+
defaultAggregate: "mean",
|
|
18724
|
+
empty: "null",
|
|
18725
|
+
companions: [NOW]
|
|
18726
|
+
}),
|
|
18727
|
+
binary({
|
|
18728
|
+
id: "motion",
|
|
18729
|
+
summary: "Any or all motion. Offline and unknown members are left out, never read as off. Nobody reporting takes the device offline.",
|
|
18730
|
+
role: DeviceRole.MotionSensor,
|
|
18731
|
+
outputCap: "motion",
|
|
18732
|
+
outputField: "detected",
|
|
18733
|
+
defaultAggregate: "any",
|
|
18734
|
+
accept: [{
|
|
18735
|
+
cap: "motion",
|
|
18736
|
+
fieldPath: "detected"
|
|
18737
|
+
}],
|
|
18738
|
+
companions: [{
|
|
18739
|
+
fieldPath: "lastDetectedAt",
|
|
18740
|
+
expr: "null"
|
|
18741
|
+
}, {
|
|
18742
|
+
fieldPath: "autoClearAfterMs",
|
|
18743
|
+
expr: "null"
|
|
18744
|
+
}]
|
|
18745
|
+
}),
|
|
18746
|
+
binary({
|
|
18747
|
+
id: "contact",
|
|
18748
|
+
summary: "Any or all open contacts. Offline and unknown members are left out, never read as closed.",
|
|
18749
|
+
role: DeviceRole.ContactSensor,
|
|
18750
|
+
outputCap: "contact",
|
|
18751
|
+
outputField: "entryOpen",
|
|
18752
|
+
defaultAggregate: "any",
|
|
18753
|
+
accept: [{
|
|
18754
|
+
cap: "contact",
|
|
18755
|
+
fieldPath: "entryOpen"
|
|
18756
|
+
}],
|
|
18757
|
+
companions: [CHANGED]
|
|
18758
|
+
}),
|
|
18759
|
+
binary({
|
|
18760
|
+
id: "leak",
|
|
18761
|
+
summary: "Any or all leak detections. Offline and unknown members are left out, never read as dry.",
|
|
18762
|
+
role: DeviceRole.LeakSensor,
|
|
18763
|
+
outputCap: "flood",
|
|
18764
|
+
outputField: "flooded",
|
|
18765
|
+
defaultAggregate: "any",
|
|
18766
|
+
accept: [{
|
|
18767
|
+
cap: "flood",
|
|
18768
|
+
fieldPath: "flooded"
|
|
18769
|
+
}],
|
|
18770
|
+
companions: [CHANGED]
|
|
18771
|
+
}),
|
|
18772
|
+
binary({
|
|
18773
|
+
id: "smoke",
|
|
18774
|
+
summary: "Any or all smoke detections. Offline and unknown members are left out, never read as clear.",
|
|
18775
|
+
role: DeviceRole.SmokeSensor,
|
|
18776
|
+
outputCap: "smoke",
|
|
18777
|
+
outputField: "detected",
|
|
18778
|
+
defaultAggregate: "any",
|
|
18779
|
+
accept: [{
|
|
18780
|
+
cap: "smoke",
|
|
18781
|
+
fieldPath: "detected"
|
|
18782
|
+
}],
|
|
18783
|
+
companions: [CHANGED]
|
|
18784
|
+
}),
|
|
18785
|
+
binary({
|
|
18786
|
+
id: "occupancy",
|
|
18787
|
+
summary: "Any or all of motion or binary sensors, published as a binary. Presence zones are not aggregated. Nobody reporting is offline, not off.",
|
|
18788
|
+
role: DeviceRole.BinarySensor,
|
|
18789
|
+
outputCap: "binary",
|
|
18790
|
+
outputField: "on",
|
|
18791
|
+
defaultAggregate: "any",
|
|
18792
|
+
accept: [{
|
|
18793
|
+
cap: "motion",
|
|
18794
|
+
fieldPath: "detected"
|
|
18795
|
+
}, {
|
|
18796
|
+
cap: "binary",
|
|
18797
|
+
fieldPath: "on"
|
|
18798
|
+
}],
|
|
18799
|
+
companions: [CHANGED]
|
|
18800
|
+
}),
|
|
18801
|
+
binary({
|
|
18802
|
+
id: "binary",
|
|
18803
|
+
summary: "Any or all generic binary sensors. Offline and unknown members are left out, never read as off.",
|
|
18804
|
+
role: DeviceRole.BinarySensor,
|
|
18805
|
+
outputCap: "binary",
|
|
18806
|
+
outputField: "on",
|
|
18807
|
+
defaultAggregate: "any",
|
|
18808
|
+
accept: [{
|
|
18809
|
+
cap: "binary",
|
|
18810
|
+
fieldPath: "on"
|
|
18811
|
+
}],
|
|
18812
|
+
companions: [CHANGED]
|
|
18813
|
+
}),
|
|
18814
|
+
actuator({
|
|
18815
|
+
id: "switch",
|
|
18816
|
+
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.",
|
|
18817
|
+
deviceType: DeviceType.Switch,
|
|
18818
|
+
outputCap: "switch",
|
|
18819
|
+
commandCaps: ["switch"]
|
|
18820
|
+
}),
|
|
18821
|
+
actuator({
|
|
18822
|
+
id: "light",
|
|
18823
|
+
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.",
|
|
18824
|
+
deviceType: DeviceType.Light,
|
|
18825
|
+
outputCap: "switch",
|
|
18826
|
+
commandCaps: [
|
|
18827
|
+
"switch",
|
|
18828
|
+
"brightness",
|
|
18829
|
+
"color"
|
|
18830
|
+
]
|
|
18831
|
+
})
|
|
18832
|
+
];
|
|
18833
|
+
function actuator(input) {
|
|
18834
|
+
return {
|
|
18835
|
+
...input,
|
|
18836
|
+
family: "actuator",
|
|
18837
|
+
role: void 0,
|
|
18838
|
+
memberDeviceType: input.deviceType,
|
|
18839
|
+
outputField: "on",
|
|
18840
|
+
quantity: "none",
|
|
18841
|
+
baseUnit: void 0,
|
|
18842
|
+
aggregates: BINARY_AGGREGATES,
|
|
18843
|
+
defaultAggregate: "any",
|
|
18844
|
+
empty: "offline",
|
|
18845
|
+
accept: [{
|
|
18846
|
+
cap: "switch",
|
|
18847
|
+
fieldPath: "on"
|
|
18848
|
+
}],
|
|
18849
|
+
companions: [CHANGED]
|
|
18850
|
+
};
|
|
18851
|
+
}
|
|
18852
|
+
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
18853
|
+
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
18466
18854
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
18467
18855
|
/**
|
|
18468
18856
|
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
@@ -20289,12 +20677,12 @@ function buildDeviceAdminUrl(parts) {
|
|
|
20289
20677
|
* providers detect renames by matching `uniqueId` and patch `id`
|
|
20290
20678
|
* in-place so dispatch keeps working.
|
|
20291
20679
|
*
|
|
20292
|
-
* **
|
|
20293
|
-
*
|
|
20294
|
-
*
|
|
20295
|
-
*
|
|
20296
|
-
* (`ha-domain-mapping`)
|
|
20297
|
-
* stored after that point.
|
|
20680
|
+
* **Display unit and precision are NOT stored here.** They are a read-time
|
|
20681
|
+
* preference (D944). A polymorphic reading stores its `quantity` here once,
|
|
20682
|
+
* at adopt, and a date/time value stores `dateTimeShape` the same way —
|
|
20683
|
+
* neither is repeated on each sample. The `device_class` tag is an
|
|
20684
|
+
* HA-specific input consumed at the discovery boundary (`ha-domain-mapping`)
|
|
20685
|
+
* to derive the CamStack `DeviceRole`; it is never stored after that point.
|
|
20298
20686
|
*
|
|
20299
20687
|
* The `raw` escape hatch carries provider-specific attributes that don't
|
|
20300
20688
|
* fit the structured slots (HA `attributes` whitelist, Reolink `deviceCache`
|
|
@@ -20308,6 +20696,14 @@ var SourceInfoSchema = object({
|
|
|
20308
20696
|
/** Immutable upstream identifier when available (HA `unique_id`, … ).
|
|
20309
20697
|
* Used to detect rename when `id` changes. */
|
|
20310
20698
|
uniqueId: string().optional(),
|
|
20699
|
+
/** Quantity of a polymorphic reading, written once at adopt (D944). */
|
|
20700
|
+
quantity: ReadingQuantitySchema.optional(),
|
|
20701
|
+
/** Date / time / datetime shape of a string reading, written once at adopt. */
|
|
20702
|
+
dateTimeShape: _enum([
|
|
20703
|
+
"date",
|
|
20704
|
+
"time",
|
|
20705
|
+
"datetime"
|
|
20706
|
+
]).optional(),
|
|
20311
20707
|
/** Provider-specific extras that don't fit the structured slots.
|
|
20312
20708
|
* Whitelist what goes here per provider — do NOT dump entire HA
|
|
20313
20709
|
* attribute blobs (would explode the meta JSON column). */
|
|
@@ -20353,6 +20749,80 @@ function mergeSourceInfo(prev, patch) {
|
|
|
20353
20749
|
return next;
|
|
20354
20750
|
}
|
|
20355
20751
|
/**
|
|
20752
|
+
* Coerce common unit-of-measurement string variants to a canonical form
|
|
20753
|
+
* so the UI's display templates can rely on stable strings. Pure
|
|
20754
|
+
* passthrough for unknown units — kept conservative on purpose, the goal
|
|
20755
|
+
* is to normalise the long tail of HA `unit_of_measurement` values that
|
|
20756
|
+
* have multiple historical spellings (`'°C'` vs `'C'` vs `'celsius'`).
|
|
20757
|
+
*
|
|
20758
|
+
* Extend the map as we encounter real-world data. Returning the raw
|
|
20759
|
+
* input for unknown values is intentional: providers should never
|
|
20760
|
+
* lose information silently here.
|
|
20761
|
+
*/
|
|
20762
|
+
function normalizeUnit(raw) {
|
|
20763
|
+
if (raw == null) return void 0;
|
|
20764
|
+
const trimmed = raw.trim();
|
|
20765
|
+
if (trimmed === "") return void 0;
|
|
20766
|
+
const lower = trimmed.toLowerCase();
|
|
20767
|
+
if (lower === "°c" || lower === "c" || lower === "celsius") return "°C";
|
|
20768
|
+
if (lower === "°f" || lower === "f" || lower === "fahrenheit") return "°F";
|
|
20769
|
+
if (lower === "k" || lower === "kelvin") return "K";
|
|
20770
|
+
if (lower === "%" || lower === "percent" || lower === "percentage") return "%";
|
|
20771
|
+
if (lower === "lx" || lower === "lux") return "lx";
|
|
20772
|
+
if (lower === "hpa" || lower === "hectopascal") return "hPa";
|
|
20773
|
+
if (lower === "kpa") return "kPa";
|
|
20774
|
+
if (lower === "pa") return "Pa";
|
|
20775
|
+
if (lower === "mbar" || lower === "millibar") return "mbar";
|
|
20776
|
+
if (lower === "w" || lower === "watt") return "W";
|
|
20777
|
+
if (lower === "kw" || lower === "kilowatt") return "kW";
|
|
20778
|
+
if (lower === "wh" || lower === "watt-hour") return "Wh";
|
|
20779
|
+
if (lower === "kwh" || lower === "kilowatt-hour") return "kWh";
|
|
20780
|
+
if (lower === "v" || lower === "volt") return "V";
|
|
20781
|
+
if (lower === "a" || lower === "amp" || lower === "ampere") return "A";
|
|
20782
|
+
if (lower === "ma" || lower === "milliampere") return "mA";
|
|
20783
|
+
if (lower === "va") return "VA";
|
|
20784
|
+
if (lower === "ppm") return "ppm";
|
|
20785
|
+
if (lower === "ppb") return "ppb";
|
|
20786
|
+
if (lower === "µg/m³" || lower === "ug/m3" || lower === "µg/m3") return "µg/m³";
|
|
20787
|
+
if (lower === "km/h" || lower === "kph") return "km/h";
|
|
20788
|
+
if (lower === "mph") return "mph";
|
|
20789
|
+
if (lower === "m/s") return "m/s";
|
|
20790
|
+
if (lower === "m") return "m";
|
|
20791
|
+
if (lower === "m²" || lower === "m2") return "m²";
|
|
20792
|
+
if (lower === "mm") return "mm";
|
|
20793
|
+
if (lower === "cm") return "cm";
|
|
20794
|
+
if (lower === "km") return "km";
|
|
20795
|
+
if (lower === "in" || lower === "inch" || lower === "inches") return "in";
|
|
20796
|
+
if (lower === "ft" || lower === "foot" || lower === "feet") return "ft";
|
|
20797
|
+
if (lower === "kg") return "kg";
|
|
20798
|
+
if (lower === "g") return "g";
|
|
20799
|
+
if (lower === "l" || lower === "liter" || lower === "litre" || lower === "liters" || lower === "litres") return "L";
|
|
20800
|
+
if (lower === "ml") return "mL";
|
|
20801
|
+
if (lower === "gal" || lower === "gallon" || lower === "gallons") return "gal";
|
|
20802
|
+
if (lower === "s" || lower === "sec" || lower === "second" || lower === "seconds") return "s";
|
|
20803
|
+
if (lower === "ms") return "ms";
|
|
20804
|
+
if (lower === "min" || lower === "minute" || lower === "minutes") return "min";
|
|
20805
|
+
if (lower === "h" || lower === "hr" || lower === "hour" || lower === "hours") return "h";
|
|
20806
|
+
if (lower === "d" || lower === "day" || lower === "days") return "d";
|
|
20807
|
+
if (lower === "b" || lower === "byte" || lower === "bytes") return "B";
|
|
20808
|
+
if (lower === "kib") return "KiB";
|
|
20809
|
+
if (lower === "mib") return "MiB";
|
|
20810
|
+
if (lower === "gib") return "GiB";
|
|
20811
|
+
if (lower === "tib") return "TiB";
|
|
20812
|
+
if (lower === "kb") return "kB";
|
|
20813
|
+
if (lower === "mb") return "MB";
|
|
20814
|
+
if (lower === "gb") return "GB";
|
|
20815
|
+
if (lower === "dbm") return "dBm";
|
|
20816
|
+
if (lower === "dbfs") return "dBFS";
|
|
20817
|
+
if (lower === "inhg") return "inHg";
|
|
20818
|
+
if (lower === "mm/h") return "mm/h";
|
|
20819
|
+
if (lower === "w/m²" || lower === "w/m2") return "W/m²";
|
|
20820
|
+
if (lower === "°" || lower === "deg" || lower === "degree" || lower === "degrees") return "°";
|
|
20821
|
+
if (lower === "kbps") return "kbps";
|
|
20822
|
+
if (lower === "fps") return "fps";
|
|
20823
|
+
return trimmed;
|
|
20824
|
+
}
|
|
20825
|
+
/**
|
|
20356
20826
|
* Key under which `SourceInfo` lives inside `DeviceMeta.metadata`. The
|
|
20357
20827
|
* blob is a free-form `Record<string, unknown>` shared with hardware-
|
|
20358
20828
|
* identity fields (`manufacturer`, `model`, `firmware`, …); SourceInfo
|
|
@@ -41429,7 +41899,8 @@ method(object({
|
|
|
41429
41899
|
"enum"
|
|
41430
41900
|
]),
|
|
41431
41901
|
enumValues: array(string()).optional(),
|
|
41432
|
-
item: boolean().optional()
|
|
41902
|
+
item: boolean().optional(),
|
|
41903
|
+
quantity: ReadingQuantitySchema.optional()
|
|
41433
41904
|
})).readonly(),
|
|
41434
41905
|
itemArray: object({
|
|
41435
41906
|
path: string(),
|
|
@@ -45273,8 +45744,8 @@ var MaskPolygonShapeSchema = object({
|
|
|
45273
45744
|
/** Boolean cell grid — row-major, length === gridWidth*gridHeight. */
|
|
45274
45745
|
var MaskGridShapeSchema = object({
|
|
45275
45746
|
kind: literal("grid"),
|
|
45276
|
-
gridWidth: number(),
|
|
45277
|
-
gridHeight: number(),
|
|
45747
|
+
gridWidth: number().meta(readingMeta("none")),
|
|
45748
|
+
gridHeight: number().meta(readingMeta("none")),
|
|
45278
45749
|
cells: array(boolean())
|
|
45279
45750
|
});
|
|
45280
45751
|
discriminatedUnion("kind", [
|
|
@@ -45386,6 +45857,8 @@ var NcSystemEventKindSchema = _enum([
|
|
|
45386
45857
|
"detection-blind",
|
|
45387
45858
|
"motion-noisy",
|
|
45388
45859
|
"motion-calm",
|
|
45860
|
+
"package-llm-silent",
|
|
45861
|
+
"package-llm-answered",
|
|
45389
45862
|
"recording-stalled",
|
|
45390
45863
|
"recording-healthy",
|
|
45391
45864
|
"recording-degraded",
|
|
@@ -56477,6 +56950,28 @@ var PackageVersionInfoSchema = object({
|
|
|
56477
56950
|
deprecated: string().optional(),
|
|
56478
56951
|
distTags: array(string()).readonly()
|
|
56479
56952
|
});
|
|
56953
|
+
/**
|
|
56954
|
+
* One published document. `ok` is markdown from the registry. `missing` means
|
|
56955
|
+
* the registry answered and the file was not there. `error` is a registry or
|
|
56956
|
+
* tarball failure — never an empty string standing in for either.
|
|
56957
|
+
*/
|
|
56958
|
+
var PackageDocBodySchema = discriminatedUnion("status", [
|
|
56959
|
+
object({
|
|
56960
|
+
status: literal("ok"),
|
|
56961
|
+
markdown: string()
|
|
56962
|
+
}),
|
|
56963
|
+
object({ status: literal("missing") }),
|
|
56964
|
+
object({
|
|
56965
|
+
status: literal("error"),
|
|
56966
|
+
message: string()
|
|
56967
|
+
})
|
|
56968
|
+
]);
|
|
56969
|
+
var PackageDocsSchema = object({
|
|
56970
|
+
name: string(),
|
|
56971
|
+
version: string(),
|
|
56972
|
+
readme: PackageDocBodySchema,
|
|
56973
|
+
changelog: PackageDocBodySchema
|
|
56974
|
+
});
|
|
56480
56975
|
var SearchResultSchema = object({
|
|
56481
56976
|
name: string(),
|
|
56482
56977
|
version: string(),
|
|
@@ -56695,7 +57190,10 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
56695
57190
|
}), object({ success: literal(true) }), {
|
|
56696
57191
|
kind: "mutation",
|
|
56697
57192
|
auth: "admin"
|
|
56698
|
-
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
|
|
57193
|
+
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({
|
|
57194
|
+
name: string().min(1),
|
|
57195
|
+
version: string().min(1).optional()
|
|
57196
|
+
}), PackageDocsSchema, { auth: "admin" }), method(object({ addonId: string() }), RestartAddonResultSchema, {
|
|
56699
57197
|
kind: "mutation",
|
|
56700
57198
|
auth: "admin"
|
|
56701
57199
|
}), method(object({ packageName: string() }), object({ success: literal(true) }), {
|
|
@@ -56754,27 +57252,17 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
56754
57252
|
*/
|
|
56755
57253
|
var AirQualitySensorStatusSchema = object({
|
|
56756
57254
|
/** Carbon dioxide concentration in ppm. */
|
|
56757
|
-
co2Ppm: number().min(0).optional(),
|
|
57255
|
+
co2Ppm: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
56758
57256
|
/** Total volatile organic compounds in ppb. */
|
|
56759
|
-
vocPpb: number().min(0).optional(),
|
|
57257
|
+
vocPpb: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
56760
57258
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
56761
|
-
pm25: number().min(0).optional(),
|
|
57259
|
+
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56762
57260
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
56763
|
-
pm10: number().min(0).optional(),
|
|
57261
|
+
pm10: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56764
57262
|
/** Composite AQI value (typically 0..500). */
|
|
56765
|
-
aqi: number().optional(),
|
|
57263
|
+
aqi: number().optional().meta(readingMeta("index")),
|
|
56766
57264
|
/** Ms epoch when the slice was last updated. */
|
|
56767
|
-
lastFetchedAt: number()
|
|
56768
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
56769
|
-
* `attributes.unit_of_measurement` → 'ppm' / 'ppb' / 'µg/m³'). Each
|
|
56770
|
-
* upstream `sensor.*` entity surfaces ONE device_class, so one unit
|
|
56771
|
-
* per slice is unambiguous. */
|
|
56772
|
-
unit: string().optional(),
|
|
56773
|
-
/** Suggested decimal places for numeric display.
|
|
56774
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
56775
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
56776
|
-
* auto-formatting when absent. */
|
|
56777
|
-
precision: number().int().min(0).max(10).optional()
|
|
57265
|
+
lastFetchedAt: number()
|
|
56778
57266
|
});
|
|
56779
57267
|
var airQualitySensorCapability = {
|
|
56780
57268
|
name: "air-quality-sensor",
|
|
@@ -56811,12 +57299,12 @@ var airQualitySensorCapability = {
|
|
|
56811
57299
|
},
|
|
56812
57300
|
pm25: {
|
|
56813
57301
|
kind: "gauge",
|
|
56814
|
-
unit: "
|
|
57302
|
+
unit: "µg/m³",
|
|
56815
57303
|
deadband: 1
|
|
56816
57304
|
},
|
|
56817
57305
|
pm10: {
|
|
56818
57306
|
kind: "gauge",
|
|
56819
|
-
unit: "
|
|
57307
|
+
unit: "µg/m³",
|
|
56820
57308
|
deadband: 1
|
|
56821
57309
|
},
|
|
56822
57310
|
aqi: {
|
|
@@ -56834,22 +57322,13 @@ var airQualitySensorCapability = {
|
|
|
56834
57322
|
* entries with `device_class: illuminance`.
|
|
56835
57323
|
*/
|
|
56836
57324
|
var AmbientLightSensorStatusSchema = object({
|
|
56837
|
-
/** Current illuminance in lux (lx). */
|
|
56838
|
-
lux: number().min(0).meta({
|
|
56839
|
-
|
|
57325
|
+
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
57326
|
+
lux: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
57327
|
+
precision: 0,
|
|
56840
57328
|
label: "Illuminance"
|
|
56841
|
-
}),
|
|
57329
|
+
})),
|
|
56842
57330
|
/** Ms epoch when the slice was last updated. */
|
|
56843
|
-
lastFetchedAt: number()
|
|
56844
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
56845
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
56846
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
56847
|
-
unit: string().optional(),
|
|
56848
|
-
/** Suggested decimal places for numeric display.
|
|
56849
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
56850
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
56851
|
-
* auto-formatting when absent. */
|
|
56852
|
-
precision: number().int().min(0).max(10).optional()
|
|
57331
|
+
lastFetchedAt: number()
|
|
56853
57332
|
});
|
|
56854
57333
|
var ambientLightSensorCapability = {
|
|
56855
57334
|
name: "ambient-light-sensor",
|
|
@@ -56881,12 +57360,7 @@ var ambientLightSensorCapability = {
|
|
|
56881
57360
|
} } },
|
|
56882
57361
|
/** Clock fields: written, but excluded from the compare that decides
|
|
56883
57362
|
* whether persisting is worth a SQLite commit. */
|
|
56884
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
56885
|
-
customize: {
|
|
56886
|
-
version: 1,
|
|
56887
|
-
advanced: "derive",
|
|
56888
|
-
targets: []
|
|
56889
|
-
}
|
|
57363
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
56890
57364
|
};
|
|
56891
57365
|
/**
|
|
56892
57366
|
* Per-class audio metrics aggregated over a sliding window.
|
|
@@ -57113,10 +57587,7 @@ var BatteryStatusSchema = object({
|
|
|
57113
57587
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
57114
57588
|
* declines to band a non-finite reading, which is the correct shape.
|
|
57115
57589
|
*/
|
|
57116
|
-
percentage: number().min(0).max(100).nullable().meta({
|
|
57117
|
-
unit: "%",
|
|
57118
|
-
label: "Charge"
|
|
57119
|
-
}),
|
|
57590
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
57120
57591
|
/**
|
|
57121
57592
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
57122
57593
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -57143,7 +57614,14 @@ var BatteryStatusSchema = object({
|
|
|
57143
57614
|
*/
|
|
57144
57615
|
sleeping: boolean().meta({ label: "Sleeping" }),
|
|
57145
57616
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
57146
|
-
lastUpdated: number(),
|
|
57617
|
+
lastUpdated: number().meta(readingMeta("none")),
|
|
57618
|
+
/**
|
|
57619
|
+
* Battery voltage in volts, for a cell the firmware reports as voltage
|
|
57620
|
+
* rather than a percentage (Ecowitt capacitor packs). Absent on cameras
|
|
57621
|
+
* that only report charge. A second reading on this cap, not a second
|
|
57622
|
+
* `numeric-sensor` on the same device.
|
|
57623
|
+
*/
|
|
57624
|
+
volts: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
57147
57625
|
/**
|
|
57148
57626
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
57149
57627
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -57195,7 +57673,6 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
57195
57673
|
/** Generic Customize target for the percentage reading (D936). */
|
|
57196
57674
|
var batteryCustomize = {
|
|
57197
57675
|
version: 1,
|
|
57198
|
-
advanced: "derive",
|
|
57199
57676
|
targets: [{
|
|
57200
57677
|
id: "percentage",
|
|
57201
57678
|
cardinality: "scalar",
|
|
@@ -57204,7 +57681,7 @@ var batteryCustomize = {
|
|
|
57204
57681
|
stateField: "percentage"
|
|
57205
57682
|
},
|
|
57206
57683
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
57207
|
-
apply: "add
|
|
57684
|
+
apply: "add",
|
|
57208
57685
|
path: "percentage",
|
|
57209
57686
|
sources: [{
|
|
57210
57687
|
kind: "field",
|
|
@@ -57325,10 +57802,16 @@ onStatusChanged: { data: object({
|
|
|
57325
57802
|
/**
|
|
57326
57803
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
57327
57804
|
*/
|
|
57328
|
-
history: { fields: {
|
|
57329
|
-
|
|
57330
|
-
|
|
57331
|
-
|
|
57805
|
+
history: { fields: {
|
|
57806
|
+
percentage: {
|
|
57807
|
+
kind: "gauge",
|
|
57808
|
+
unit: "%"
|
|
57809
|
+
},
|
|
57810
|
+
volts: {
|
|
57811
|
+
kind: "gauge",
|
|
57812
|
+
unit: "V"
|
|
57813
|
+
}
|
|
57814
|
+
} },
|
|
57332
57815
|
customize: batteryCustomize,
|
|
57333
57816
|
/** Clock fields: written, but excluded from the compare that decides
|
|
57334
57817
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -57375,12 +57858,7 @@ var binaryCapability = {
|
|
|
57375
57858
|
history: { fields: { on: { kind: "boolean" } } },
|
|
57376
57859
|
/** Clock fields: written, but excluded from the compare that decides
|
|
57377
57860
|
* whether persisting is worth a SQLite commit. */
|
|
57378
|
-
volatileStateFields: ["lastChangedAt"]
|
|
57379
|
-
customize: {
|
|
57380
|
-
version: 1,
|
|
57381
|
-
advanced: "derive",
|
|
57382
|
-
targets: []
|
|
57383
|
-
}
|
|
57861
|
+
volatileStateFields: ["lastChangedAt"]
|
|
57384
57862
|
};
|
|
57385
57863
|
/**
|
|
57386
57864
|
* Dimmable-light brightness control. Co-exists with `switch` on the
|
|
@@ -57396,7 +57874,7 @@ var binaryCapability = {
|
|
|
57396
57874
|
*/
|
|
57397
57875
|
var BrightnessStatusSchema = object({
|
|
57398
57876
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
57399
|
-
percentage: number().min(0).max(100),
|
|
57877
|
+
percentage: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
57400
57878
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
57401
57879
|
lastChangedAt: number()
|
|
57402
57880
|
});
|
|
@@ -57850,21 +58328,21 @@ var ClimateControlStatusSchema = object({
|
|
|
57850
58328
|
/** Single setpoint in Celsius. Used by single-target modes
|
|
57851
58329
|
* (heat / cool / dry). Null when the device is in a range mode
|
|
57852
58330
|
* (`heat_cool`) or has no setpoint at all (`fan_only`/`off`). */
|
|
57853
|
-
target: number().nullable(),
|
|
58331
|
+
target: number().nullable().meta(readingMeta("temperature")),
|
|
57854
58332
|
/** Upper bound of the dual setpoint in Celsius. Populated only
|
|
57855
58333
|
* in `heat_cool` mode. */
|
|
57856
|
-
targetHigh: number().nullable(),
|
|
58334
|
+
targetHigh: number().nullable().meta(readingMeta("temperature")),
|
|
57857
58335
|
/** Lower bound of the dual setpoint in Celsius. Populated only
|
|
57858
58336
|
* in `heat_cool` mode. */
|
|
57859
|
-
targetLow: number().nullable(),
|
|
58337
|
+
targetLow: number().nullable().meta(readingMeta("temperature")),
|
|
57860
58338
|
/** Target relative humidity (0..100). Null when the device has no
|
|
57861
58339
|
* humidity control. */
|
|
57862
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
58340
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
57863
58341
|
/** Current measured temperature in Celsius. Read-only — pushed by
|
|
57864
58342
|
* the device's internal sensor. */
|
|
57865
|
-
currentTemp: number().nullable(),
|
|
58343
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
57866
58344
|
/** Current measured relative humidity (0..100). Read-only. */
|
|
57867
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
58345
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
57868
58346
|
/** Vertical louver swing on/off. Null when the device has no vertical
|
|
57869
58347
|
* swing surface (gate with `DeviceFeature.ClimateSwingVertical`). */
|
|
57870
58348
|
swingVertical: boolean().nullable(),
|
|
@@ -57872,11 +58350,11 @@ var ClimateControlStatusSchema = object({
|
|
|
57872
58350
|
* horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
|
|
57873
58351
|
swingHorizontal: boolean().nullable(),
|
|
57874
58352
|
/** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
57875
|
-
minTemp: number().nullable().optional(),
|
|
58353
|
+
minTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
57876
58354
|
/** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
57877
|
-
maxTemp: number().nullable().optional(),
|
|
58355
|
+
maxTemp: number().nullable().optional().meta(readingMeta("temperature")),
|
|
57878
58356
|
/** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
|
|
57879
|
-
tempStep: number().positive().nullable().optional(),
|
|
58357
|
+
tempStep: number().positive().nullable().optional().meta(readingMeta("temperature")),
|
|
57880
58358
|
/** Ms epoch when the slice was last updated (push or command). */
|
|
57881
58359
|
lastFetchedAt: number()
|
|
57882
58360
|
});
|
|
@@ -58018,17 +58496,17 @@ var climateControlCapability = {
|
|
|
58018
58496
|
* modes to expose in the picker without a slice fetch.
|
|
58019
58497
|
*/
|
|
58020
58498
|
var RgbTripletSchema = object({
|
|
58021
|
-
r: number().int().min(0).max(255),
|
|
58022
|
-
g: number().int().min(0).max(255),
|
|
58023
|
-
b: number().int().min(0).max(255)
|
|
58499
|
+
r: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
58500
|
+
g: number().int().min(0).max(255).meta(readingMeta("none")),
|
|
58501
|
+
b: number().int().min(0).max(255).meta(readingMeta("none"))
|
|
58024
58502
|
});
|
|
58025
58503
|
var HsvTripletSchema = object({
|
|
58026
58504
|
/** Hue in degrees, 0..360. */
|
|
58027
|
-
h: number().min(0).max(360),
|
|
58505
|
+
h: number().min(0).max(360).meta(readingMeta("none")),
|
|
58028
58506
|
/** Saturation as 0..100 inclusive. */
|
|
58029
|
-
s: number().min(0).max(100),
|
|
58507
|
+
s: number().min(0).max(100).meta(readingMeta("none")),
|
|
58030
58508
|
/** Value/brightness as 0..100 inclusive. */
|
|
58031
|
-
v: number().min(0).max(100)
|
|
58509
|
+
v: number().min(0).max(100).meta(readingMeta("none"))
|
|
58032
58510
|
});
|
|
58033
58511
|
var ColorInputSchema = discriminatedUnion("mode", [
|
|
58034
58512
|
object({
|
|
@@ -58057,7 +58535,7 @@ var ColorStatusSchema = object({
|
|
|
58057
58535
|
/** Populated when `mode === 'hsv'`. */
|
|
58058
58536
|
hsv: HsvTripletSchema.optional(),
|
|
58059
58537
|
/** Populated when `mode === 'mired'`. */
|
|
58060
|
-
mireds: number().int().optional(),
|
|
58538
|
+
mireds: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
58061
58539
|
/** Ms epoch of the last operator-driven change. */
|
|
58062
58540
|
lastChangedAt: number()
|
|
58063
58541
|
});
|
|
@@ -58252,18 +58730,12 @@ _enum([
|
|
|
58252
58730
|
"empty",
|
|
58253
58731
|
"full"
|
|
58254
58732
|
]);
|
|
58255
|
-
/**
|
|
58256
|
-
|
|
58257
|
-
|
|
58258
|
-
"days",
|
|
58259
|
-
"hours",
|
|
58260
|
-
"cycles"
|
|
58261
|
-
]);
|
|
58262
|
-
/** Remaining life as a COUNT, not a percentage (the PetKit desiccant reports
|
|
58263
|
-
* days). `value` null = unknown (D393). */
|
|
58733
|
+
/** Whether `remaining.value` is a duration in seconds or a dimensionless count. */
|
|
58734
|
+
var ConsumableRemainingKindSchema = _enum(["duration", "count"]);
|
|
58735
|
+
/** Remaining life. `value` null = unknown (D393). Duration is seconds. */
|
|
58264
58736
|
var ConsumableRemainingSchema = object({
|
|
58265
|
-
value: number().min(0).nullable(),
|
|
58266
|
-
|
|
58737
|
+
value: number().min(0).nullable().meta(readingMeta("device")),
|
|
58738
|
+
kind: ConsumableRemainingKindSchema
|
|
58267
58739
|
});
|
|
58268
58740
|
/** A single consumable item. Either a continuous `level` (remaining
|
|
58269
58741
|
* life %, or fill % for a `supply` / `waste` item) or a discrete `status`
|
|
@@ -58276,7 +58748,7 @@ var ConsumableItemSchema = object({
|
|
|
58276
58748
|
/** Display name. */
|
|
58277
58749
|
label: string().min(1),
|
|
58278
58750
|
/** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
|
|
58279
|
-
level: number().min(0).max(100).nullable(),
|
|
58751
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58280
58752
|
/** The provider's discrete verdict when known (binary mode). */
|
|
58281
58753
|
status: ConsumableStatusSchema.nullable(),
|
|
58282
58754
|
/** What the item is — decides whether low or full is bad. Absent = `wear`. */
|
|
@@ -58358,8 +58830,8 @@ var consumablesCustomize = {
|
|
|
58358
58830
|
transforms: [{ kind: "identity" }]
|
|
58359
58831
|
}],
|
|
58360
58832
|
literals: [{
|
|
58361
|
-
path: "remaining.
|
|
58362
|
-
values: [...
|
|
58833
|
+
path: "remaining.kind",
|
|
58834
|
+
values: [...ConsumableRemainingKindSchema.options]
|
|
58363
58835
|
}]
|
|
58364
58836
|
},
|
|
58365
58837
|
{
|
|
@@ -58488,7 +58960,6 @@ reset: method(object({
|
|
|
58488
58960
|
},
|
|
58489
58961
|
"remaining.value": {
|
|
58490
58962
|
kind: "gauge",
|
|
58491
|
-
unitFrom: "remaining.unit",
|
|
58492
58963
|
deadband: 1,
|
|
58493
58964
|
displayName: "Remaining"
|
|
58494
58965
|
}
|
|
@@ -58558,10 +59029,9 @@ var contactCapability = {
|
|
|
58558
59029
|
* (empty array for the other kinds). It lives in the slice because
|
|
58559
59030
|
* HA `select.*` entities may update their options at runtime.
|
|
58560
59031
|
*
|
|
58561
|
-
*
|
|
58562
|
-
*
|
|
58563
|
-
*
|
|
58564
|
-
* push — consistent with `options` and the sensor-unit approach.
|
|
59032
|
+
* Numeric `min` / `max` / `step` live in the slice and share the device
|
|
59033
|
+
* quantity (`SourceInfo.quantity`, D944). Display precision is a read-time
|
|
59034
|
+
* preference. A datetime control's shape is `SourceInfo.dateTimeShape`.
|
|
58565
59035
|
*/
|
|
58566
59036
|
var ControlStatusSchema = object({
|
|
58567
59037
|
kind: _enum([
|
|
@@ -58573,39 +59043,17 @@ var ControlStatusSchema = object({
|
|
|
58573
59043
|
/** Current value. Numeric kind → number; select/text/datetime → string.
|
|
58574
59044
|
* Datetime values are ISO 8601 strings (full timestamp, date-only,
|
|
58575
59045
|
* or time-only — HA accepts all three forms). */
|
|
58576
|
-
value: union([number(), string()]),
|
|
59046
|
+
value: union([number().meta(readingMeta("device")), string()]),
|
|
58577
59047
|
/** Acceptable values when `kind === 'select'`. Empty for other kinds. */
|
|
58578
59048
|
options: array(string()),
|
|
58579
59049
|
/** Ms epoch when the slice was last updated. */
|
|
58580
59050
|
lastChangedAt: number(),
|
|
58581
|
-
/**
|
|
58582
|
-
|
|
58583
|
-
|
|
58584
|
-
|
|
58585
|
-
/**
|
|
58586
|
-
|
|
58587
|
-
min: number().optional(),
|
|
58588
|
-
/** Maximum allowed value. Populated live from HA `attributes.max`.
|
|
58589
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58590
|
-
max: number().optional(),
|
|
58591
|
-
/** Step increment. Populated live from HA `attributes.step`.
|
|
58592
|
-
* Meaningful for `kind === 'numeric'`; absent for other kinds. */
|
|
58593
|
-
step: number().optional(),
|
|
58594
|
-
/** Suggested decimal places for numeric display. Populated live from HA
|
|
58595
|
-
* `attributes.suggested_display_precision`, or derived from `step` when the
|
|
58596
|
-
* HA attribute is absent. Meaningful for `kind === 'numeric'`; absent for
|
|
58597
|
-
* other kinds. Falls back to auto-formatting when absent. */
|
|
58598
|
-
precision: number().int().min(0).max(10).optional(),
|
|
58599
|
-
/** Date-picker granularity for `kind === 'datetime'`. 'date' → date-only,
|
|
58600
|
-
* 'time' → time-only, 'datetime' → full timestamp. Absent for other kinds
|
|
58601
|
-
* (UI defaults to 'datetime'). Derived live from the device's HA domain
|
|
58602
|
-
* (date.* / time.* / datetime.*) or, for input_datetime, from the entity's
|
|
58603
|
-
* `has_date`/`has_time` attributes on each push. */
|
|
58604
|
-
format: _enum([
|
|
58605
|
-
"date",
|
|
58606
|
-
"time",
|
|
58607
|
-
"datetime"
|
|
58608
|
-
]).optional()
|
|
59051
|
+
/** Minimum allowed value, in the device quantity's base unit. */
|
|
59052
|
+
min: number().optional().meta(readingMeta("device")),
|
|
59053
|
+
/** Maximum allowed value, in the device quantity's base unit. */
|
|
59054
|
+
max: number().optional().meta(readingMeta("device")),
|
|
59055
|
+
/** Step increment, in the device quantity's base unit. */
|
|
59056
|
+
step: number().optional().meta(readingMeta("device"))
|
|
58609
59057
|
});
|
|
58610
59058
|
var ControlSetValueInputSchema = discriminatedUnion("kind", [
|
|
58611
59059
|
object({
|
|
@@ -58671,9 +59119,9 @@ var CoverStatusSchema = object({
|
|
|
58671
59119
|
]),
|
|
58672
59120
|
/** 0 = fully closed, 100 = fully open. Null when the device has
|
|
58673
59121
|
* no intermediate position surface. */
|
|
58674
|
-
position: number().min(0).max(100).nullable(),
|
|
59122
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58675
59123
|
/** 0 = closed slats, 100 = open slats. Null when no tilt support. */
|
|
58676
|
-
tiltPosition: number().min(0).max(100).nullable(),
|
|
59124
|
+
tiltPosition: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
58677
59125
|
/** Ms epoch when the slice was last updated. */
|
|
58678
59126
|
lastChangedAt: number()
|
|
58679
59127
|
});
|
|
@@ -58776,9 +59224,9 @@ var NormalizedRangeSchema$1 = object({
|
|
|
58776
59224
|
var DayNightStatusSchema = object({
|
|
58777
59225
|
mode: DayNightModeSchema,
|
|
58778
59226
|
/** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
|
|
58779
|
-
sensitivity: number().optional(),
|
|
59227
|
+
sensitivity: number().optional().meta(readingMeta("none")),
|
|
58780
59228
|
/** Delay before the IR-cut filter flips, in seconds. */
|
|
58781
|
-
switchDelaySec: number().optional(),
|
|
59229
|
+
switchDelaySec: number().optional().meta(readingMeta("duration")),
|
|
58782
59230
|
lastFetchedAt: number()
|
|
58783
59231
|
});
|
|
58784
59232
|
/**
|
|
@@ -58996,11 +59444,11 @@ var DishwasherStatusSchema = object({
|
|
|
58996
59444
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
58997
59445
|
availablePrograms: array(string()),
|
|
58998
59446
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
58999
|
-
remainingSeconds: number().int().nonnegative().nullable(),
|
|
59447
|
+
remainingSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
59000
59448
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
59001
|
-
delayedStartSeconds: number().int().nonnegative().nullable(),
|
|
59449
|
+
delayedStartSeconds: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
59002
59450
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
59003
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
59451
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59004
59452
|
/** True when the door is reported open; null when the door state is not known. */
|
|
59005
59453
|
doorOpen: boolean().nullable(),
|
|
59006
59454
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -59024,7 +59472,7 @@ var DishwasherStatusSchema = object({
|
|
|
59024
59472
|
/** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
|
|
59025
59473
|
machineCareDue: boolean().nullable(),
|
|
59026
59474
|
/** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
|
|
59027
|
-
machineCareRemainingRuns: number().int().nonnegative().nullable(),
|
|
59475
|
+
machineCareRemainingRuns: number().int().nonnegative().nullable().meta(readingMeta("count")),
|
|
59028
59476
|
/** Option states: null when the appliance does not expose the option or has not said. */
|
|
59029
59477
|
halfLoad: boolean().nullable(),
|
|
59030
59478
|
hygienePlus: boolean().nullable(),
|
|
@@ -59039,7 +59487,7 @@ var DishwasherStatusSchema = object({
|
|
|
59039
59487
|
/** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
|
|
59040
59488
|
activeEvents: array(string()),
|
|
59041
59489
|
/** Numeric error code when the appliance reports one; null otherwise. */
|
|
59042
|
-
errorCode: number().nullable(),
|
|
59490
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
59043
59491
|
/** Name of the raised alert-level event while the appliance is in error; null otherwise. */
|
|
59044
59492
|
errorLabel: string().nullable(),
|
|
59045
59493
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
@@ -59270,7 +59718,7 @@ var DoorbellStatusSchema = object({
|
|
|
59270
59718
|
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
59271
59719
|
lastPressedAt: number().nullable(),
|
|
59272
59720
|
/** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
|
|
59273
|
-
pressCountSinceStart: number()
|
|
59721
|
+
pressCountSinceStart: number().meta(readingMeta("count"))
|
|
59274
59722
|
});
|
|
59275
59723
|
var DoorbellPressEventSchema = object({
|
|
59276
59724
|
deviceId: number(),
|
|
@@ -59335,17 +59783,7 @@ onPressed: {
|
|
|
59335
59783
|
history: "none",
|
|
59336
59784
|
customize: doorbellCustomize
|
|
59337
59785
|
};
|
|
59338
|
-
|
|
59339
|
-
* Enum-state sensor — a string value picked from a finite option set.
|
|
59340
|
-
* Drives HA `sensor` entries with `state_class: enum` (HVAC action
|
|
59341
|
-
* states, weather conditions, contact-source channels, mode strings).
|
|
59342
|
-
* The option set is stable across observations; it is persisted in the
|
|
59343
|
-
* device config blob (via the control slice at adoption) and is NOT
|
|
59344
|
-
* stored in `sourceInfo`.
|
|
59345
|
-
*/
|
|
59346
|
-
/** Locale-render shape for an ISO `value` carried by a `DateTimeSensor`-role
|
|
59347
|
-
* enum sensor: `date` (date-only), `time` (time-only), `datetime` (both). */
|
|
59348
|
-
var EnumSensorDateTimeFormatSchema = _enum([
|
|
59786
|
+
_enum([
|
|
59349
59787
|
"date",
|
|
59350
59788
|
"time",
|
|
59351
59789
|
"datetime"
|
|
@@ -59353,12 +59791,11 @@ var EnumSensorDateTimeFormatSchema = _enum([
|
|
|
59353
59791
|
var EnumSensorStatusSchema = object({
|
|
59354
59792
|
value: string(),
|
|
59355
59793
|
/**
|
|
59356
|
-
* Set
|
|
59357
|
-
* as
|
|
59358
|
-
*
|
|
59359
|
-
* a time-only sensor → `'time'`.
|
|
59794
|
+
* Set when `value` is a timestamp rather than an enum state, so history
|
|
59795
|
+
* does not learn ISO strings as states. The display shape (date / time /
|
|
59796
|
+
* datetime) lives on `SourceInfo.dateTimeShape`, written once at adopt.
|
|
59360
59797
|
*/
|
|
59361
|
-
|
|
59798
|
+
timestamp: boolean().optional(),
|
|
59362
59799
|
/** Ms epoch when the slice was last updated. */
|
|
59363
59800
|
lastFetchedAt: number()
|
|
59364
59801
|
});
|
|
@@ -59385,11 +59822,11 @@ var enumSensorCapability = {
|
|
|
59385
59822
|
* History (D850): the value as an `enum` series (an appliance's program or
|
|
59386
59823
|
* operation state, an HVAC action). The option set is per device, so the
|
|
59387
59824
|
* value list is LEARNED as values are first seen (bounded). A sensor with a
|
|
59388
|
-
* `
|
|
59825
|
+
* `timestamp` carries a clock, not a state, and is not recorded.
|
|
59389
59826
|
*/
|
|
59390
59827
|
history: { fields: { value: {
|
|
59391
59828
|
kind: "enum",
|
|
59392
|
-
skipWhenSet: "
|
|
59829
|
+
skipWhenSet: "timestamp"
|
|
59393
59830
|
} } },
|
|
59394
59831
|
/** Clock fields: written, but excluded from the compare that decides
|
|
59395
59832
|
* whether persisting is worth a SQLite commit. */
|
|
@@ -59409,13 +59846,13 @@ var EventFireSchema = object({
|
|
|
59409
59846
|
deviceId: number(),
|
|
59410
59847
|
eventType: string(),
|
|
59411
59848
|
data: record(string(), unknown()).nullable(),
|
|
59412
|
-
timestamp: number(),
|
|
59413
|
-
seq: number()
|
|
59849
|
+
timestamp: number().meta(readingMeta("none")),
|
|
59850
|
+
seq: number().meta(readingMeta("count"))
|
|
59414
59851
|
});
|
|
59415
59852
|
var EventEmitterStatusSchema = object({
|
|
59416
59853
|
eventTypes: array(string()),
|
|
59417
59854
|
lastEvent: EventFireSchema.nullable(),
|
|
59418
|
-
eventCountSinceStart: number()
|
|
59855
|
+
eventCountSinceStart: number().meta(readingMeta("count"))
|
|
59419
59856
|
});
|
|
59420
59857
|
var eventEmitterCapability = {
|
|
59421
59858
|
name: "event-emitter",
|
|
@@ -59828,10 +60265,10 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
59828
60265
|
var FanControlStatusSchema = object({
|
|
59829
60266
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
59830
60267
|
* speed surface (single-speed fan). */
|
|
59831
|
-
percentage: number().min(0).max(100).nullable(),
|
|
60268
|
+
percentage: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59832
60269
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
59833
60270
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
59834
|
-
percentageStep: number().positive().optional(),
|
|
60271
|
+
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
59835
60272
|
/** Active preset mode (`auto` / `quiet` / `turbo` / vendor-specific)
|
|
59836
60273
|
* — empty string when no preset is active or supported. */
|
|
59837
60274
|
preset: string(),
|
|
@@ -59952,7 +60389,7 @@ var FeatureProbeStatusSchema = object({
|
|
|
59952
60389
|
/** Camera/firmware model string. `null` when the firmware doesn't expose it. */
|
|
59953
60390
|
model: string().nullable(),
|
|
59954
60391
|
/** Channel count for NVR/Hub devices; `1` for standalone cameras; `null` pre-probe. */
|
|
59955
|
-
channelCount: number().nullable(),
|
|
60392
|
+
channelCount: number().nullable().meta(readingMeta("count")),
|
|
59956
60393
|
/**
|
|
59957
60394
|
* Ms epoch of the last SUCCESSFUL probe. `0` before the first probe
|
|
59958
60395
|
* completes — drivers' `getAccessoryChildren()` should treat zero as
|
|
@@ -60075,9 +60512,91 @@ var gasCapability = {
|
|
|
60075
60512
|
* whether persisting is worth a SQLite commit. */
|
|
60076
60513
|
volatileStateFields: ["lastChangedAt"]
|
|
60077
60514
|
};
|
|
60515
|
+
var GroupCommandsSchema = object({
|
|
60516
|
+
/** One row per member that has been commanded. A later command replaces that member's row. */
|
|
60517
|
+
results: array(object({
|
|
60518
|
+
deviceId: number().int().nonnegative(),
|
|
60519
|
+
cap: string().min(1),
|
|
60520
|
+
method: string().min(1),
|
|
60521
|
+
ok: boolean(),
|
|
60522
|
+
error: string().optional(),
|
|
60523
|
+
/** Ms epoch of the attempt. */
|
|
60524
|
+
at: number()
|
|
60525
|
+
})) });
|
|
60526
|
+
var groupCommandsCapability = {
|
|
60527
|
+
name: "group-commands",
|
|
60528
|
+
scope: "device",
|
|
60529
|
+
deviceNative: true,
|
|
60530
|
+
mode: "singleton",
|
|
60531
|
+
deviceTypes: [DeviceType.Switch, DeviceType.Light],
|
|
60532
|
+
methods: {},
|
|
60533
|
+
status: {
|
|
60534
|
+
schema: GroupCommandsSchema,
|
|
60535
|
+
kind: "push"
|
|
60536
|
+
},
|
|
60537
|
+
runtimeState: GroupCommandsSchema,
|
|
60538
|
+
/**
|
|
60539
|
+
* Runtime-state durability: **session** — the last attempt. A restored
|
|
60540
|
+
* failure would outlive the command that caused it.
|
|
60541
|
+
*
|
|
60542
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
60543
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
60544
|
+
*/
|
|
60545
|
+
durability: "session",
|
|
60546
|
+
history: "none"
|
|
60547
|
+
};
|
|
60548
|
+
/**
|
|
60549
|
+
* How many members of a helper group answered (D944). Counts are integers,
|
|
60550
|
+
* not measurements: zero included is a real count, never a stand-in reading.
|
|
60551
|
+
* The reading itself stays on the output capability. History is none — the
|
|
60552
|
+
* series that later statistics read is the output cap's own history block.
|
|
60553
|
+
*/
|
|
60554
|
+
var GroupStatusSchema = object({
|
|
60555
|
+
/** Members whose current value entered the aggregate. */
|
|
60556
|
+
included: number().int().nonnegative().meta(readingMeta("count", {
|
|
60557
|
+
precision: 0,
|
|
60558
|
+
label: "Reporting"
|
|
60559
|
+
})),
|
|
60560
|
+
/** Members left out: offline, unknown, or not a finite reading (D393). */
|
|
60561
|
+
excluded: number().int().nonnegative().meta(readingMeta("count", {
|
|
60562
|
+
precision: 0,
|
|
60563
|
+
label: "Excluded"
|
|
60564
|
+
})),
|
|
60565
|
+
/** `included` + `excluded`. */
|
|
60566
|
+
total: number().int().nonnegative().meta(readingMeta("count", {
|
|
60567
|
+
precision: 0,
|
|
60568
|
+
label: "Members"
|
|
60569
|
+
}))
|
|
60570
|
+
});
|
|
60571
|
+
var groupStatusCapability = {
|
|
60572
|
+
name: "group-status",
|
|
60573
|
+
scope: "device",
|
|
60574
|
+
deviceNative: true,
|
|
60575
|
+
mode: "singleton",
|
|
60576
|
+
deviceTypes: [
|
|
60577
|
+
DeviceType.Sensor,
|
|
60578
|
+
DeviceType.Switch,
|
|
60579
|
+
DeviceType.Light
|
|
60580
|
+
],
|
|
60581
|
+
methods: {},
|
|
60582
|
+
status: {
|
|
60583
|
+
schema: GroupStatusSchema,
|
|
60584
|
+
kind: "push"
|
|
60585
|
+
},
|
|
60586
|
+
runtimeState: GroupStatusSchema,
|
|
60587
|
+
/**
|
|
60588
|
+
* Runtime-state durability: **session** — recomputed from members on the
|
|
60589
|
+
* next source event; a restored count would outlive the readings it named.
|
|
60590
|
+
*
|
|
60591
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
60592
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
60593
|
+
*/
|
|
60594
|
+
durability: "session",
|
|
60595
|
+
history: "none"
|
|
60596
|
+
};
|
|
60078
60597
|
var HeatingValveStatusSchema = object({
|
|
60079
60598
|
/** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
|
|
60080
|
-
position: number().min(0).max(100).nullable(),
|
|
60599
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60081
60600
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
60082
60601
|
lastFetchedAt: number()
|
|
60083
60602
|
});
|
|
@@ -60120,9 +60639,9 @@ var HumidifierStatusSchema = object({
|
|
|
60120
60639
|
/** Whether the humidifier is currently on. */
|
|
60121
60640
|
on: boolean(),
|
|
60122
60641
|
/** Current measured relative humidity (0..100). Null when not reported. */
|
|
60123
|
-
currentHumidity: number().min(0).max(100).nullable(),
|
|
60642
|
+
currentHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
60124
60643
|
/** Target relative humidity (0..100). Null when no setpoint surface. */
|
|
60125
|
-
targetHumidity: number().min(0).max(100).nullable(),
|
|
60644
|
+
targetHumidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
60126
60645
|
/** Active mode (`auto` / `normal` / `baby` / vendor). Null when the
|
|
60127
60646
|
* device has no mode surface. */
|
|
60128
60647
|
mode: string().nullable(),
|
|
@@ -60132,9 +60651,9 @@ var HumidifierStatusSchema = object({
|
|
|
60132
60651
|
* `idle` / `off`). Null when the device doesn't report it. */
|
|
60133
60652
|
action: string().nullable(),
|
|
60134
60653
|
/** HA `min_humidity` attribute. Null → UI uses 0. */
|
|
60135
|
-
minHumidity: number().nullable(),
|
|
60654
|
+
minHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
60136
60655
|
/** HA `max_humidity` attribute. Null → UI uses 100. */
|
|
60137
|
-
maxHumidity: number().nullable(),
|
|
60656
|
+
maxHumidity: number().nullable().meta(readingMeta("humidity")),
|
|
60138
60657
|
/** Ms epoch when the slice was last updated. */
|
|
60139
60658
|
lastChangedAt: number()
|
|
60140
60659
|
});
|
|
@@ -60204,22 +60723,13 @@ var humidifierCapability = {
|
|
|
60204
60723
|
* `sourceInfo`.
|
|
60205
60724
|
*/
|
|
60206
60725
|
var HumiditySensorStatusSchema = object({
|
|
60207
|
-
/** Current relative humidity, 0..100. */
|
|
60208
|
-
percent: number().min(0).max(100).meta({
|
|
60209
|
-
|
|
60726
|
+
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
60727
|
+
percent: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
60728
|
+
precision: 0,
|
|
60210
60729
|
label: "Humidity"
|
|
60211
|
-
}),
|
|
60730
|
+
})),
|
|
60212
60731
|
/** Ms epoch when the slice was last updated. */
|
|
60213
|
-
lastFetchedAt: number()
|
|
60214
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
60215
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
60216
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
60217
|
-
unit: string().optional(),
|
|
60218
|
-
/** Suggested decimal places for numeric display.
|
|
60219
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
60220
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
60221
|
-
* auto-formatting when absent. */
|
|
60222
|
-
precision: number().int().min(0).max(10).optional()
|
|
60732
|
+
lastFetchedAt: number()
|
|
60223
60733
|
});
|
|
60224
60734
|
var humiditySensorCapability = {
|
|
60225
60735
|
name: "humidity-sensor",
|
|
@@ -60250,12 +60760,7 @@ var humiditySensorCapability = {
|
|
|
60250
60760
|
} } },
|
|
60251
60761
|
/** Clock fields: written, but excluded from the compare that decides
|
|
60252
60762
|
* whether persisting is worth a SQLite commit. */
|
|
60253
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
60254
|
-
customize: {
|
|
60255
|
-
version: 1,
|
|
60256
|
-
advanced: "derive",
|
|
60257
|
-
targets: []
|
|
60258
|
-
}
|
|
60763
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
60259
60764
|
};
|
|
60260
60765
|
/**
|
|
60261
60766
|
* Image display cap. Models a single still image exposed by an integration —
|
|
@@ -60276,7 +60781,7 @@ var ImageStatusSchema = object({
|
|
|
60276
60781
|
* entity exposes no `entity_picture` (yet). */
|
|
60277
60782
|
url: string().nullable(),
|
|
60278
60783
|
/** Ms epoch of the upstream last-updated timestamp. Null at cold-start. */
|
|
60279
|
-
lastUpdated: number().nullable()
|
|
60784
|
+
lastUpdated: number().nullable().meta(readingMeta("none"))
|
|
60280
60785
|
});
|
|
60281
60786
|
var imageCapability = {
|
|
60282
60787
|
name: "image",
|
|
@@ -60364,19 +60869,19 @@ var NormalizedRangeSchema = object({
|
|
|
60364
60869
|
*/
|
|
60365
60870
|
var ImageSettingsStatusSchema = object({
|
|
60366
60871
|
/** Normalized 0–100. */
|
|
60367
|
-
brightness: number().optional(),
|
|
60872
|
+
brightness: number().optional().meta(readingMeta("none")),
|
|
60368
60873
|
/** Normalized 0–100. */
|
|
60369
|
-
contrast: number().optional(),
|
|
60874
|
+
contrast: number().optional().meta(readingMeta("none")),
|
|
60370
60875
|
/** Normalized 0–100. */
|
|
60371
|
-
saturation: number().optional(),
|
|
60876
|
+
saturation: number().optional().meta(readingMeta("none")),
|
|
60372
60877
|
/** Normalized 0–100. */
|
|
60373
|
-
sharpness: number().optional(),
|
|
60878
|
+
sharpness: number().optional().meta(readingMeta("none")),
|
|
60374
60879
|
mirror: boolean().optional(),
|
|
60375
60880
|
flip: boolean().optional(),
|
|
60376
60881
|
rotate: ImageRotateSchema.optional(),
|
|
60377
60882
|
whiteBalance: WhiteBalanceModeSchema.optional(),
|
|
60378
60883
|
/** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
|
|
60379
|
-
warmth: number().optional(),
|
|
60884
|
+
warmth: number().optional().meta(readingMeta("none")),
|
|
60380
60885
|
exposureMode: ExposureModeSchema.optional(),
|
|
60381
60886
|
backlightMode: BacklightModeSchema.optional(),
|
|
60382
60887
|
lastFetchedAt: number()
|
|
@@ -60655,10 +61160,10 @@ var IntercomAbilitySchema = object({
|
|
|
60655
61160
|
/** Codecs accepted by firmware, in preference order. Driver-authored strings. */
|
|
60656
61161
|
codecs: array(string()),
|
|
60657
61162
|
/** Sample rate (Hz) the firmware expects. Reolink typical: 16000. */
|
|
60658
|
-
sampleRate: number(),
|
|
61163
|
+
sampleRate: number().meta(readingMeta("none")),
|
|
60659
61164
|
duplex: _enum(["half", "full"]),
|
|
60660
61165
|
/** Max in-flight audio backlog before drop (ms). Server uses this to bound latency. */
|
|
60661
|
-
maxBacklogMs: number()
|
|
61166
|
+
maxBacklogMs: number().meta(readingMeta("duration-ms"))
|
|
60662
61167
|
});
|
|
60663
61168
|
var IntercomStatusSchema = object({
|
|
60664
61169
|
/** True while a talk session is open. */
|
|
@@ -60885,13 +61390,13 @@ var LawnMowerControlStatusSchema = object({
|
|
|
60885
61390
|
activity: LawnMowerActivitySchema,
|
|
60886
61391
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
60887
61392
|
* reading. */
|
|
60888
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
61393
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60889
61394
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
60890
61395
|
* task is active / progress is unavailable. */
|
|
60891
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
61396
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60892
61397
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
60893
61398
|
* or null when unknown. */
|
|
60894
|
-
currentCode: number().nullable(),
|
|
61399
|
+
currentCode: number().nullable().meta(readingMeta("none")),
|
|
60895
61400
|
/** Human label for {@link currentCode}, or null when undecodable. */
|
|
60896
61401
|
currentCodeLabel: string().nullable(),
|
|
60897
61402
|
/** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
|
|
@@ -61424,7 +61929,7 @@ var MediaPlayerStatusSchema = object({
|
|
|
61424
61929
|
state: MediaPlayerStateSchema,
|
|
61425
61930
|
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
61426
61931
|
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
61427
|
-
volumeLevel: number().min(0).max(100).nullable(),
|
|
61932
|
+
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61428
61933
|
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
61429
61934
|
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
61430
61935
|
isMuted: boolean().nullable(),
|
|
@@ -61437,10 +61942,10 @@ var MediaPlayerStatusSchema = object({
|
|
|
61437
61942
|
currentMedia: MediaInfoSchema.nullable(),
|
|
61438
61943
|
/** Current playback position in ms. Null when not seekable or
|
|
61439
61944
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
61440
|
-
positionMs: number().int().nonnegative().nullable(),
|
|
61945
|
+
positionMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61441
61946
|
/** Total duration of the current media in ms. Null when unknown
|
|
61442
61947
|
* (live stream) or nothing is playing. */
|
|
61443
|
-
durationMs: number().int().nonnegative().nullable(),
|
|
61948
|
+
durationMs: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61444
61949
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
61445
61950
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
61446
61951
|
shuffle: boolean().nullable(),
|
|
@@ -61749,7 +62254,7 @@ var motionCustomize = {
|
|
|
61749
62254
|
eventName: "onMotionChanged"
|
|
61750
62255
|
},
|
|
61751
62256
|
deviceTypes: [DeviceType.Camera],
|
|
61752
|
-
apply: "
|
|
62257
|
+
apply: "add",
|
|
61753
62258
|
applyAuthority: {
|
|
61754
62259
|
kind: "camera-sensors",
|
|
61755
62260
|
timeoutField: "motionCooldownMs"
|
|
@@ -61821,7 +62326,7 @@ var MotionStatusSchema = object({
|
|
|
61821
62326
|
* it, every provider seeded it at boot, and every session left it
|
|
61822
62327
|
* `detected: true` after its detach.
|
|
61823
62328
|
*/
|
|
61824
|
-
autoClearAfterMs: number().nullable()
|
|
62329
|
+
autoClearAfterMs: number().nullable().meta(readingMeta("duration-ms"))
|
|
61825
62330
|
});
|
|
61826
62331
|
/**
|
|
61827
62332
|
* Payload of `motion.onMotionChanged` event + the corresponding bus
|
|
@@ -61858,12 +62363,18 @@ var motionCapability = {
|
|
|
61858
62363
|
methods: {
|
|
61859
62364
|
/**
|
|
61860
62365
|
* Pull the current motion state synchronously. Convenience shortcut
|
|
61861
|
-
* for consumers that don't need the full status object
|
|
61862
|
-
*
|
|
61863
|
-
* `getStatus` once the auto-injected status surface lands in every
|
|
61864
|
-
* consumer.
|
|
62366
|
+
* for consumers that don't need the full status object. A composed
|
|
62367
|
+
* group answers from its slice (D944): unknown throws, it is not off.
|
|
61865
62368
|
*/
|
|
61866
62369
|
isDetected: method(object({ deviceId: number() }), boolean()) },
|
|
62370
|
+
/**
|
|
62371
|
+
* A composed motion group serves `isDetected` from `detected` (D944).
|
|
62372
|
+
* Without this the cap stays uncomposable (`has-commands`).
|
|
62373
|
+
*/
|
|
62374
|
+
composedMethods: { isDetected: {
|
|
62375
|
+
kind: "slice",
|
|
62376
|
+
field: "detected"
|
|
62377
|
+
} },
|
|
61867
62378
|
events: {
|
|
61868
62379
|
/**
|
|
61869
62380
|
* Fires every time the runner transitions a camera between
|
|
@@ -62003,7 +62514,7 @@ var MotionZoneRegionSchema = object({
|
|
|
62003
62514
|
* the grid region(s). */
|
|
62004
62515
|
var MotionZoneStatusSchema = object({
|
|
62005
62516
|
enabled: boolean(),
|
|
62006
|
-
sensitivity: number(),
|
|
62517
|
+
sensitivity: number().meta(readingMeta("none")),
|
|
62007
62518
|
/** Grid region(s). Today exactly one `grid` shape. */
|
|
62008
62519
|
regions: array(MotionZoneRegionSchema),
|
|
62009
62520
|
lastFetchedAt: number()
|
|
@@ -62373,7 +62884,7 @@ var NavigationStatusSchema = object({
|
|
|
62373
62884
|
following: boolean(),
|
|
62374
62885
|
flash: boolean(),
|
|
62375
62886
|
lightMode: NavigationLightModeSchema,
|
|
62376
|
-
lightLevel: number().min(40).max(100),
|
|
62887
|
+
lightLevel: number().min(40).max(100).meta(readingMeta("none")),
|
|
62377
62888
|
/** Ms epoch when the slice was last updated. */
|
|
62378
62889
|
lastChangedAt: number()
|
|
62379
62890
|
});
|
|
@@ -62507,13 +63018,13 @@ var NetworkLinkStatusSchema = object({
|
|
|
62507
63018
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
62508
63019
|
* SKIP a null rather than coerce it.
|
|
62509
63020
|
*/
|
|
62510
|
-
signalPercent: number().min(0).max(100).nullable(),
|
|
63021
|
+
signalPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62511
63022
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
62512
|
-
rssiDbm: number().optional(),
|
|
63023
|
+
rssiDbm: number().optional().meta(readingMeta("signal-level")),
|
|
62513
63024
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
62514
63025
|
ssid: string().optional(),
|
|
62515
63026
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
62516
|
-
lastUpdated: number()
|
|
63027
|
+
lastUpdated: number().meta(readingMeta("none"))
|
|
62517
63028
|
});
|
|
62518
63029
|
var networkLinkCapability = {
|
|
62519
63030
|
name: "network-link",
|
|
@@ -62898,7 +63409,7 @@ var NotifierStatusSchema = object({
|
|
|
62898
63409
|
lastError: string().nullable(),
|
|
62899
63410
|
/** Number of deliveries currently buffered server-side (rate-limited
|
|
62900
63411
|
* notifiers may queue). 0 when the provider sends synchronously. */
|
|
62901
|
-
queueDepth: number().int().nonnegative(),
|
|
63412
|
+
queueDepth: number().int().nonnegative().meta(readingMeta("count")),
|
|
62902
63413
|
/** Per-feature capability matrix — authoritative source for the
|
|
62903
63414
|
* compose-form field gating. */
|
|
62904
63415
|
supports: NotifierSupportsSchema
|
|
@@ -62990,11 +63501,9 @@ onSent: { data: object({
|
|
|
62990
63501
|
* cap fits (HA `sensor` whose `device_class` we don't have a typed cap
|
|
62991
63502
|
* for: water flow, distance, weight, frequency, signal strength, …).
|
|
62992
63503
|
*
|
|
62993
|
-
* The
|
|
62994
|
-
*
|
|
62995
|
-
*
|
|
62996
|
-
* The UI reads them directly from the slice — they are the single source
|
|
62997
|
-
* of truth for generic numeric rendering and do NOT live on `sourceInfo`.
|
|
63504
|
+
* The quantity is not on this slice. It is chosen when the device is
|
|
63505
|
+
* adopted and stored once on `SourceInfo.quantity` (D944). `value` is in
|
|
63506
|
+
* that quantity's base unit. `null` is a refused sample (D393).
|
|
62998
63507
|
*
|
|
62999
63508
|
* Prefer the typed alternatives (`temperature-sensor`, `humidity-sensor`,
|
|
63000
63509
|
* `ambient-light-sensor`, `pressure-sensor`, `power-meter`,
|
|
@@ -63003,15 +63512,8 @@ onSent: { data: object({
|
|
|
63003
63512
|
* numeric values land here.
|
|
63004
63513
|
*/
|
|
63005
63514
|
var NumericSensorStatusSchema = object({
|
|
63006
|
-
|
|
63007
|
-
|
|
63008
|
-
* Populated live from the upstream source on each state push.
|
|
63009
|
-
* Absent for unitless sensors. */
|
|
63010
|
-
unit: string().optional(),
|
|
63011
|
-
/** Suggested decimal places for numeric display.
|
|
63012
|
-
* Populated live from the upstream source when provided.
|
|
63013
|
-
* Falls back to auto-formatting when absent. */
|
|
63014
|
-
precision: number().int().min(0).max(10).optional(),
|
|
63515
|
+
/** Reading in the device quantity's base unit. `null` when refused (D393). */
|
|
63516
|
+
value: number().nullable().meta(readingMeta("device")),
|
|
63015
63517
|
/** Ms epoch when the slice was last updated. */
|
|
63016
63518
|
lastFetchedAt: number()
|
|
63017
63519
|
});
|
|
@@ -63035,11 +63537,11 @@ var numericSensorCapability = {
|
|
|
63035
63537
|
*/
|
|
63036
63538
|
durability: "restored",
|
|
63037
63539
|
/**
|
|
63038
|
-
* History (D850): recorded series. The unit is
|
|
63540
|
+
* History (D850): recorded series. The unit is the device quantity's base,
|
|
63541
|
+
* resolved when the series is read — the cap has no single unit.
|
|
63039
63542
|
*/
|
|
63040
63543
|
history: { fields: { value: {
|
|
63041
63544
|
kind: "gauge",
|
|
63042
|
-
unitFrom: "unit",
|
|
63043
63545
|
minIntervalMs: 3e4
|
|
63044
63546
|
} } },
|
|
63045
63547
|
/** Clock fields: written, but excluded from the compare that decides
|
|
@@ -63331,23 +63833,26 @@ var PetFeederStatusSchema = object({
|
|
|
63331
63833
|
/** Food currently in the bowl (grams). Null when the device has not
|
|
63332
63834
|
* reported a reading yet. On dual-hopper models this is the combined
|
|
63333
63835
|
* bowl reading; per-hopper levels live in `food1`/`food2`. */
|
|
63334
|
-
foodLevel: number().nullable(),
|
|
63836
|
+
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
63335
63837
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
63336
63838
|
* single-hopper models. */
|
|
63337
|
-
food1: number().nullable(),
|
|
63839
|
+
food1: number().nullable().meta(readingMeta("none")),
|
|
63338
63840
|
/** Hopper-2 food level (grams) on dual-hopper feeders; null on
|
|
63339
63841
|
* single-hopper models. */
|
|
63340
|
-
food2: number().nullable(),
|
|
63842
|
+
food2: number().nullable().meta(readingMeta("none")),
|
|
63341
63843
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
63342
63844
|
* (`device_class: problem`, on = low). True when the bowl is empty /
|
|
63343
63845
|
* below the feeder's low threshold. */
|
|
63344
63846
|
lowFood: boolean(),
|
|
63345
63847
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
63346
63848
|
* device has no battery reading. */
|
|
63347
|
-
batteryPower: number().min(0).max(100).nullable(),
|
|
63348
|
-
/**
|
|
63349
|
-
*
|
|
63350
|
-
|
|
63849
|
+
batteryPower: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63850
|
+
/**
|
|
63851
|
+
* Desiccant life remaining, stored in seconds (duration base, D944).
|
|
63852
|
+
* Providers convert from the days the feeder reports. Null when the
|
|
63853
|
+
* model has no desiccant sensor.
|
|
63854
|
+
*/
|
|
63855
|
+
desiccantLeftDays: number().nullable().meta(readingMeta("duration")),
|
|
63351
63856
|
/** True while a feed is in progress. */
|
|
63352
63857
|
feeding: boolean(),
|
|
63353
63858
|
/** Decoded connectivity / power status (HA petkit device-status enum).
|
|
@@ -63358,7 +63863,7 @@ var PetFeederStatusSchema = object({
|
|
|
63358
63863
|
* with `errorCode` for consumers that want the raw integer. */
|
|
63359
63864
|
error: string().nullable(),
|
|
63360
63865
|
/** Raw device error code (0 / null = no error). */
|
|
63361
|
-
errorCode: number().nullable(),
|
|
63866
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
63362
63867
|
/** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
|
|
63363
63868
|
isDualHopper: boolean(),
|
|
63364
63869
|
/** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
|
|
@@ -63368,7 +63873,7 @@ var PetFeederStatusSchema = object({
|
|
|
63368
63873
|
/** Play a chime when dispensing. */
|
|
63369
63874
|
feedSound: boolean(),
|
|
63370
63875
|
/** Speaker / prompt volume level (device-scaled integer). */
|
|
63371
|
-
volume: number(),
|
|
63876
|
+
volume: number().meta(readingMeta("none")),
|
|
63372
63877
|
/** Ms epoch when the slice was last refreshed from the cloud. */
|
|
63373
63878
|
lastFetchedAt: number()
|
|
63374
63879
|
});
|
|
@@ -63487,7 +63992,7 @@ var petFeederCapability = {
|
|
|
63487
63992
|
},
|
|
63488
63993
|
desiccantLeftDays: {
|
|
63489
63994
|
kind: "gauge",
|
|
63490
|
-
unit: "
|
|
63995
|
+
unit: "s"
|
|
63491
63996
|
}
|
|
63492
63997
|
} }
|
|
63493
63998
|
};
|
|
@@ -63888,36 +64393,19 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
63888
64393
|
*/
|
|
63889
64394
|
var PowerMeterStatusSchema = object({
|
|
63890
64395
|
/** Instantaneous power draw in watts. */
|
|
63891
|
-
watts: number().optional().meta({
|
|
63892
|
-
unit: "W",
|
|
63893
|
-
label: "Power"
|
|
63894
|
-
}),
|
|
64396
|
+
watts: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
63895
64397
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
63896
|
-
kwhTotal: number().optional().meta({
|
|
63897
|
-
unit: "kWh",
|
|
63898
|
-
label: "Energy"
|
|
63899
|
-
}),
|
|
64398
|
+
kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
63900
64399
|
/** Voltage in volts. */
|
|
63901
|
-
volts: number().optional(),
|
|
64400
|
+
volts: number().optional().meta(readingMeta("voltage")),
|
|
63902
64401
|
/** Current in amperes. */
|
|
63903
|
-
amps: number().optional(),
|
|
63904
|
-
/** Apparent power in volt-amperes
|
|
63905
|
-
va: number().optional(),
|
|
64402
|
+
amps: number().optional().meta(readingMeta("current")),
|
|
64403
|
+
/** Apparent power in volt-amperes. */
|
|
64404
|
+
va: number().optional().meta(readingMeta("apparent-power")),
|
|
63906
64405
|
/** Power factor (0..1) if reported. */
|
|
63907
|
-
powerFactor: number().min(0).max(1).optional(),
|
|
64406
|
+
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
63908
64407
|
/** Ms epoch when the slice was last updated. */
|
|
63909
|
-
lastFetchedAt: number()
|
|
63910
|
-
/** Live display unit of the single metric this slice carries (e.g. HA
|
|
63911
|
-
* `attributes.unit_of_measurement` → 'V' / 'A' / 'W' / 'kW' / 'kWh').
|
|
63912
|
-
* Each upstream `sensor.*` entity surfaces ONE device_class, so one
|
|
63913
|
-
* unit per slice is unambiguous. The UI prefers this over the role's
|
|
63914
|
-
* canonical unit so a 'kW' / 'Wh' feed renders verbatim. */
|
|
63915
|
-
unit: string().optional(),
|
|
63916
|
-
/** Suggested decimal places for numeric display.
|
|
63917
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
63918
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
63919
|
-
* auto-formatting when absent. */
|
|
63920
|
-
precision: number().int().min(0).max(10).optional()
|
|
64408
|
+
lastFetchedAt: number()
|
|
63921
64409
|
});
|
|
63922
64410
|
var powerMeterCapability = {
|
|
63923
64411
|
name: "power-meter",
|
|
@@ -63981,12 +64469,7 @@ var powerMeterCapability = {
|
|
|
63981
64469
|
} },
|
|
63982
64470
|
/** Clock fields: written, but excluded from the compare that decides
|
|
63983
64471
|
* whether persisting is worth a SQLite commit. */
|
|
63984
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
63985
|
-
customize: {
|
|
63986
|
-
version: 1,
|
|
63987
|
-
advanced: "derive",
|
|
63988
|
-
targets: []
|
|
63989
|
-
}
|
|
64472
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
63990
64473
|
};
|
|
63991
64474
|
/**
|
|
63992
64475
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
@@ -64006,11 +64489,11 @@ var powerMeterCapability = {
|
|
|
64006
64489
|
*/
|
|
64007
64490
|
var GpsLocationSchema = object({
|
|
64008
64491
|
/** Latitude in decimal degrees, -90..90. */
|
|
64009
|
-
latitude: number().min(-90).max(90),
|
|
64492
|
+
latitude: number().min(-90).max(90).meta(readingMeta("angle")),
|
|
64010
64493
|
/** Longitude in decimal degrees, -180..180. */
|
|
64011
|
-
longitude: number().min(-180).max(180),
|
|
64494
|
+
longitude: number().min(-180).max(180).meta(readingMeta("angle")),
|
|
64012
64495
|
/** Reported accuracy in meters (lower = better). */
|
|
64013
|
-
accuracyMeters: number().nonnegative()
|
|
64496
|
+
accuracyMeters: number().nonnegative().meta(readingMeta("length"))
|
|
64014
64497
|
});
|
|
64015
64498
|
var PresenceStatusSchema = object({
|
|
64016
64499
|
/** `home` / `not_home` / any user-defined zone name. */
|
|
@@ -64022,7 +64505,7 @@ var PresenceStatusSchema = object({
|
|
|
64022
64505
|
gps: GpsLocationSchema.nullable(),
|
|
64023
64506
|
/** Optional battery level of the tracking device (0..100). Null
|
|
64024
64507
|
* when not reported. */
|
|
64025
|
-
batteryPercent: number().min(0).max(100).nullable(),
|
|
64508
|
+
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
64026
64509
|
/** Ms epoch when the slice was last updated. */
|
|
64027
64510
|
lastChangedAt: number()
|
|
64028
64511
|
});
|
|
@@ -64072,19 +64555,10 @@ var presenceCapability = {
|
|
|
64072
64555
|
* `sourceInfo`.
|
|
64073
64556
|
*/
|
|
64074
64557
|
var PressureSensorStatusSchema = object({
|
|
64075
|
-
/** Current pressure in hPa. */
|
|
64076
|
-
hpa: number(),
|
|
64558
|
+
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
64559
|
+
hpa: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
64077
64560
|
/** Ms epoch when the slice was last updated. */
|
|
64078
|
-
lastFetchedAt: number()
|
|
64079
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
64080
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
64081
|
-
* role's canonical unit. Absent → fall back to the canonical unit. */
|
|
64082
|
-
unit: string().optional(),
|
|
64083
|
-
/** Suggested decimal places for numeric display.
|
|
64084
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
64085
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
64086
|
-
* auto-formatting when absent. */
|
|
64087
|
-
precision: number().int().min(0).max(10).optional()
|
|
64561
|
+
lastFetchedAt: number()
|
|
64088
64562
|
});
|
|
64089
64563
|
var pressureSensorCapability = {
|
|
64090
64564
|
name: "pressure-sensor",
|
|
@@ -64498,8 +64972,8 @@ DeviceType.Camera, method(PtzMoveCommandSchema.extend({ deviceId: number() }), _
|
|
|
64498
64972
|
}), _void(), { kind: "mutation" });
|
|
64499
64973
|
var PtzAutotrackSettingsSchema = object({
|
|
64500
64974
|
targetType: string().describe("Vendor target string (people/vehicle/pet); empty = camera default"),
|
|
64501
|
-
stopDelaySeconds: number().int().min(0).max(300),
|
|
64502
|
-
disappearDelaySeconds: number().int().min(0).max(300)
|
|
64975
|
+
stopDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration")),
|
|
64976
|
+
disappearDelaySeconds: number().int().min(0).max(300).meta(readingMeta("duration"))
|
|
64503
64977
|
});
|
|
64504
64978
|
/**
|
|
64505
64979
|
* One option the camera advertises as a valid `targetType`. Drivers
|
|
@@ -64653,7 +65127,7 @@ var RunningStateStatusSchema = object({
|
|
|
64653
65127
|
/** null = unknown — never coerced to false (D393). */
|
|
64654
65128
|
running: boolean().nullable(),
|
|
64655
65129
|
/** Epoch ms the current boolean began; null while `running` is null. */
|
|
64656
|
-
since: number().nullable(),
|
|
65130
|
+
since: number().nullable().meta(readingMeta("none")),
|
|
64657
65131
|
source: RunningStateSourceSchema.meta({ label: "Source" }),
|
|
64658
65132
|
phase: string().nullable(),
|
|
64659
65133
|
/** null until a reading has been published (D393). */
|
|
@@ -64668,7 +65142,6 @@ var RUNNING_DEVICE_TYPES = Object.values(DeviceType);
|
|
|
64668
65142
|
/** Generic Customize target for the running boolean (D936). */
|
|
64669
65143
|
var runningCustomize = {
|
|
64670
65144
|
version: 1,
|
|
64671
|
-
advanced: "derive",
|
|
64672
65145
|
targets: [{
|
|
64673
65146
|
id: "running",
|
|
64674
65147
|
cardinality: "scalar",
|
|
@@ -64688,7 +65161,7 @@ var runningCustomize = {
|
|
|
64688
65161
|
DeviceType.Thermostat,
|
|
64689
65162
|
DeviceType.PetFeeder
|
|
64690
65163
|
],
|
|
64691
|
-
apply: "add
|
|
65164
|
+
apply: "add",
|
|
64692
65165
|
path: "running",
|
|
64693
65166
|
sources: [
|
|
64694
65167
|
{
|
|
@@ -65952,9 +66425,9 @@ var RecordTriggerSchema = _enum([
|
|
|
65952
66425
|
*/
|
|
65953
66426
|
var RecordWindowSchema = object({
|
|
65954
66427
|
trigger: RecordTriggerSchema,
|
|
65955
|
-
day: number().int().min(0).max(6),
|
|
65956
|
-
startMinute: number().int().min(0).max(1439),
|
|
65957
|
-
endMinute: number().int().min(1).max(1440)
|
|
66428
|
+
day: number().int().min(0).max(6).meta(readingMeta("none")),
|
|
66429
|
+
startMinute: number().int().min(0).max(1439).meta(readingMeta("none")),
|
|
66430
|
+
endMinute: number().int().min(1).max(1440).meta(readingMeta("none"))
|
|
65958
66431
|
});
|
|
65959
66432
|
/** Status of one physical volume, as the camera itself describes it. */
|
|
65960
66433
|
var OnboardStorageVolumeSchema = object({
|
|
@@ -66030,10 +66503,10 @@ var RecordingOnboardStatusSchema = object({
|
|
|
66030
66503
|
/** Master "record to the card at all" switch. */
|
|
66031
66504
|
enabled: boolean().nullable(),
|
|
66032
66505
|
overwriteWhenFull: boolean().nullable(),
|
|
66033
|
-
preRecordSec: number().nullable(),
|
|
66034
|
-
postRecordSec: number().nullable(),
|
|
66506
|
+
preRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
66507
|
+
postRecordSec: number().nullable().meta(readingMeta("duration")),
|
|
66035
66508
|
/** Length of one recorded file, in minutes. */
|
|
66036
|
-
segmentMinutes: number().nullable(),
|
|
66509
|
+
segmentMinutes: number().nullable().meta(readingMeta("none")),
|
|
66037
66510
|
/** The primary track's weekly windows, flattened. */
|
|
66038
66511
|
windows: array(RecordWindowSchema),
|
|
66039
66512
|
/**
|
|
@@ -66049,7 +66522,7 @@ var RecordingOnboardStatusSchema = object({
|
|
|
66049
66522
|
* provider reporting a non-zero count also reports the schedule as not
|
|
66050
66523
|
* writable.
|
|
66051
66524
|
*/
|
|
66052
|
-
unreadableWindows: number(),
|
|
66525
|
+
unreadableWindows: number().meta(readingMeta("count")),
|
|
66053
66526
|
/**
|
|
66054
66527
|
* The camera is scheduled to record and has NO usable storage.
|
|
66055
66528
|
*
|
|
@@ -66894,7 +67367,7 @@ var CamStreamDescriptorSchema = object({
|
|
|
66894
67367
|
url: string().optional(),
|
|
66895
67368
|
codec: string().optional(),
|
|
66896
67369
|
resolution: CamStreamResolutionSchema.optional(),
|
|
66897
|
-
fps: number().positive().optional(),
|
|
67370
|
+
fps: number().positive().optional().meta(readingMeta("rate")),
|
|
66898
67371
|
label: string().optional(),
|
|
66899
67372
|
/** Device-level features (e.g. `battery-operated`) — drives broker policy. */
|
|
66900
67373
|
deviceFeatures: array(string()).optional(),
|
|
@@ -66981,18 +67454,18 @@ var StreamProfileSchema = _enum([
|
|
|
66981
67454
|
]);
|
|
66982
67455
|
/** The current encoding config of one stream profile. */
|
|
66983
67456
|
var StreamProfileConfigSchema = object({
|
|
66984
|
-
width: number(),
|
|
66985
|
-
height: number(),
|
|
67457
|
+
width: number().meta(readingMeta("none")),
|
|
67458
|
+
height: number().meta(readingMeta("none")),
|
|
66986
67459
|
codec: _enum(["h264", "h265"]),
|
|
66987
|
-
framerate: number(),
|
|
66988
|
-
bitrate: number(),
|
|
67460
|
+
framerate: number().meta(readingMeta("rate")),
|
|
67461
|
+
bitrate: number().meta(readingMeta("bitrate")),
|
|
66989
67462
|
bitrateMode: _enum(["vbr", "cbr"]).optional(),
|
|
66990
67463
|
encoderProfile: _enum([
|
|
66991
67464
|
"high",
|
|
66992
67465
|
"main",
|
|
66993
67466
|
"baseline"
|
|
66994
67467
|
]).optional(),
|
|
66995
|
-
gop: number().optional(),
|
|
67468
|
+
gop: number().optional().meta(readingMeta("none")),
|
|
66996
67469
|
/**
|
|
66997
67470
|
* Whether THIS profile currently carries an audio track. READ-ONLY here.
|
|
66998
67471
|
*
|
|
@@ -67213,12 +67686,7 @@ var switchCapability = {
|
|
|
67213
67686
|
label: "On",
|
|
67214
67687
|
kind: "boolean"
|
|
67215
67688
|
}
|
|
67216
|
-
}] }
|
|
67217
|
-
customize: {
|
|
67218
|
-
version: 1,
|
|
67219
|
-
advanced: "derive",
|
|
67220
|
-
targets: []
|
|
67221
|
-
}
|
|
67689
|
+
}] }
|
|
67222
67690
|
};
|
|
67223
67691
|
/** C0 controls, DEL and the C1 range: none of them belongs in a name. */
|
|
67224
67692
|
function hasControlCharacter(value) {
|
|
@@ -67801,35 +68269,21 @@ var tamperCapability = {
|
|
|
67801
68269
|
* entries with `device_class: temperature` and any future native
|
|
67802
68270
|
* thermometer.
|
|
67803
68271
|
*
|
|
67804
|
-
*
|
|
67805
|
-
*
|
|
67806
|
-
* (climate-control's `currentTemp`, energy analytics) can compose
|
|
67807
|
-
* without per-source unit fixups. The canonical display unit (`°C`) is
|
|
67808
|
-
* a descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
67809
|
-
* `sourceInfo`.
|
|
68272
|
+
* The number is Celsius (D944). Providers convert on ingest. A failed
|
|
68273
|
+
* conversion is `null`, never a guess. Display unit is a read-time preference.
|
|
67810
68274
|
*
|
|
67811
68275
|
* Status `lastFetchedAt` lets staleness-aware consumers detect a
|
|
67812
68276
|
* frozen feed (provider hung) distinct from a "temperature hasn't
|
|
67813
68277
|
* changed" steady state.
|
|
67814
68278
|
*/
|
|
67815
68279
|
var TemperatureSensorStatusSchema = object({
|
|
67816
|
-
/** Current temperature in Celsius. */
|
|
67817
|
-
celsius: number().meta({
|
|
67818
|
-
|
|
68280
|
+
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
68281
|
+
celsius: number().nullable().meta(readingMeta("temperature", {
|
|
68282
|
+
precision: 1,
|
|
67819
68283
|
label: "Temperature"
|
|
67820
|
-
}),
|
|
68284
|
+
})),
|
|
67821
68285
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
67822
|
-
lastFetchedAt: number()
|
|
67823
|
-
/** Live display unit from the upstream source (e.g. HA
|
|
67824
|
-
* `attributes.unit_of_measurement`). The UI prefers this over the
|
|
67825
|
-
* role's canonical unit, so a Fahrenheit feed renders '°F' not '°C'.
|
|
67826
|
-
* Absent → the UI falls back to the role's canonical unit. */
|
|
67827
|
-
unit: string().optional(),
|
|
67828
|
-
/** Suggested decimal places for numeric display.
|
|
67829
|
-
* Populated live from the upstream source when provided (e.g. HA
|
|
67830
|
-
* `attributes.suggested_display_precision`). Falls back to
|
|
67831
|
-
* auto-formatting when absent. */
|
|
67832
|
-
precision: number().int().min(0).max(10).optional()
|
|
68286
|
+
lastFetchedAt: number()
|
|
67833
68287
|
});
|
|
67834
68288
|
var temperatureSensorCapability = {
|
|
67835
68289
|
name: "temperature-sensor",
|
|
@@ -67860,12 +68314,7 @@ var temperatureSensorCapability = {
|
|
|
67860
68314
|
} } },
|
|
67861
68315
|
/** Clock fields: written, but excluded from the compare that decides
|
|
67862
68316
|
* whether persisting is worth a SQLite commit. */
|
|
67863
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
67864
|
-
customize: {
|
|
67865
|
-
version: 1,
|
|
67866
|
-
advanced: "derive",
|
|
67867
|
-
targets: []
|
|
67868
|
-
}
|
|
68317
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
67869
68318
|
};
|
|
67870
68319
|
/**
|
|
67871
68320
|
* toast — system-scoped singleton capability that streams toast
|
|
@@ -68148,16 +68597,16 @@ var VacuumControlStatusSchema = object({
|
|
|
68148
68597
|
]),
|
|
68149
68598
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
68150
68599
|
* reading. */
|
|
68151
|
-
batteryLevel: number().min(0).max(100).nullable(),
|
|
68600
|
+
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68152
68601
|
/** Current fan-speed token (provider-verbatim). Null when unknown or
|
|
68153
68602
|
* the vacuum has no speed control. */
|
|
68154
68603
|
fanSpeed: string().nullable(),
|
|
68155
68604
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
68156
68605
|
availableFanSpeeds: array(string()),
|
|
68157
68606
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
68158
|
-
progressPercent: number().min(0).max(100).nullable(),
|
|
68607
|
+
progressPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68159
68608
|
/** Current error code (0 / null = no error). */
|
|
68160
|
-
errorCode: number().nullable(),
|
|
68609
|
+
errorCode: number().nullable().meta(readingMeta("none")),
|
|
68161
68610
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
68162
68611
|
errorLabel: string().nullable(),
|
|
68163
68612
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -68235,7 +68684,7 @@ var ValveStatusSchema = object({
|
|
|
68235
68684
|
]),
|
|
68236
68685
|
/** 0 = fully closed, 100 = fully open. Null when the device has no
|
|
68237
68686
|
* intermediate position surface. */
|
|
68238
|
-
position: number().min(0).max(100).nullable(),
|
|
68687
|
+
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68239
68688
|
/** Ms epoch when the slice was last updated. */
|
|
68240
68689
|
lastChangedAt: number()
|
|
68241
68690
|
});
|
|
@@ -68343,9 +68792,9 @@ var vibrationCapability = {
|
|
|
68343
68792
|
*/
|
|
68344
68793
|
var WaterHeaterStatusSchema = object({
|
|
68345
68794
|
/** Current measured temperature. Null when not reported. */
|
|
68346
|
-
currentTemp: number().nullable(),
|
|
68795
|
+
currentTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68347
68796
|
/** Target temperature setpoint. Null when no setpoint surface. */
|
|
68348
|
-
targetTemp: number().nullable(),
|
|
68797
|
+
targetTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68349
68798
|
/** Active operation mode = HA `state` (`eco` / `electric` / `gas` /
|
|
68350
68799
|
* `heat_pump` / `high_demand` / `performance` / `off`). Null when the
|
|
68351
68800
|
* device reports an unknown state. */
|
|
@@ -68356,9 +68805,9 @@ var WaterHeaterStatusSchema = object({
|
|
|
68356
68805
|
* has no away surface. */
|
|
68357
68806
|
away: boolean().nullable(),
|
|
68358
68807
|
/** HA `min_temp` attribute. Null when not reported. */
|
|
68359
|
-
minTemp: number().nullable(),
|
|
68808
|
+
minTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68360
68809
|
/** HA `max_temp` attribute. Null when not reported. */
|
|
68361
|
-
maxTemp: number().nullable(),
|
|
68810
|
+
maxTemp: number().nullable().meta(readingMeta("temperature")),
|
|
68362
68811
|
/** Ms epoch when the slice was last updated. */
|
|
68363
68812
|
lastChangedAt: number()
|
|
68364
68813
|
});
|
|
@@ -68443,25 +68892,18 @@ var waterHeaterCapability = {
|
|
|
68443
68892
|
* Forecast deferred — current conditions only for v1.
|
|
68444
68893
|
*/
|
|
68445
68894
|
var WeatherStatusSchema = object({
|
|
68446
|
-
/** Verbatim
|
|
68447
|
-
* when no condition has been reported yet. */
|
|
68895
|
+
/** Verbatim condition state (`sunny`, `cloudy`, `rainy`, …). Null when none yet. */
|
|
68448
68896
|
condition: string().nullable(),
|
|
68449
|
-
/** Current temperature in
|
|
68450
|
-
temperature: number().nullable(),
|
|
68451
|
-
/** Temperature unit string (e.g. `°C` / `°F`). Null when not provided. */
|
|
68452
|
-
temperatureUnit: string().nullable(),
|
|
68897
|
+
/** Current temperature in Celsius. Null when not provided or refused. */
|
|
68898
|
+
temperature: number().nullable().meta(readingMeta("temperature", { precision: 1 })),
|
|
68453
68899
|
/** Relative humidity (0..100). Null when not provided. */
|
|
68454
|
-
humidity: number().min(0).max(100).nullable(),
|
|
68455
|
-
/** Barometric pressure in
|
|
68456
|
-
pressure: number().nullable(),
|
|
68457
|
-
/**
|
|
68458
|
-
|
|
68459
|
-
/** Wind speed in the reported unit. Null when not provided. */
|
|
68460
|
-
windSpeed: number().nullable(),
|
|
68461
|
-
/** Wind-speed unit string (e.g. `km/h` / `mph`). Null when not provided. */
|
|
68462
|
-
windSpeedUnit: string().nullable(),
|
|
68900
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity")),
|
|
68901
|
+
/** Barometric pressure in hPa. Null when not provided or refused. */
|
|
68902
|
+
pressure: number().nullable().meta(readingMeta("pressure")),
|
|
68903
|
+
/** Wind speed in metres per second. Null when not provided or refused. */
|
|
68904
|
+
windSpeed: number().nullable().meta(readingMeta("speed")),
|
|
68463
68905
|
/** Wind bearing in degrees (0..360, meteorological). Null when not provided. */
|
|
68464
|
-
windBearing: number().nullable(),
|
|
68906
|
+
windBearing: number().nullable().meta(readingMeta("angle")),
|
|
68465
68907
|
/** Ms epoch when the slice was last updated. */
|
|
68466
68908
|
lastFetchedAt: number()
|
|
68467
68909
|
});
|
|
@@ -68489,12 +68931,12 @@ var weatherCapability = {
|
|
|
68489
68931
|
*/
|
|
68490
68932
|
durability: "session",
|
|
68491
68933
|
/**
|
|
68492
|
-
* History (D850): recorded series.
|
|
68934
|
+
* History (D850): recorded series. Numbers are the quantity base (D944).
|
|
68493
68935
|
*/
|
|
68494
68936
|
history: { fields: {
|
|
68495
68937
|
temperature: {
|
|
68496
68938
|
kind: "gauge",
|
|
68497
|
-
|
|
68939
|
+
unit: "°C",
|
|
68498
68940
|
deadband: .2
|
|
68499
68941
|
},
|
|
68500
68942
|
humidity: {
|
|
@@ -68504,12 +68946,12 @@ var weatherCapability = {
|
|
|
68504
68946
|
},
|
|
68505
68947
|
pressure: {
|
|
68506
68948
|
kind: "gauge",
|
|
68507
|
-
|
|
68949
|
+
unit: "hPa",
|
|
68508
68950
|
deadband: .5
|
|
68509
68951
|
},
|
|
68510
68952
|
windSpeed: {
|
|
68511
68953
|
kind: "gauge",
|
|
68512
|
-
|
|
68954
|
+
unit: "m/s",
|
|
68513
68955
|
deadband: .5
|
|
68514
68956
|
}
|
|
68515
68957
|
} }
|
|
@@ -69410,6 +69852,389 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
69410
69852
|
kind: "mutation",
|
|
69411
69853
|
auth: "admin"
|
|
69412
69854
|
});
|
|
69855
|
+
/**
|
|
69856
|
+
* Frozen unit → spec table. `•` in the doc marks each dimension's canonical
|
|
69857
|
+
* unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
|
|
69858
|
+
* `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
|
|
69859
|
+
* decimals). K derives from `°C = K − 273.15`.
|
|
69860
|
+
*/
|
|
69861
|
+
var UNIT_TABLE = {
|
|
69862
|
+
"°C": {
|
|
69863
|
+
dimension: "temperature",
|
|
69864
|
+
factor: 1,
|
|
69865
|
+
offset: 0
|
|
69866
|
+
},
|
|
69867
|
+
"°F": {
|
|
69868
|
+
dimension: "temperature",
|
|
69869
|
+
factor: 5 / 9,
|
|
69870
|
+
offset: -160 / 9
|
|
69871
|
+
},
|
|
69872
|
+
K: {
|
|
69873
|
+
dimension: "temperature",
|
|
69874
|
+
factor: 1,
|
|
69875
|
+
offset: -273.15
|
|
69876
|
+
},
|
|
69877
|
+
hPa: {
|
|
69878
|
+
dimension: "pressure",
|
|
69879
|
+
factor: 1,
|
|
69880
|
+
offset: 0
|
|
69881
|
+
},
|
|
69882
|
+
kPa: {
|
|
69883
|
+
dimension: "pressure",
|
|
69884
|
+
factor: 10,
|
|
69885
|
+
offset: 0
|
|
69886
|
+
},
|
|
69887
|
+
Pa: {
|
|
69888
|
+
dimension: "pressure",
|
|
69889
|
+
factor: .01,
|
|
69890
|
+
offset: 0
|
|
69891
|
+
},
|
|
69892
|
+
mbar: {
|
|
69893
|
+
dimension: "pressure",
|
|
69894
|
+
factor: 1,
|
|
69895
|
+
offset: 0
|
|
69896
|
+
},
|
|
69897
|
+
bar: {
|
|
69898
|
+
dimension: "pressure",
|
|
69899
|
+
factor: 1e3,
|
|
69900
|
+
offset: 0
|
|
69901
|
+
},
|
|
69902
|
+
inHg: {
|
|
69903
|
+
dimension: "pressure",
|
|
69904
|
+
factor: 33.8639,
|
|
69905
|
+
offset: 0
|
|
69906
|
+
},
|
|
69907
|
+
mmHg: {
|
|
69908
|
+
dimension: "pressure",
|
|
69909
|
+
factor: 1.33322,
|
|
69910
|
+
offset: 0
|
|
69911
|
+
},
|
|
69912
|
+
psi: {
|
|
69913
|
+
dimension: "pressure",
|
|
69914
|
+
factor: 68.9476,
|
|
69915
|
+
offset: 0
|
|
69916
|
+
},
|
|
69917
|
+
"m/s": {
|
|
69918
|
+
dimension: "speed",
|
|
69919
|
+
factor: 1,
|
|
69920
|
+
offset: 0
|
|
69921
|
+
},
|
|
69922
|
+
"km/h": {
|
|
69923
|
+
dimension: "speed",
|
|
69924
|
+
factor: 1 / 3.6,
|
|
69925
|
+
offset: 0
|
|
69926
|
+
},
|
|
69927
|
+
mph: {
|
|
69928
|
+
dimension: "speed",
|
|
69929
|
+
factor: .44704,
|
|
69930
|
+
offset: 0
|
|
69931
|
+
},
|
|
69932
|
+
kn: {
|
|
69933
|
+
dimension: "speed",
|
|
69934
|
+
factor: .514444,
|
|
69935
|
+
offset: 0
|
|
69936
|
+
},
|
|
69937
|
+
"mm/h": {
|
|
69938
|
+
dimension: "precipitation-rate",
|
|
69939
|
+
factor: 1,
|
|
69940
|
+
offset: 0
|
|
69941
|
+
},
|
|
69942
|
+
"in/h": {
|
|
69943
|
+
dimension: "precipitation-rate",
|
|
69944
|
+
factor: 25.4,
|
|
69945
|
+
offset: 0
|
|
69946
|
+
},
|
|
69947
|
+
m: {
|
|
69948
|
+
dimension: "length",
|
|
69949
|
+
factor: 1,
|
|
69950
|
+
offset: 0
|
|
69951
|
+
},
|
|
69952
|
+
mm: {
|
|
69953
|
+
dimension: "length",
|
|
69954
|
+
factor: .001,
|
|
69955
|
+
offset: 0
|
|
69956
|
+
},
|
|
69957
|
+
cm: {
|
|
69958
|
+
dimension: "length",
|
|
69959
|
+
factor: .01,
|
|
69960
|
+
offset: 0
|
|
69961
|
+
},
|
|
69962
|
+
km: {
|
|
69963
|
+
dimension: "length",
|
|
69964
|
+
factor: 1e3,
|
|
69965
|
+
offset: 0
|
|
69966
|
+
},
|
|
69967
|
+
in: {
|
|
69968
|
+
dimension: "length",
|
|
69969
|
+
factor: .0254,
|
|
69970
|
+
offset: 0
|
|
69971
|
+
},
|
|
69972
|
+
ft: {
|
|
69973
|
+
dimension: "length",
|
|
69974
|
+
factor: .3048,
|
|
69975
|
+
offset: 0
|
|
69976
|
+
},
|
|
69977
|
+
mi: {
|
|
69978
|
+
dimension: "length",
|
|
69979
|
+
factor: 1609.344,
|
|
69980
|
+
offset: 0
|
|
69981
|
+
},
|
|
69982
|
+
"m²": {
|
|
69983
|
+
dimension: "area",
|
|
69984
|
+
factor: 1,
|
|
69985
|
+
offset: 0
|
|
69986
|
+
},
|
|
69987
|
+
lx: {
|
|
69988
|
+
dimension: "illuminance",
|
|
69989
|
+
factor: 1,
|
|
69990
|
+
offset: 0
|
|
69991
|
+
},
|
|
69992
|
+
"W/m²": {
|
|
69993
|
+
dimension: "irradiance",
|
|
69994
|
+
factor: 1,
|
|
69995
|
+
offset: 0
|
|
69996
|
+
},
|
|
69997
|
+
W: {
|
|
69998
|
+
dimension: "power",
|
|
69999
|
+
factor: 1,
|
|
70000
|
+
offset: 0
|
|
70001
|
+
},
|
|
70002
|
+
kW: {
|
|
70003
|
+
dimension: "power",
|
|
70004
|
+
factor: 1e3,
|
|
70005
|
+
offset: 0
|
|
70006
|
+
},
|
|
70007
|
+
VA: {
|
|
70008
|
+
dimension: "apparent-power",
|
|
70009
|
+
factor: 1,
|
|
70010
|
+
offset: 0
|
|
70011
|
+
},
|
|
70012
|
+
Wh: {
|
|
70013
|
+
dimension: "energy",
|
|
70014
|
+
factor: 1,
|
|
70015
|
+
offset: 0
|
|
70016
|
+
},
|
|
70017
|
+
kWh: {
|
|
70018
|
+
dimension: "energy",
|
|
70019
|
+
factor: 1e3,
|
|
70020
|
+
offset: 0
|
|
70021
|
+
},
|
|
70022
|
+
MWh: {
|
|
70023
|
+
dimension: "energy",
|
|
70024
|
+
factor: 1e6,
|
|
70025
|
+
offset: 0
|
|
70026
|
+
},
|
|
70027
|
+
V: {
|
|
70028
|
+
dimension: "voltage",
|
|
70029
|
+
factor: 1,
|
|
70030
|
+
offset: 0
|
|
70031
|
+
},
|
|
70032
|
+
mV: {
|
|
70033
|
+
dimension: "voltage",
|
|
70034
|
+
factor: .001,
|
|
70035
|
+
offset: 0
|
|
70036
|
+
},
|
|
70037
|
+
A: {
|
|
70038
|
+
dimension: "current",
|
|
70039
|
+
factor: 1,
|
|
70040
|
+
offset: 0
|
|
70041
|
+
},
|
|
70042
|
+
mA: {
|
|
70043
|
+
dimension: "current",
|
|
70044
|
+
factor: .001,
|
|
70045
|
+
offset: 0
|
|
70046
|
+
},
|
|
70047
|
+
ppm: {
|
|
70048
|
+
dimension: "concentration-volume",
|
|
70049
|
+
factor: 1,
|
|
70050
|
+
offset: 0
|
|
70051
|
+
},
|
|
70052
|
+
ppb: {
|
|
70053
|
+
dimension: "concentration-volume",
|
|
70054
|
+
factor: .001,
|
|
70055
|
+
offset: 0
|
|
70056
|
+
},
|
|
70057
|
+
"µg/m³": {
|
|
70058
|
+
dimension: "concentration-mass",
|
|
70059
|
+
factor: 1,
|
|
70060
|
+
offset: 0
|
|
70061
|
+
},
|
|
70062
|
+
"mg/m³": {
|
|
70063
|
+
dimension: "concentration-mass",
|
|
70064
|
+
factor: 1e3,
|
|
70065
|
+
offset: 0
|
|
70066
|
+
},
|
|
70067
|
+
kg: {
|
|
70068
|
+
dimension: "mass",
|
|
70069
|
+
factor: 1,
|
|
70070
|
+
offset: 0
|
|
70071
|
+
},
|
|
70072
|
+
g: {
|
|
70073
|
+
dimension: "mass",
|
|
70074
|
+
factor: .001,
|
|
70075
|
+
offset: 0
|
|
70076
|
+
},
|
|
70077
|
+
lb: {
|
|
70078
|
+
dimension: "mass",
|
|
70079
|
+
factor: .453592,
|
|
70080
|
+
offset: 0
|
|
70081
|
+
},
|
|
70082
|
+
oz: {
|
|
70083
|
+
dimension: "mass",
|
|
70084
|
+
factor: .0283495,
|
|
70085
|
+
offset: 0
|
|
70086
|
+
},
|
|
70087
|
+
"%": {
|
|
70088
|
+
dimension: "percentage",
|
|
70089
|
+
factor: 1,
|
|
70090
|
+
offset: 0
|
|
70091
|
+
},
|
|
70092
|
+
L: {
|
|
70093
|
+
dimension: "volume",
|
|
70094
|
+
factor: 1,
|
|
70095
|
+
offset: 0
|
|
70096
|
+
},
|
|
70097
|
+
mL: {
|
|
70098
|
+
dimension: "volume",
|
|
70099
|
+
factor: .001,
|
|
70100
|
+
offset: 0
|
|
70101
|
+
},
|
|
70102
|
+
gal: {
|
|
70103
|
+
dimension: "volume",
|
|
70104
|
+
factor: 3.785411784,
|
|
70105
|
+
offset: 0
|
|
70106
|
+
},
|
|
70107
|
+
s: {
|
|
70108
|
+
dimension: "duration",
|
|
70109
|
+
factor: 1,
|
|
70110
|
+
offset: 0
|
|
70111
|
+
},
|
|
70112
|
+
ms: {
|
|
70113
|
+
dimension: "duration",
|
|
70114
|
+
factor: .001,
|
|
70115
|
+
offset: 0
|
|
70116
|
+
},
|
|
70117
|
+
min: {
|
|
70118
|
+
dimension: "duration",
|
|
70119
|
+
factor: 60,
|
|
70120
|
+
offset: 0
|
|
70121
|
+
},
|
|
70122
|
+
h: {
|
|
70123
|
+
dimension: "duration",
|
|
70124
|
+
factor: 3600,
|
|
70125
|
+
offset: 0
|
|
70126
|
+
},
|
|
70127
|
+
d: {
|
|
70128
|
+
dimension: "duration",
|
|
70129
|
+
factor: 86400,
|
|
70130
|
+
offset: 0
|
|
70131
|
+
},
|
|
70132
|
+
B: {
|
|
70133
|
+
dimension: "information",
|
|
70134
|
+
factor: 1,
|
|
70135
|
+
offset: 0
|
|
70136
|
+
},
|
|
70137
|
+
KiB: {
|
|
70138
|
+
dimension: "information",
|
|
70139
|
+
factor: 1024,
|
|
70140
|
+
offset: 0
|
|
70141
|
+
},
|
|
70142
|
+
MiB: {
|
|
70143
|
+
dimension: "information",
|
|
70144
|
+
factor: 1024 ** 2,
|
|
70145
|
+
offset: 0
|
|
70146
|
+
},
|
|
70147
|
+
GiB: {
|
|
70148
|
+
dimension: "information",
|
|
70149
|
+
factor: 1024 ** 3,
|
|
70150
|
+
offset: 0
|
|
70151
|
+
},
|
|
70152
|
+
TiB: {
|
|
70153
|
+
dimension: "information",
|
|
70154
|
+
factor: 1024 ** 4,
|
|
70155
|
+
offset: 0
|
|
70156
|
+
},
|
|
70157
|
+
kB: {
|
|
70158
|
+
dimension: "information",
|
|
70159
|
+
factor: 1e3,
|
|
70160
|
+
offset: 0
|
|
70161
|
+
},
|
|
70162
|
+
MB: {
|
|
70163
|
+
dimension: "information",
|
|
70164
|
+
factor: 1e6,
|
|
70165
|
+
offset: 0
|
|
70166
|
+
},
|
|
70167
|
+
GB: {
|
|
70168
|
+
dimension: "information",
|
|
70169
|
+
factor: 1e9,
|
|
70170
|
+
offset: 0
|
|
70171
|
+
},
|
|
70172
|
+
"°": {
|
|
70173
|
+
dimension: "angle",
|
|
70174
|
+
factor: 1,
|
|
70175
|
+
offset: 0
|
|
70176
|
+
},
|
|
70177
|
+
dBFS: {
|
|
70178
|
+
dimension: "sound-level",
|
|
70179
|
+
factor: 1,
|
|
70180
|
+
offset: 0
|
|
70181
|
+
},
|
|
70182
|
+
dBm: {
|
|
70183
|
+
dimension: "signal-level",
|
|
70184
|
+
factor: 1,
|
|
70185
|
+
offset: 0
|
|
70186
|
+
},
|
|
70187
|
+
mired: {
|
|
70188
|
+
dimension: "colour-temperature",
|
|
70189
|
+
factor: 1,
|
|
70190
|
+
offset: 0
|
|
70191
|
+
},
|
|
70192
|
+
kbps: {
|
|
70193
|
+
dimension: "bitrate",
|
|
70194
|
+
factor: 1,
|
|
70195
|
+
offset: 0
|
|
70196
|
+
},
|
|
70197
|
+
Mbps: {
|
|
70198
|
+
dimension: "bitrate",
|
|
70199
|
+
factor: 1e3,
|
|
70200
|
+
offset: 0
|
|
70201
|
+
},
|
|
70202
|
+
fps: {
|
|
70203
|
+
dimension: "rate",
|
|
70204
|
+
factor: 1,
|
|
70205
|
+
offset: 0
|
|
70206
|
+
}
|
|
70207
|
+
};
|
|
70208
|
+
/** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
|
|
70209
|
+
function specFor(unit) {
|
|
70210
|
+
return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
|
|
70211
|
+
}
|
|
70212
|
+
/** The dimension a unit belongs to, or `undefined` for an unknown unit. */
|
|
70213
|
+
function unitDimension(unit) {
|
|
70214
|
+
return specFor(unit)?.dimension;
|
|
70215
|
+
}
|
|
70216
|
+
/** Value in `spec`'s unit → its dimension's canonical unit. */
|
|
70217
|
+
function toCanonical(value, spec) {
|
|
70218
|
+
return value * spec.factor + spec.offset;
|
|
70219
|
+
}
|
|
70220
|
+
/** Canonical-unit value → the unit described by `spec`. */
|
|
70221
|
+
function fromCanonical(canonical, spec) {
|
|
70222
|
+
return (canonical - spec.offset) / spec.factor;
|
|
70223
|
+
}
|
|
70224
|
+
/**
|
|
70225
|
+
* Graceful variant for the render path: returns the converted value, or
|
|
70226
|
+
* `undefined` when the conversion is refused (unknown unit / cross-dimension).
|
|
70227
|
+
* Never throws — a render caller falls back to the raw value + source unit.
|
|
70228
|
+
*/
|
|
70229
|
+
function tryConvertUnit(value, from, to) {
|
|
70230
|
+
if (from === to) return value;
|
|
70231
|
+
const fromSpec = specFor(from);
|
|
70232
|
+
if (!fromSpec) return void 0;
|
|
70233
|
+
const toSpec = specFor(to);
|
|
70234
|
+
if (!toSpec) return void 0;
|
|
70235
|
+
if (fromSpec.dimension !== toSpec.dimension) return void 0;
|
|
70236
|
+
return fromCanonical(toCanonical(value, fromSpec), toSpec);
|
|
70237
|
+
}
|
|
69413
70238
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
69414
70239
|
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;
|
|
69415
70240
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
@@ -69740,6 +70565,8 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
69740
70565
|
featureProbe: featureProbeCapability,
|
|
69741
70566
|
flood: floodCapability,
|
|
69742
70567
|
gas: gasCapability,
|
|
70568
|
+
groupCommands: groupCommandsCapability,
|
|
70569
|
+
groupStatus: groupStatusCapability,
|
|
69743
70570
|
heatingValve: heatingValveCapability,
|
|
69744
70571
|
humidifier: humidifierCapability,
|
|
69745
70572
|
humiditySensor: humiditySensorCapability,
|
|
@@ -71116,6 +71943,13 @@ Object.freeze({
|
|
|
71116
71943
|
access: "view",
|
|
71117
71944
|
role: "admin"
|
|
71118
71945
|
},
|
|
71946
|
+
"addons.getPackageDocs": {
|
|
71947
|
+
capName: "addons",
|
|
71948
|
+
capScope: "system",
|
|
71949
|
+
addonId: null,
|
|
71950
|
+
access: "view",
|
|
71951
|
+
role: "admin"
|
|
71952
|
+
},
|
|
71119
71953
|
"addons.getVersions": {
|
|
71120
71954
|
capName: "addons",
|
|
71121
71955
|
capScope: "system",
|
|
@@ -74224,6 +75058,20 @@ Object.freeze({
|
|
|
74224
75058
|
access: "view",
|
|
74225
75059
|
role: "viewer"
|
|
74226
75060
|
},
|
|
75061
|
+
"groupCommands.getStatus": {
|
|
75062
|
+
capName: "group-commands",
|
|
75063
|
+
capScope: "device",
|
|
75064
|
+
addonId: null,
|
|
75065
|
+
access: "view",
|
|
75066
|
+
role: "viewer"
|
|
75067
|
+
},
|
|
75068
|
+
"groupStatus.getStatus": {
|
|
75069
|
+
capName: "group-status",
|
|
75070
|
+
capScope: "device",
|
|
75071
|
+
addonId: null,
|
|
75072
|
+
access: "view",
|
|
75073
|
+
role: "viewer"
|
|
75074
|
+
},
|
|
74227
75075
|
"heatingValve.getStatus": {
|
|
74228
75076
|
capName: "heating-valve",
|
|
74229
75077
|
capScope: "device",
|
|
@@ -84046,6 +84894,37 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84046
84894
|
icon: "flame"
|
|
84047
84895
|
}]
|
|
84048
84896
|
},
|
|
84897
|
+
"group-commands": {
|
|
84898
|
+
scope: "device",
|
|
84899
|
+
noun: t("Group command", "Comando di gruppo")
|
|
84900
|
+
},
|
|
84901
|
+
"group-status": {
|
|
84902
|
+
scope: "device",
|
|
84903
|
+
noun: t("Group", "Gruppo"),
|
|
84904
|
+
state: [
|
|
84905
|
+
{
|
|
84906
|
+
kind: "numeric",
|
|
84907
|
+
field: "included",
|
|
84908
|
+
quantity: t("the members reporting", "i membri che rispondono"),
|
|
84909
|
+
label: t("Reporting", "Rispondono"),
|
|
84910
|
+
icon: "users"
|
|
84911
|
+
},
|
|
84912
|
+
{
|
|
84913
|
+
kind: "numeric",
|
|
84914
|
+
field: "excluded",
|
|
84915
|
+
quantity: t("the members left out", "i membri esclusi"),
|
|
84916
|
+
label: t("Excluded", "Esclusi"),
|
|
84917
|
+
icon: "users"
|
|
84918
|
+
},
|
|
84919
|
+
{
|
|
84920
|
+
kind: "numeric",
|
|
84921
|
+
field: "total",
|
|
84922
|
+
quantity: t("the member count", "il numero di membri"),
|
|
84923
|
+
label: t("Members", "Membri"),
|
|
84924
|
+
icon: "sigma"
|
|
84925
|
+
}
|
|
84926
|
+
]
|
|
84927
|
+
},
|
|
84049
84928
|
"humidity-sensor": {
|
|
84050
84929
|
scope: "device",
|
|
84051
84930
|
noun: t("Humidity", "Umidità"),
|
|
@@ -84079,7 +84958,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84079
84958
|
field: "value",
|
|
84080
84959
|
quantity: t("the reading", "il valore"),
|
|
84081
84960
|
label: t("Reading", "Valore"),
|
|
84082
|
-
unitField: "unit",
|
|
84083
84961
|
icon: "gauge"
|
|
84084
84962
|
}]
|
|
84085
84963
|
},
|
|
@@ -84215,7 +85093,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84215
85093
|
field: "temperature",
|
|
84216
85094
|
quantity: t("the outdoor temperature", "la temperatura esterna"),
|
|
84217
85095
|
label: t("Temperature", "Temperatura"),
|
|
84218
|
-
|
|
85096
|
+
unit: "°C",
|
|
84219
85097
|
icon: "thermometer"
|
|
84220
85098
|
},
|
|
84221
85099
|
{
|
|
@@ -84231,7 +85109,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84231
85109
|
field: "windSpeed",
|
|
84232
85110
|
quantity: t("the wind speed", "la velocità del vento"),
|
|
84233
85111
|
label: t("Wind", "Vento"),
|
|
84234
|
-
|
|
85112
|
+
unit: "m/s",
|
|
84235
85113
|
icon: "wind"
|
|
84236
85114
|
},
|
|
84237
85115
|
{
|
|
@@ -84239,7 +85117,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84239
85117
|
field: "pressure",
|
|
84240
85118
|
quantity: t("the air pressure", "la pressione atmosferica"),
|
|
84241
85119
|
label: t("Pressure", "Pressione"),
|
|
84242
|
-
|
|
85120
|
+
unit: "hPa",
|
|
84243
85121
|
icon: "gauge"
|
|
84244
85122
|
}
|
|
84245
85123
|
]
|
|
@@ -85512,6 +86390,16 @@ Object.freeze({
|
|
|
85512
86390
|
form: "single",
|
|
85513
86391
|
optional: false
|
|
85514
86392
|
}],
|
|
86393
|
+
"groupCommands.getStatus": [{
|
|
86394
|
+
name: "deviceId",
|
|
86395
|
+
form: "single",
|
|
86396
|
+
optional: false
|
|
86397
|
+
}],
|
|
86398
|
+
"groupStatus.getStatus": [{
|
|
86399
|
+
name: "deviceId",
|
|
86400
|
+
form: "single",
|
|
86401
|
+
optional: false
|
|
86402
|
+
}],
|
|
85515
86403
|
"heatingValve.getStatus": [{
|
|
85516
86404
|
name: "deviceId",
|
|
85517
86405
|
form: "single",
|
|
@@ -87496,6 +88384,8 @@ new Map(Object.entries({
|
|
|
87496
88384
|
"detection-blind": "device-events",
|
|
87497
88385
|
"motion-noisy": "device-events",
|
|
87498
88386
|
"motion-calm": "device-events",
|
|
88387
|
+
"package-llm-silent": "device-events",
|
|
88388
|
+
"package-llm-answered": "device-events",
|
|
87499
88389
|
"recording-stalled": "device-events",
|
|
87500
88390
|
"recording-healthy": "device-events",
|
|
87501
88391
|
"recording-degraded": "device-events",
|
|
@@ -87579,6 +88469,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
87579
88469
|
label: "Motion without a subject",
|
|
87580
88470
|
kinds: ["motion-noisy", "motion-calm"]
|
|
87581
88471
|
},
|
|
88472
|
+
{
|
|
88473
|
+
id: "package-llm",
|
|
88474
|
+
family: "device-events",
|
|
88475
|
+
label: "Package confirm",
|
|
88476
|
+
kinds: ["package-llm-silent", "package-llm-answered"]
|
|
88477
|
+
},
|
|
87582
88478
|
{
|
|
87583
88479
|
id: "recording",
|
|
87584
88480
|
family: "device-events",
|
|
@@ -87760,6 +88656,8 @@ var DEVICE_EVENTS = [
|
|
|
87760
88656
|
"detection-blind",
|
|
87761
88657
|
"motion-noisy",
|
|
87762
88658
|
"motion-calm",
|
|
88659
|
+
"package-llm-silent",
|
|
88660
|
+
"package-llm-answered",
|
|
87763
88661
|
"recording-stalled",
|
|
87764
88662
|
"recording-healthy",
|
|
87765
88663
|
"recording-degraded",
|
|
@@ -88028,6 +88926,15 @@ new Map([
|
|
|
88028
88926
|
snapshotLabel: "Cameras with constant motion now",
|
|
88029
88927
|
snapshotSection: null
|
|
88030
88928
|
},
|
|
88929
|
+
{
|
|
88930
|
+
id: "package-llm-confirm",
|
|
88931
|
+
family: "device-events",
|
|
88932
|
+
label: "Package confirm (not answering / answering again)",
|
|
88933
|
+
bad: ["package-llm-silent"],
|
|
88934
|
+
recovery: ["package-llm-answered"],
|
|
88935
|
+
snapshotLabel: "Cameras whose package confirm is not answering",
|
|
88936
|
+
snapshotSection: null
|
|
88937
|
+
},
|
|
88031
88938
|
{
|
|
88032
88939
|
id: "recording-health",
|
|
88033
88940
|
family: "device-events",
|
|
@@ -88605,6 +89512,96 @@ DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
|
88605
89512
|
DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
|
|
88606
89513
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
88607
89514
|
/**
|
|
89515
|
+
* Convert an upstream number into the stored base of its quantity (D944 §4).
|
|
89516
|
+
* An unknown unit or a unit from another dimension is a refusal: the caller
|
|
89517
|
+
* logs it and publishes null (D393). This function never guesses.
|
|
89518
|
+
*/
|
|
89519
|
+
var DIMENSION_QUANTITY = {
|
|
89520
|
+
temperature: "temperature",
|
|
89521
|
+
pressure: "pressure",
|
|
89522
|
+
speed: "speed",
|
|
89523
|
+
"precipitation-rate": "precipitation-rate",
|
|
89524
|
+
length: "length",
|
|
89525
|
+
area: "area",
|
|
89526
|
+
illuminance: "illuminance",
|
|
89527
|
+
irradiance: "irradiance",
|
|
89528
|
+
power: "power",
|
|
89529
|
+
"apparent-power": "apparent-power",
|
|
89530
|
+
energy: "energy",
|
|
89531
|
+
voltage: "voltage",
|
|
89532
|
+
current: "current",
|
|
89533
|
+
"concentration-volume": "concentration-volume",
|
|
89534
|
+
"concentration-mass": "concentration-mass",
|
|
89535
|
+
mass: "mass",
|
|
89536
|
+
percentage: "percentage",
|
|
89537
|
+
volume: "volume",
|
|
89538
|
+
duration: "duration",
|
|
89539
|
+
information: "information",
|
|
89540
|
+
angle: "angle",
|
|
89541
|
+
"sound-level": "sound-level",
|
|
89542
|
+
"signal-level": "signal-level",
|
|
89543
|
+
"colour-temperature": "colour-temperature",
|
|
89544
|
+
bitrate: "bitrate",
|
|
89545
|
+
rate: "rate"
|
|
89546
|
+
};
|
|
89547
|
+
/**
|
|
89548
|
+
* Quantity implied by a unit symbol. `ppb` and `ms` are the two symbols
|
|
89549
|
+
* whose stored base is not the dimension's canonical unit. `mm` is length;
|
|
89550
|
+
* precipitation is chosen by the caller (a device class), not by the symbol.
|
|
89551
|
+
*/
|
|
89552
|
+
function quantityFromUnit(rawUnit) {
|
|
89553
|
+
const unit = normalizeUnit(rawUnit);
|
|
89554
|
+
if (unit === void 0) return void 0;
|
|
89555
|
+
if (unit === "ppb") return "concentration-ppb";
|
|
89556
|
+
if (unit === "ms") return "duration-ms";
|
|
89557
|
+
const dimension = unitDimension(unit);
|
|
89558
|
+
if (dimension === void 0) return void 0;
|
|
89559
|
+
return DIMENSION_QUANTITY[dimension];
|
|
89560
|
+
}
|
|
89561
|
+
function ingestReading(input) {
|
|
89562
|
+
if (!Number.isFinite(input.value)) return {
|
|
89563
|
+
ok: false,
|
|
89564
|
+
reason: "non-finite"
|
|
89565
|
+
};
|
|
89566
|
+
if (input.quantity === "device") return {
|
|
89567
|
+
ok: false,
|
|
89568
|
+
reason: "unknown-unit"
|
|
89569
|
+
};
|
|
89570
|
+
const raw = input.rawUnit?.trim();
|
|
89571
|
+
if (raw === void 0 || raw.length === 0) return {
|
|
89572
|
+
ok: true,
|
|
89573
|
+
value: input.value
|
|
89574
|
+
};
|
|
89575
|
+
const unit = normalizeUnit(raw);
|
|
89576
|
+
if (unit === void 0) return {
|
|
89577
|
+
ok: false,
|
|
89578
|
+
reason: "unknown-unit"
|
|
89579
|
+
};
|
|
89580
|
+
const base = baseUnitOf(input.quantity);
|
|
89581
|
+
if (base === void 0) {
|
|
89582
|
+
if (unitDimension(unit) !== void 0) return {
|
|
89583
|
+
ok: false,
|
|
89584
|
+
reason: "cross-dimension"
|
|
89585
|
+
};
|
|
89586
|
+
return {
|
|
89587
|
+
ok: false,
|
|
89588
|
+
reason: "unknown-unit"
|
|
89589
|
+
};
|
|
89590
|
+
}
|
|
89591
|
+
const converted = tryConvertUnit(input.value, unit, base);
|
|
89592
|
+
if (converted === void 0) return unitDimension(unit) === void 0 ? {
|
|
89593
|
+
ok: false,
|
|
89594
|
+
reason: "unknown-unit"
|
|
89595
|
+
} : {
|
|
89596
|
+
ok: false,
|
|
89597
|
+
reason: "cross-dimension"
|
|
89598
|
+
};
|
|
89599
|
+
return {
|
|
89600
|
+
ok: true,
|
|
89601
|
+
value: converted
|
|
89602
|
+
};
|
|
89603
|
+
}
|
|
89604
|
+
/**
|
|
88608
89605
|
* Pure fuzzy matcher for adoption location import. Normalized
|
|
88609
89606
|
* case-insensitive Levenshtein similarity, used to reuse an existing
|
|
88610
89607
|
* CamStack location when an imported source location (e.g. an HA area) is
|
|
@@ -114239,11 +115236,7 @@ var hmDiagnosticSchema = hmChildSchema.extend({
|
|
|
114239
115236
|
sourceParams: array(string()).min(1)
|
|
114240
115237
|
});
|
|
114241
115238
|
/** Cold-start slices keyed by cap. */
|
|
114242
|
-
var COLD_NUMERIC =
|
|
114243
|
-
value: 0,
|
|
114244
|
-
unit,
|
|
114245
|
-
lastFetchedAt: 0
|
|
114246
|
-
} : {
|
|
115239
|
+
var COLD_NUMERIC = {
|
|
114247
115240
|
value: 0,
|
|
114248
115241
|
lastFetchedAt: 0
|
|
114249
115242
|
};
|
|
@@ -114287,7 +115280,7 @@ var HmDiagnosticDevice = class extends HmChildDevice {
|
|
|
114287
115280
|
this.runtimeState.setCapState("temperature-sensor", COLD_TEMP);
|
|
114288
115281
|
return;
|
|
114289
115282
|
default: {
|
|
114290
|
-
const cold = COLD_NUMERIC
|
|
115283
|
+
const cold = COLD_NUMERIC;
|
|
114291
115284
|
this.ctx.registerNativeCap(numericSensorCapability, { getStatus: async () => this.runtimeState.getCapState("numeric-sensor") ?? cold });
|
|
114292
115285
|
this.runtimeState.setCapState("numeric-sensor", cold);
|
|
114293
115286
|
return;
|
|
@@ -114312,22 +115305,55 @@ var HmDiagnosticDevice = class extends HmChildDevice {
|
|
|
114312
115305
|
return;
|
|
114313
115306
|
}
|
|
114314
115307
|
if (typeof value !== "number") return;
|
|
115308
|
+
const stored = this.toBase(value);
|
|
115309
|
+
if (stored === null) return;
|
|
114315
115310
|
if (this.capName === "temperature-sensor") {
|
|
114316
115311
|
this.runtimeState.setCapState("temperature-sensor", {
|
|
114317
|
-
celsius:
|
|
115312
|
+
celsius: stored,
|
|
114318
115313
|
lastFetchedAt: now
|
|
114319
115314
|
});
|
|
114320
115315
|
return;
|
|
114321
115316
|
}
|
|
114322
|
-
|
|
114323
|
-
value,
|
|
114324
|
-
unit: this.unit,
|
|
115317
|
+
this.runtimeState.setCapState("numeric-sensor", {
|
|
115318
|
+
value: stored,
|
|
114325
115319
|
lastFetchedAt: now
|
|
114326
|
-
}
|
|
115320
|
+
});
|
|
115321
|
+
}
|
|
115322
|
+
/** Upstream diagnostic number → the quantity's base. Unknown units publish nothing. */
|
|
115323
|
+
toBase(value) {
|
|
115324
|
+
const quantity = this.readingQuantity();
|
|
115325
|
+
if (quantity === null) {
|
|
115326
|
+
this.warnRefusal("unknown-unit");
|
|
115327
|
+
return null;
|
|
115328
|
+
}
|
|
115329
|
+
const result = ingestReading({
|
|
114327
115330
|
value,
|
|
114328
|
-
|
|
114329
|
-
|
|
114330
|
-
|
|
115331
|
+
rawUnit: this.unit,
|
|
115332
|
+
quantity
|
|
115333
|
+
});
|
|
115334
|
+
if (!result.ok) {
|
|
115335
|
+
this.warnRefusal(result.reason);
|
|
115336
|
+
return null;
|
|
115337
|
+
}
|
|
115338
|
+
if (this.capName === "numeric-sensor" && this.sourceInfo.quantity !== quantity) this.updateSourceInfo({ quantity });
|
|
115339
|
+
return result.value;
|
|
115340
|
+
}
|
|
115341
|
+
readingQuantity() {
|
|
115342
|
+
if (this.capName === "temperature-sensor") return "temperature";
|
|
115343
|
+
if (this.unit === void 0) return "none";
|
|
115344
|
+
const learned = quantityFromUnit(this.unit);
|
|
115345
|
+
if (learned === void 0 || learned === "device") return null;
|
|
115346
|
+
return learned;
|
|
115347
|
+
}
|
|
115348
|
+
warnRefusal(reason) {
|
|
115349
|
+
this.ctx.logger.warn("numeric reading refused", {
|
|
115350
|
+
tags: { deviceId: this.id },
|
|
115351
|
+
meta: {
|
|
115352
|
+
quantity: this.readingQuantity() ?? "device",
|
|
115353
|
+
unit: this.unit ?? "",
|
|
115354
|
+
reason
|
|
115355
|
+
}
|
|
115356
|
+
});
|
|
114331
115357
|
}
|
|
114332
115358
|
};
|
|
114333
115359
|
//#endregion
|