@camstack/addon-provider-ecowitt 0.2.148 → 0.2.150
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 +16 -1
- package/README.md +26 -8
- package/dist/addon.js +1530 -291
- package/dist/addon.mjs +1530 -291
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
let http = require("http");
|
|
3
3
|
let events = require("events");
|
|
4
4
|
let dgram = require("dgram");
|
|
5
|
-
//#region ../types/dist/event-category-
|
|
5
|
+
//#region ../types/dist/event-category-DmZDPDf_.mjs
|
|
6
6
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
7
7
|
EventCategory["SystemBoot"] = "system.boot";
|
|
8
8
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -629,7 +629,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
629
629
|
* (a track was promoted to a stationary-registry entry) or departs (the
|
|
630
630
|
* object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
|
|
631
631
|
* the dedicated "Stationary" section — never the live event feed. Payload:
|
|
632
|
-
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
|
|
632
|
+
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp, observedAt, zones }`.
|
|
633
633
|
*/
|
|
634
634
|
EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
|
|
635
635
|
/**
|
|
@@ -767,6 +767,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
767
767
|
*/
|
|
768
768
|
EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
|
|
769
769
|
/**
|
|
770
|
+
* An action run started or ended. Emitted by the `actions` surface.
|
|
771
|
+
* Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
|
|
772
|
+
*/
|
|
773
|
+
EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
|
|
774
|
+
/**
|
|
770
775
|
* Progress update from a running model conversion job.
|
|
771
776
|
* Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
|
|
772
777
|
* Emitted by `addon-model-studio` on the converting node.
|
|
@@ -5445,7 +5450,7 @@ var ZodIssueCode$1 = {
|
|
|
5445
5450
|
var ZodFirstPartyTypeKind$1;
|
|
5446
5451
|
ZodFirstPartyTypeKind$1 || (ZodFirstPartyTypeKind$1 = {});
|
|
5447
5452
|
//#endregion
|
|
5448
|
-
//#region ../types/dist/sleep-
|
|
5453
|
+
//#region ../types/dist/sleep-Crlmu_tO.mjs
|
|
5449
5454
|
/**
|
|
5450
5455
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5451
5456
|
* window to float samples (D455).
|
|
@@ -8565,6 +8570,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
|
|
|
8565
8570
|
/** Every capability name, so addon code can check a name without the schema graph. */
|
|
8566
8571
|
var CAP_NAMES = Object.freeze([
|
|
8567
8572
|
"accessories",
|
|
8573
|
+
"action-source",
|
|
8574
|
+
"actions",
|
|
8568
8575
|
"addon-pages",
|
|
8569
8576
|
"addon-pages-source",
|
|
8570
8577
|
"addon-routes",
|
|
@@ -8638,6 +8645,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8638
8645
|
"filesystem-browse",
|
|
8639
8646
|
"flood",
|
|
8640
8647
|
"gas",
|
|
8648
|
+
"group-active",
|
|
8641
8649
|
"group-commands",
|
|
8642
8650
|
"group-status",
|
|
8643
8651
|
"heating-valve",
|
|
@@ -8660,6 +8668,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8660
8668
|
"login-method",
|
|
8661
8669
|
"media-player",
|
|
8662
8670
|
"mesh-network",
|
|
8671
|
+
"meter",
|
|
8663
8672
|
"metrics-provider",
|
|
8664
8673
|
"model-convert",
|
|
8665
8674
|
"model-distributor",
|
|
@@ -8751,11 +8760,11 @@ var CAP_NAMES = Object.freeze([
|
|
|
8751
8760
|
Object.freeze({
|
|
8752
8761
|
"air-quality-sensor": [
|
|
8753
8762
|
{
|
|
8754
|
-
"path": "
|
|
8763
|
+
"path": "co2",
|
|
8755
8764
|
"valueType": "number"
|
|
8756
8765
|
},
|
|
8757
8766
|
{
|
|
8758
|
-
"path": "
|
|
8767
|
+
"path": "voc",
|
|
8759
8768
|
"valueType": "number"
|
|
8760
8769
|
},
|
|
8761
8770
|
{
|
|
@@ -8805,7 +8814,7 @@ Object.freeze({
|
|
|
8805
8814
|
}
|
|
8806
8815
|
],
|
|
8807
8816
|
"ambient-light-sensor": [{
|
|
8808
|
-
"path": "
|
|
8817
|
+
"path": "illuminance",
|
|
8809
8818
|
"valueType": "number"
|
|
8810
8819
|
}],
|
|
8811
8820
|
"audio-metrics": [
|
|
@@ -8866,7 +8875,7 @@ Object.freeze({
|
|
|
8866
8875
|
],
|
|
8867
8876
|
"battery": [
|
|
8868
8877
|
{
|
|
8869
|
-
"path": "
|
|
8878
|
+
"path": "level",
|
|
8870
8879
|
"valueType": "number"
|
|
8871
8880
|
},
|
|
8872
8881
|
{
|
|
@@ -8888,7 +8897,7 @@ Object.freeze({
|
|
|
8888
8897
|
"valueType": "number"
|
|
8889
8898
|
},
|
|
8890
8899
|
{
|
|
8891
|
-
"path": "
|
|
8900
|
+
"path": "voltage",
|
|
8892
8901
|
"valueType": "number"
|
|
8893
8902
|
},
|
|
8894
8903
|
{
|
|
@@ -8909,7 +8918,7 @@ Object.freeze({
|
|
|
8909
8918
|
"valueType": "boolean"
|
|
8910
8919
|
}],
|
|
8911
8920
|
"brightness": [{
|
|
8912
|
-
"path": "
|
|
8921
|
+
"path": "level",
|
|
8913
8922
|
"valueType": "number"
|
|
8914
8923
|
}],
|
|
8915
8924
|
"broker": [{
|
|
@@ -9110,7 +9119,7 @@ Object.freeze({
|
|
|
9110
9119
|
"valueType": "number"
|
|
9111
9120
|
},
|
|
9112
9121
|
{
|
|
9113
|
-
"path": "
|
|
9122
|
+
"path": "colorTemperature",
|
|
9114
9123
|
"valueType": "number"
|
|
9115
9124
|
}
|
|
9116
9125
|
],
|
|
@@ -9289,15 +9298,15 @@ Object.freeze({
|
|
|
9289
9298
|
"valueType": "string"
|
|
9290
9299
|
},
|
|
9291
9300
|
{
|
|
9292
|
-
"path": "
|
|
9301
|
+
"path": "remaining",
|
|
9293
9302
|
"valueType": "number"
|
|
9294
9303
|
},
|
|
9295
9304
|
{
|
|
9296
|
-
"path": "
|
|
9305
|
+
"path": "delayedStart",
|
|
9297
9306
|
"valueType": "number"
|
|
9298
9307
|
},
|
|
9299
9308
|
{
|
|
9300
|
-
"path": "
|
|
9309
|
+
"path": "progress",
|
|
9301
9310
|
"valueType": "number"
|
|
9302
9311
|
},
|
|
9303
9312
|
{
|
|
@@ -9411,7 +9420,7 @@ Object.freeze({
|
|
|
9411
9420
|
],
|
|
9412
9421
|
"fan-control": [
|
|
9413
9422
|
{
|
|
9414
|
-
"path": "
|
|
9423
|
+
"path": "speed",
|
|
9415
9424
|
"valueType": "number"
|
|
9416
9425
|
},
|
|
9417
9426
|
{
|
|
@@ -9458,6 +9467,10 @@ Object.freeze({
|
|
|
9458
9467
|
"path": "detected",
|
|
9459
9468
|
"valueType": "boolean"
|
|
9460
9469
|
}],
|
|
9470
|
+
"group-active": [{
|
|
9471
|
+
"path": "active",
|
|
9472
|
+
"valueType": "boolean"
|
|
9473
|
+
}],
|
|
9461
9474
|
"group-status": [
|
|
9462
9475
|
{
|
|
9463
9476
|
"path": "included",
|
|
@@ -9507,7 +9520,7 @@ Object.freeze({
|
|
|
9507
9520
|
}
|
|
9508
9521
|
],
|
|
9509
9522
|
"humidity-sensor": [{
|
|
9510
|
-
"path": "
|
|
9523
|
+
"path": "humidity",
|
|
9511
9524
|
"valueType": "number"
|
|
9512
9525
|
}],
|
|
9513
9526
|
"image": [{
|
|
@@ -9617,7 +9630,7 @@ Object.freeze({
|
|
|
9617
9630
|
"valueType": "number"
|
|
9618
9631
|
},
|
|
9619
9632
|
{
|
|
9620
|
-
"path": "
|
|
9633
|
+
"path": "progress",
|
|
9621
9634
|
"valueType": "number"
|
|
9622
9635
|
},
|
|
9623
9636
|
{
|
|
@@ -9696,11 +9709,11 @@ Object.freeze({
|
|
|
9696
9709
|
"valueType": "string"
|
|
9697
9710
|
},
|
|
9698
9711
|
{
|
|
9699
|
-
"path": "
|
|
9712
|
+
"path": "position",
|
|
9700
9713
|
"valueType": "number"
|
|
9701
9714
|
},
|
|
9702
9715
|
{
|
|
9703
|
-
"path": "
|
|
9716
|
+
"path": "duration",
|
|
9704
9717
|
"valueType": "number"
|
|
9705
9718
|
},
|
|
9706
9719
|
{
|
|
@@ -9717,6 +9730,10 @@ Object.freeze({
|
|
|
9717
9730
|
]
|
|
9718
9731
|
}
|
|
9719
9732
|
],
|
|
9733
|
+
"meter": [{
|
|
9734
|
+
"path": "value",
|
|
9735
|
+
"valueType": "number"
|
|
9736
|
+
}],
|
|
9720
9737
|
"motion": [
|
|
9721
9738
|
{
|
|
9722
9739
|
"path": "detected",
|
|
@@ -9798,11 +9815,11 @@ Object.freeze({
|
|
|
9798
9815
|
]
|
|
9799
9816
|
},
|
|
9800
9817
|
{
|
|
9801
|
-
"path": "
|
|
9818
|
+
"path": "signal",
|
|
9802
9819
|
"valueType": "number"
|
|
9803
9820
|
},
|
|
9804
9821
|
{
|
|
9805
|
-
"path": "
|
|
9822
|
+
"path": "rssi",
|
|
9806
9823
|
"valueType": "number"
|
|
9807
9824
|
},
|
|
9808
9825
|
{
|
|
@@ -9857,6 +9874,18 @@ Object.freeze({
|
|
|
9857
9874
|
"path": "foodLevel",
|
|
9858
9875
|
"valueType": "number"
|
|
9859
9876
|
},
|
|
9877
|
+
{
|
|
9878
|
+
"path": "bowlFood",
|
|
9879
|
+
"valueType": "number"
|
|
9880
|
+
},
|
|
9881
|
+
{
|
|
9882
|
+
"path": "eating",
|
|
9883
|
+
"valueType": "boolean"
|
|
9884
|
+
},
|
|
9885
|
+
{
|
|
9886
|
+
"path": "supportsMarkRefilled",
|
|
9887
|
+
"valueType": "boolean"
|
|
9888
|
+
},
|
|
9860
9889
|
{
|
|
9861
9890
|
"path": "food1",
|
|
9862
9891
|
"valueType": "number"
|
|
@@ -9921,23 +9950,23 @@ Object.freeze({
|
|
|
9921
9950
|
],
|
|
9922
9951
|
"power-meter": [
|
|
9923
9952
|
{
|
|
9924
|
-
"path": "
|
|
9953
|
+
"path": "power",
|
|
9925
9954
|
"valueType": "number"
|
|
9926
9955
|
},
|
|
9927
9956
|
{
|
|
9928
|
-
"path": "
|
|
9957
|
+
"path": "energy",
|
|
9929
9958
|
"valueType": "number"
|
|
9930
9959
|
},
|
|
9931
9960
|
{
|
|
9932
|
-
"path": "
|
|
9961
|
+
"path": "voltage",
|
|
9933
9962
|
"valueType": "number"
|
|
9934
9963
|
},
|
|
9935
9964
|
{
|
|
9936
|
-
"path": "
|
|
9965
|
+
"path": "current",
|
|
9937
9966
|
"valueType": "number"
|
|
9938
9967
|
},
|
|
9939
9968
|
{
|
|
9940
|
-
"path": "
|
|
9969
|
+
"path": "apparentPower",
|
|
9941
9970
|
"valueType": "number"
|
|
9942
9971
|
},
|
|
9943
9972
|
{
|
|
@@ -9965,14 +9994,10 @@ Object.freeze({
|
|
|
9965
9994
|
{
|
|
9966
9995
|
"path": "gps.accuracyMeters",
|
|
9967
9996
|
"valueType": "number"
|
|
9968
|
-
},
|
|
9969
|
-
{
|
|
9970
|
-
"path": "batteryPercent",
|
|
9971
|
-
"valueType": "number"
|
|
9972
9997
|
}
|
|
9973
9998
|
],
|
|
9974
9999
|
"pressure-sensor": [{
|
|
9975
|
-
"path": "
|
|
10000
|
+
"path": "pressure",
|
|
9976
10001
|
"valueType": "number"
|
|
9977
10002
|
}],
|
|
9978
10003
|
"privacy-mask": [{
|
|
@@ -10286,7 +10311,7 @@ Object.freeze({
|
|
|
10286
10311
|
"valueType": "boolean"
|
|
10287
10312
|
}],
|
|
10288
10313
|
"temperature-sensor": [{
|
|
10289
|
-
"path": "
|
|
10314
|
+
"path": "temperature",
|
|
10290
10315
|
"valueType": "number"
|
|
10291
10316
|
}],
|
|
10292
10317
|
"update": [
|
|
@@ -10334,7 +10359,7 @@ Object.freeze({
|
|
|
10334
10359
|
"valueType": "string"
|
|
10335
10360
|
},
|
|
10336
10361
|
{
|
|
10337
|
-
"path": "
|
|
10362
|
+
"path": "progress",
|
|
10338
10363
|
"valueType": "number"
|
|
10339
10364
|
},
|
|
10340
10365
|
{
|
|
@@ -10474,7 +10499,7 @@ function userError(code, params, englishFallback) {
|
|
|
10474
10499
|
return new CamstackError(code, params, englishFallback);
|
|
10475
10500
|
}
|
|
10476
10501
|
//#endregion
|
|
10477
|
-
//#region ../types/dist/err-msg-
|
|
10502
|
+
//#region ../types/dist/err-msg-DIorGYeC.mjs
|
|
10478
10503
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
10479
10504
|
DeviceType["Camera"] = "camera";
|
|
10480
10505
|
DeviceType["Hub"] = "hub";
|
|
@@ -10568,8 +10593,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
10568
10593
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
10569
10594
|
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
10570
10595
|
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
10571
|
-
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10596
|
+
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10597
|
+
* A plug-built dishwasher carries running-state and power instead. */
|
|
10572
10598
|
DeviceType["Dishwasher"] = "dishwasher";
|
|
10599
|
+
/** Washing machine. A plug-built one is running while its power stays
|
|
10600
|
+
* above a threshold. No program cap — that surface is the dishwasher's. */
|
|
10601
|
+
DeviceType["Washer"] = "washer";
|
|
10602
|
+
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
10603
|
+
DeviceType["Dryer"] = "dryer";
|
|
10573
10604
|
return DeviceType;
|
|
10574
10605
|
}({});
|
|
10575
10606
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -10827,7 +10858,7 @@ function errMsg(err) {
|
|
|
10827
10858
|
return String(err);
|
|
10828
10859
|
}
|
|
10829
10860
|
//#endregion
|
|
10830
|
-
//#region ../types/dist/composition-
|
|
10861
|
+
//#region ../types/dist/composition-CZImLpOI.mjs
|
|
10831
10862
|
/**
|
|
10832
10863
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
10833
10864
|
*
|
|
@@ -11142,6 +11173,13 @@ var table = {
|
|
|
11142
11173
|
apply: () => {
|
|
11143
11174
|
throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
|
|
11144
11175
|
}
|
|
11176
|
+
},
|
|
11177
|
+
schmitt: {
|
|
11178
|
+
minArgs: 3,
|
|
11179
|
+
maxArgs: 5,
|
|
11180
|
+
apply: () => {
|
|
11181
|
+
throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
|
|
11182
|
+
}
|
|
11145
11183
|
}
|
|
11146
11184
|
};
|
|
11147
11185
|
Object.freeze(Object.assign(Object.create(null), table));
|
|
@@ -11583,6 +11621,99 @@ function validateExpressionSource(src) {
|
|
|
11583
11621
|
return null;
|
|
11584
11622
|
}
|
|
11585
11623
|
/**
|
|
11624
|
+
* One power reading becomes a washer, dryer or dishwasher (D944 helper).
|
|
11625
|
+
*
|
|
11626
|
+
* Standby of the control board is under 5 W; a motor or a heater is far
|
|
11627
|
+
* above that, so 5 W is the line. A door lock or a short pump spike is not
|
|
11628
|
+
* a cycle, so running starts only after 30 s above the line.
|
|
11629
|
+
*
|
|
11630
|
+
* A wash, dry or dishwasher pause (soak, heat-wait, heater off between
|
|
11631
|
+
* bursts) lasts several minutes. Ending the cycle on the first dip would
|
|
11632
|
+
* report "finished" in the middle of the program, so the stop dwell is
|
|
11633
|
+
* 10 minutes on every type. The number is stored per type so one machine
|
|
11634
|
+
* can move later without moving the others.
|
|
11635
|
+
*/
|
|
11636
|
+
var APPLIANCE_TEMPLATE_ID = "appliance";
|
|
11637
|
+
var APPLIANCE_KINDS = [
|
|
11638
|
+
"washer",
|
|
11639
|
+
"dryer",
|
|
11640
|
+
"dishwasher"
|
|
11641
|
+
];
|
|
11642
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
11643
|
+
/**
|
|
11644
|
+
* Statistics and integrals over a history read (D944).
|
|
11645
|
+
*
|
|
11646
|
+
* Pure: the same points always give the same answer, so a restart recomputes
|
|
11647
|
+
* rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
|
|
11648
|
+
* never the mean of the samples. A hole with no held value is not integrated
|
|
11649
|
+
* as zero. A truncated read is not published.
|
|
11650
|
+
*/
|
|
11651
|
+
var STATISTIC_STATS = [
|
|
11652
|
+
"min",
|
|
11653
|
+
"max",
|
|
11654
|
+
"mean",
|
|
11655
|
+
"sum",
|
|
11656
|
+
"count",
|
|
11657
|
+
"change"
|
|
11658
|
+
];
|
|
11659
|
+
/**
|
|
11660
|
+
* The range a statistics or integral helper asks history for (D944).
|
|
11661
|
+
*
|
|
11662
|
+
* Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
|
|
11663
|
+
* or 25 hours across a DST change. A tier is eligible only when its retention
|
|
11664
|
+
* is strictly longer than the longest that range can be. Forever qualifies.
|
|
11665
|
+
* Equal to a tier's retention is a refusal, not a short answer.
|
|
11666
|
+
*/
|
|
11667
|
+
var MINUTE_MS = 6e4;
|
|
11668
|
+
var HOUR_MS = 60 * MINUTE_MS;
|
|
11669
|
+
var DAY_MS = 24 * HOUR_MS;
|
|
11670
|
+
var ROLLING_SPANS = [
|
|
11671
|
+
"1h",
|
|
11672
|
+
"24h",
|
|
11673
|
+
"2d",
|
|
11674
|
+
"7d",
|
|
11675
|
+
"30d",
|
|
11676
|
+
"custom"
|
|
11677
|
+
];
|
|
11678
|
+
var CALENDAR_PERIODS = [
|
|
11679
|
+
"today",
|
|
11680
|
+
"yesterday",
|
|
11681
|
+
"this-week",
|
|
11682
|
+
"this-month"
|
|
11683
|
+
];
|
|
11684
|
+
var RESET_PERIODS = [
|
|
11685
|
+
"never",
|
|
11686
|
+
"daily",
|
|
11687
|
+
"weekly",
|
|
11688
|
+
"monthly"
|
|
11689
|
+
];
|
|
11690
|
+
5 * MINUTE_MS;
|
|
11691
|
+
24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
|
|
11692
|
+
7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
|
|
11693
|
+
var rollingWindowObject = object({
|
|
11694
|
+
kind: literal("rolling"),
|
|
11695
|
+
span: _enum(ROLLING_SPANS),
|
|
11696
|
+
customMinutes: number().int().min(5).optional()
|
|
11697
|
+
}).check((payload) => {
|
|
11698
|
+
if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
|
|
11699
|
+
code: "custom",
|
|
11700
|
+
message: "a custom range needs a duration in minutes",
|
|
11701
|
+
path: ["customMinutes"],
|
|
11702
|
+
input: payload.value
|
|
11703
|
+
});
|
|
11704
|
+
if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
|
|
11705
|
+
code: "custom",
|
|
11706
|
+
message: "only a custom range carries a duration",
|
|
11707
|
+
path: ["customMinutes"],
|
|
11708
|
+
input: payload.value
|
|
11709
|
+
});
|
|
11710
|
+
});
|
|
11711
|
+
var RollingWindowSchema = rollingWindowObject;
|
|
11712
|
+
var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
|
|
11713
|
+
kind: literal("calendar"),
|
|
11714
|
+
period: _enum(CALENDAR_PERIODS)
|
|
11715
|
+
})]);
|
|
11716
|
+
/**
|
|
11586
11717
|
* A composition source identity: `(addonId, stableId)`, never a numeric device
|
|
11587
11718
|
* id. Split out so helper schemas can name it without a cycle through
|
|
11588
11719
|
* `composition.ts` (D659).
|
|
@@ -11608,7 +11739,13 @@ var GROUP_TEMPLATE_IDS = [
|
|
|
11608
11739
|
"occupancy",
|
|
11609
11740
|
"binary",
|
|
11610
11741
|
"switch",
|
|
11611
|
-
"light"
|
|
11742
|
+
"light",
|
|
11743
|
+
"fan",
|
|
11744
|
+
"cover",
|
|
11745
|
+
"valve",
|
|
11746
|
+
"lock",
|
|
11747
|
+
"media-player",
|
|
11748
|
+
"climate"
|
|
11612
11749
|
];
|
|
11613
11750
|
var NUMERIC_AGGREGATES = [
|
|
11614
11751
|
"mean",
|
|
@@ -11638,14 +11775,73 @@ var HelperMemberSchema = object({
|
|
|
11638
11775
|
cap: string().min(1),
|
|
11639
11776
|
fieldPath: string().min(1)
|
|
11640
11777
|
});
|
|
11641
|
-
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
11646
|
-
|
|
11647
|
-
|
|
11648
|
-
|
|
11778
|
+
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
11779
|
+
object({
|
|
11780
|
+
kind: literal("group"),
|
|
11781
|
+
templateId: GroupTemplateIdSchema,
|
|
11782
|
+
aggregate: AggregateReducerSchema,
|
|
11783
|
+
members: array(HelperMemberSchema).min(1).max(64),
|
|
11784
|
+
/** Actuator groups only. Readings leave this unset. */
|
|
11785
|
+
active: ActiveCriterionSchema.optional()
|
|
11786
|
+
}),
|
|
11787
|
+
object({
|
|
11788
|
+
kind: literal("appliance"),
|
|
11789
|
+
templateId: literal(APPLIANCE_TEMPLATE_ID),
|
|
11790
|
+
appliance: _enum(APPLIANCE_KINDS),
|
|
11791
|
+
thresholdWatts: number().positive(),
|
|
11792
|
+
startDwellMs: number().int().nonnegative(),
|
|
11793
|
+
stopDwellMs: number().int().nonnegative(),
|
|
11794
|
+
member: HelperMemberSchema
|
|
11795
|
+
}),
|
|
11796
|
+
object({
|
|
11797
|
+
kind: literal("statistic"),
|
|
11798
|
+
stat: _enum(STATISTIC_STATS),
|
|
11799
|
+
window: StatisticWindowSchema,
|
|
11800
|
+
source: HelperMemberSchema
|
|
11801
|
+
}),
|
|
11802
|
+
object({
|
|
11803
|
+
kind: literal("integral"),
|
|
11804
|
+
reset: _enum(RESET_PERIODS),
|
|
11805
|
+
output: _enum(["energy", "meter"]),
|
|
11806
|
+
source: HelperMemberSchema
|
|
11807
|
+
}),
|
|
11808
|
+
object({
|
|
11809
|
+
kind: literal("threshold"),
|
|
11810
|
+
source: HelperMemberSchema,
|
|
11811
|
+
high: number(),
|
|
11812
|
+
low: number(),
|
|
11813
|
+
onForMs: number().int().nonnegative(),
|
|
11814
|
+
offForMs: number().int().nonnegative()
|
|
11815
|
+
}),
|
|
11816
|
+
object({
|
|
11817
|
+
kind: literal("arithmetic"),
|
|
11818
|
+
op: _enum([
|
|
11819
|
+
"sum",
|
|
11820
|
+
"difference",
|
|
11821
|
+
"ratio"
|
|
11822
|
+
]),
|
|
11823
|
+
inputs: array(HelperMemberSchema).min(2).max(64)
|
|
11824
|
+
}),
|
|
11825
|
+
object({
|
|
11826
|
+
kind: literal("expression"),
|
|
11827
|
+
expr: string().min(1),
|
|
11828
|
+
inputs: array(object({
|
|
11829
|
+
name: string().min(1),
|
|
11830
|
+
member: HelperMemberSchema
|
|
11831
|
+
})).min(1).max(64),
|
|
11832
|
+
output: _enum(["number", "boolean"])
|
|
11833
|
+
}),
|
|
11834
|
+
object({
|
|
11835
|
+
kind: literal("derivative"),
|
|
11836
|
+
window: RollingWindowSchema,
|
|
11837
|
+
per: _enum([
|
|
11838
|
+
"second",
|
|
11839
|
+
"minute",
|
|
11840
|
+
"hour"
|
|
11841
|
+
]),
|
|
11842
|
+
source: HelperMemberSchema
|
|
11843
|
+
})
|
|
11844
|
+
]);
|
|
11649
11845
|
/** A composed device's stableId is this prefix plus the block id. */
|
|
11650
11846
|
var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
|
|
11651
11847
|
/** Longest snippet a `code` source may carry (slice 4). */
|
|
@@ -11706,6 +11902,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
11706
11902
|
"all"
|
|
11707
11903
|
]),
|
|
11708
11904
|
reads: array(CompositionFieldReadSchema).min(1).max(64),
|
|
11905
|
+
/**
|
|
11906
|
+
* Set when the reads are an enum state, not a yes/no: the listed states
|
|
11907
|
+
* count as active (a cover is `open`). A state outside the list is inactive;
|
|
11908
|
+
* a read that is not a state at all is left out. Only `any` / `all` use it.
|
|
11909
|
+
*/
|
|
11910
|
+
activeWhen: array(string().min(1)).min(1).optional(),
|
|
11709
11911
|
/** `value` is the reading; the others are the member counts. */
|
|
11710
11912
|
publish: _enum([
|
|
11711
11913
|
"value",
|
|
@@ -11719,6 +11921,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
11719
11921
|
"omit",
|
|
11720
11922
|
"offline"
|
|
11721
11923
|
])
|
|
11924
|
+
}),
|
|
11925
|
+
object({
|
|
11926
|
+
kind: literal("series"),
|
|
11927
|
+
read: CompositionFieldReadSchema,
|
|
11928
|
+
/** `value` is the statistic or the integral. The others are the gap counts. */
|
|
11929
|
+
publish: _enum([
|
|
11930
|
+
"value",
|
|
11931
|
+
"included",
|
|
11932
|
+
"excluded",
|
|
11933
|
+
"total"
|
|
11934
|
+
]),
|
|
11935
|
+
/**
|
|
11936
|
+
* What an unanswered `value` does. Counts ignore this. `offline` leaves the
|
|
11937
|
+
* device unseeded until history answers, so a restart cannot publish 0.
|
|
11938
|
+
*/
|
|
11939
|
+
empty: _enum([
|
|
11940
|
+
"null",
|
|
11941
|
+
"omit",
|
|
11942
|
+
"offline"
|
|
11943
|
+
])
|
|
11722
11944
|
})
|
|
11723
11945
|
]);
|
|
11724
11946
|
var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
|
|
@@ -15156,6 +15378,14 @@ var AutomationConditionLeafSchema = object({
|
|
|
15156
15378
|
array(union([string(), number()]))
|
|
15157
15379
|
])
|
|
15158
15380
|
});
|
|
15381
|
+
var AutomationConditionStationaryPresentSchema = object({
|
|
15382
|
+
kind: literal("stationary-present"),
|
|
15383
|
+
deviceId: number().int().nonnegative(),
|
|
15384
|
+
/** Class names; a macro class matches every raw class that maps to it. */
|
|
15385
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
15386
|
+
/** The parked object must be in at least one of these zones. */
|
|
15387
|
+
zones: array(string().min(1)).min(1).optional()
|
|
15388
|
+
});
|
|
15159
15389
|
/**
|
|
15160
15390
|
* The expression leaf, declared as a plain object rather than an intersection
|
|
15161
15391
|
* with {@link ExpressionSourceSchema}: a discriminated union has to be able to
|
|
@@ -15189,7 +15419,8 @@ var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
|
|
|
15189
15419
|
child: AutomationConditionSchema
|
|
15190
15420
|
}),
|
|
15191
15421
|
AutomationConditionLeafSchema,
|
|
15192
|
-
AutomationConditionExpressionSchema
|
|
15422
|
+
AutomationConditionExpressionSchema,
|
|
15423
|
+
AutomationConditionStationaryPresentSchema
|
|
15193
15424
|
]));
|
|
15194
15425
|
/**
|
|
15195
15426
|
* What starts a run.
|
|
@@ -15230,7 +15461,34 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
15230
15461
|
kind: literal("schedule"),
|
|
15231
15462
|
cron: string().min(1).max(120)
|
|
15232
15463
|
}),
|
|
15233
|
-
object({ kind: literal("manual") })
|
|
15464
|
+
object({ kind: literal("manual") }),
|
|
15465
|
+
object({
|
|
15466
|
+
kind: literal("detection"),
|
|
15467
|
+
/** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
|
|
15468
|
+
* matches every raw class that maps to it; any other name matches
|
|
15469
|
+
* exactly. */
|
|
15470
|
+
classes: array(string().min(1)).min(1),
|
|
15471
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
15472
|
+
/** The track must have visited at least one of these zones. */
|
|
15473
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
15474
|
+
/** Best-so-far confidence the track must reach. */
|
|
15475
|
+
minScore: number().min(0).max(1).optional()
|
|
15476
|
+
}),
|
|
15477
|
+
object({
|
|
15478
|
+
kind: literal("stationary"),
|
|
15479
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
15480
|
+
/** Class names; a macro class matches like the detection trigger. */
|
|
15481
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
15482
|
+
/** The parked object must be in at least one of these zones. */
|
|
15483
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
15484
|
+
/** `appeared`: it became parked. `departed`: the registry retired it.
|
|
15485
|
+
* The vocabulary of `PipelineAnalyticsStationaryChanged`. */
|
|
15486
|
+
phase: _enum(["appeared", "departed"])
|
|
15487
|
+
}),
|
|
15488
|
+
object({
|
|
15489
|
+
kind: literal("interval"),
|
|
15490
|
+
minutes: number().int().min(1).max(1440)
|
|
15491
|
+
})
|
|
15234
15492
|
]);
|
|
15235
15493
|
/**
|
|
15236
15494
|
* One action step.
|
|
@@ -15250,28 +15508,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
15250
15508
|
* is called. The runner still receives a number and is untouched; the
|
|
15251
15509
|
* resolution is the recipe's job, not the runner's.
|
|
15252
15510
|
*/
|
|
15511
|
+
var AutomationWaitStepSchema = object({
|
|
15512
|
+
kind: literal("wait"),
|
|
15513
|
+
seconds: number().min(0).max(300)
|
|
15514
|
+
});
|
|
15515
|
+
var AutomationCapStepSchema = object({
|
|
15516
|
+
kind: literal("cap"),
|
|
15517
|
+
/**
|
|
15518
|
+
* One device, several same-family devices (locks / climate family / …),
|
|
15519
|
+
* or `{ $var }` for the device that triggered the run. An array is
|
|
15520
|
+
* fan-out at codegen — each id gets the same cap.method call.
|
|
15521
|
+
*/
|
|
15522
|
+
deviceId: union([
|
|
15523
|
+
number().int(),
|
|
15524
|
+
array(number().int()).min(1),
|
|
15525
|
+
object({ $var: string().min(1) })
|
|
15526
|
+
]),
|
|
15527
|
+
cap: string().min(1),
|
|
15528
|
+
method: string().min(1),
|
|
15529
|
+
/** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
|
|
15530
|
+
* SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
|
|
15531
|
+
* the expression leaf. */
|
|
15532
|
+
args: record(string(), unknown()).optional()
|
|
15533
|
+
});
|
|
15534
|
+
/**
|
|
15535
|
+
* The steps an internal Action may contain: cap and wait, and nothing that can
|
|
15536
|
+
* call back into an Action — so an Action can never reach itself (no cycles).
|
|
15537
|
+
*/
|
|
15538
|
+
var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
|
|
15253
15539
|
var AutomationActionSchema = discriminatedUnion("kind", [
|
|
15540
|
+
AutomationWaitStepSchema,
|
|
15541
|
+
AutomationCapStepSchema,
|
|
15254
15542
|
object({
|
|
15255
|
-
kind: literal("
|
|
15256
|
-
|
|
15257
|
-
|
|
15258
|
-
|
|
15259
|
-
|
|
15260
|
-
|
|
15261
|
-
|
|
15262
|
-
|
|
15263
|
-
|
|
15264
|
-
*/
|
|
15265
|
-
deviceId: union([
|
|
15266
|
-
number().int(),
|
|
15267
|
-
array(number().int()).min(1),
|
|
15268
|
-
object({ $var: string().min(1) })
|
|
15269
|
-
]),
|
|
15270
|
-
cap: string().min(1),
|
|
15271
|
-
method: string().min(1),
|
|
15272
|
-
/** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
|
|
15273
|
-
* evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
|
|
15274
|
-
args: record(string(), unknown()).optional()
|
|
15543
|
+
kind: literal("action"),
|
|
15544
|
+
/** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
|
|
15545
|
+
actionId: string().min(1),
|
|
15546
|
+
/** Values for the action's declared variables; the others take their default. */
|
|
15547
|
+
variables: record(string(), union([
|
|
15548
|
+
string(),
|
|
15549
|
+
number(),
|
|
15550
|
+
boolean()
|
|
15551
|
+
])).optional()
|
|
15275
15552
|
}),
|
|
15276
15553
|
object({
|
|
15277
15554
|
kind: literal("code"),
|
|
@@ -15280,10 +15557,357 @@ var AutomationActionSchema = discriminatedUnion("kind", [
|
|
|
15280
15557
|
code: string().min(1).max(2e4)
|
|
15281
15558
|
})
|
|
15282
15559
|
]);
|
|
15560
|
+
/**
|
|
15561
|
+
* How a trigger that arrives while the automation is busy (or cooling down) is
|
|
15562
|
+
* handled. One of the automation's execution modes, never a property of its
|
|
15563
|
+
* actions. Absent means `single`.
|
|
15564
|
+
*
|
|
15565
|
+
* - `single` — drop the trigger while a run is in progress (traced + logged).
|
|
15566
|
+
* - `restart` — abort the run in progress (at its next step boundary), start fresh.
|
|
15567
|
+
* - `queued` — queue up to `max` triggers, drop the rest (traced).
|
|
15568
|
+
* - `parallel` — up to `max` runs at once, drop the rest (traced).
|
|
15569
|
+
* - `cooldown` — `single`, and no new run until `minutes` after the last run
|
|
15570
|
+
* STARTED; persisted across a restart. A manual trigger bypasses only this
|
|
15571
|
+
* wait, never the single/queued limits.
|
|
15572
|
+
*/
|
|
15573
|
+
var AutomationRunModeSchema = discriminatedUnion("mode", [
|
|
15574
|
+
object({ mode: literal("single") }),
|
|
15575
|
+
object({ mode: literal("restart") }),
|
|
15576
|
+
object({
|
|
15577
|
+
mode: literal("queued"),
|
|
15578
|
+
max: number().int().min(1).max(10)
|
|
15579
|
+
}),
|
|
15580
|
+
object({
|
|
15581
|
+
mode: literal("parallel"),
|
|
15582
|
+
max: number().int().min(1).max(10)
|
|
15583
|
+
}),
|
|
15584
|
+
object({
|
|
15585
|
+
mode: literal("cooldown"),
|
|
15586
|
+
minutes: number().int().min(1).max(1440)
|
|
15587
|
+
})
|
|
15588
|
+
]);
|
|
15283
15589
|
var AutomationRecipeSchema = object({
|
|
15284
15590
|
triggers: array(AutomationTriggerSchema),
|
|
15285
15591
|
conditions: AutomationConditionSchema.optional(),
|
|
15286
|
-
actions: array(AutomationActionSchema)
|
|
15592
|
+
actions: array(AutomationActionSchema),
|
|
15593
|
+
runMode: AutomationRunModeSchema.optional(),
|
|
15594
|
+
/** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
|
|
15595
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
15596
|
+
});
|
|
15597
|
+
new Set([
|
|
15598
|
+
{
|
|
15599
|
+
id: "person",
|
|
15600
|
+
name: "Person"
|
|
15601
|
+
},
|
|
15602
|
+
{
|
|
15603
|
+
id: "vehicle",
|
|
15604
|
+
name: "Vehicle"
|
|
15605
|
+
},
|
|
15606
|
+
{
|
|
15607
|
+
id: "animal",
|
|
15608
|
+
name: "Animal"
|
|
15609
|
+
},
|
|
15610
|
+
{
|
|
15611
|
+
id: "package",
|
|
15612
|
+
name: "Package"
|
|
15613
|
+
}
|
|
15614
|
+
].map((l) => l.id));
|
|
15615
|
+
var COCO_TO_MACRO = {
|
|
15616
|
+
mapping: {
|
|
15617
|
+
person: "person",
|
|
15618
|
+
bicycle: "vehicle",
|
|
15619
|
+
car: "vehicle",
|
|
15620
|
+
motorcycle: "vehicle",
|
|
15621
|
+
airplane: "vehicle",
|
|
15622
|
+
bus: "vehicle",
|
|
15623
|
+
train: "vehicle",
|
|
15624
|
+
truck: "vehicle",
|
|
15625
|
+
boat: "vehicle",
|
|
15626
|
+
bird: "animal",
|
|
15627
|
+
cat: "animal",
|
|
15628
|
+
dog: "animal",
|
|
15629
|
+
horse: "animal",
|
|
15630
|
+
sheep: "animal",
|
|
15631
|
+
cow: "animal",
|
|
15632
|
+
elephant: "animal",
|
|
15633
|
+
bear: "animal",
|
|
15634
|
+
zebra: "animal",
|
|
15635
|
+
giraffe: "animal",
|
|
15636
|
+
suitcase: "package",
|
|
15637
|
+
backpack: "package",
|
|
15638
|
+
handbag: "package"
|
|
15639
|
+
},
|
|
15640
|
+
preserveOriginal: false
|
|
15641
|
+
};
|
|
15642
|
+
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
15643
|
+
/**
|
|
15644
|
+
* Run traces — what an automation (or, later, an Action run) DID, node by node,
|
|
15645
|
+
* so the editor can paint every block green or red with its reason.
|
|
15646
|
+
*
|
|
15647
|
+
* A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
|
|
15648
|
+
* every condition node (containers and leaves, including the ones a
|
|
15649
|
+
* short-circuit never reached), the run-mode verdict and every action step —
|
|
15650
|
+
* each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
|
|
15651
|
+
* for an Action run). Skipped
|
|
15652
|
+
* evaluations are traced too, and identical consecutive skips collapse into a
|
|
15653
|
+
* repeat count so a trigger that fails every 5 minutes cannot flush real runs.
|
|
15654
|
+
*
|
|
15655
|
+
* {@link AutomationStepTraceSchema} is deliberately free of automation
|
|
15656
|
+
* vocabulary so an Action run records the same step shape.
|
|
15657
|
+
*/
|
|
15658
|
+
/** A value a trace may show: JSON primitives and short lists, nothing deeper. */
|
|
15659
|
+
var TraceValueSchema = union([
|
|
15660
|
+
string(),
|
|
15661
|
+
number(),
|
|
15662
|
+
boolean(),
|
|
15663
|
+
_null(),
|
|
15664
|
+
array(union([string(), number()]))
|
|
15665
|
+
]);
|
|
15666
|
+
var AutomationRunOutcomeSchema = _enum([
|
|
15667
|
+
"ran",
|
|
15668
|
+
"skipped-trigger",
|
|
15669
|
+
"skipped-conditions",
|
|
15670
|
+
"skipped-cooldown",
|
|
15671
|
+
"skipped-overlap",
|
|
15672
|
+
"failed",
|
|
15673
|
+
"aborted"
|
|
15674
|
+
]);
|
|
15675
|
+
/** `not-evaluated` = a short-circuit never reached the node. */
|
|
15676
|
+
var TraceNodeOutcomeSchema = _enum([
|
|
15677
|
+
"pass",
|
|
15678
|
+
"fail",
|
|
15679
|
+
"not-evaluated"
|
|
15680
|
+
]);
|
|
15681
|
+
var AutomationTriggerTraceSchema = object({
|
|
15682
|
+
kind: string().min(1),
|
|
15683
|
+
outcome: _enum(["pass", "fail"]),
|
|
15684
|
+
/** Why an event was rejected ("class person not in [dog]") or what fired. */
|
|
15685
|
+
reason: string(),
|
|
15686
|
+
/** deviceId, className, trackId, score … — only what the trigger exposes. */
|
|
15687
|
+
vars: record(string(), TraceValueSchema)
|
|
15688
|
+
});
|
|
15689
|
+
var AutomationConditionTraceNodeSchema = object({
|
|
15690
|
+
/** `conditions` for the root, `conditions.<i>` for its i-th child, … */
|
|
15691
|
+
path: string().min(1),
|
|
15692
|
+
kind: _enum([
|
|
15693
|
+
"all",
|
|
15694
|
+
"any",
|
|
15695
|
+
"not",
|
|
15696
|
+
"condition",
|
|
15697
|
+
"expression",
|
|
15698
|
+
"stationary-present"
|
|
15699
|
+
]),
|
|
15700
|
+
outcome: TraceNodeOutcomeSchema,
|
|
15701
|
+
reason: string(),
|
|
15702
|
+
deviceId: number().int().optional(),
|
|
15703
|
+
cap: string().optional(),
|
|
15704
|
+
fieldPath: string().optional(),
|
|
15705
|
+
operator: string().optional(),
|
|
15706
|
+
expected: TraceValueSchema.optional(),
|
|
15707
|
+
/** What the mirror held; `null` when it held nothing (unknown). */
|
|
15708
|
+
observed: TraceValueSchema.optional()
|
|
15709
|
+
});
|
|
15710
|
+
var AutomationRunVerdictSchema = object({
|
|
15711
|
+
mode: AutomationRunModeSchema,
|
|
15712
|
+
decision: _enum([
|
|
15713
|
+
"run",
|
|
15714
|
+
"queued",
|
|
15715
|
+
"restart",
|
|
15716
|
+
"drop",
|
|
15717
|
+
"cooldown",
|
|
15718
|
+
"bypass"
|
|
15719
|
+
]),
|
|
15720
|
+
reason: string()
|
|
15721
|
+
});
|
|
15722
|
+
/** One executed (or not-run) step — shared by automations and Action runs. */
|
|
15723
|
+
var AutomationStepTraceSchema = object({
|
|
15724
|
+
/** `actions.<i>` */
|
|
15725
|
+
path: string().min(1),
|
|
15726
|
+
kind: string().min(1),
|
|
15727
|
+
cap: string().optional(),
|
|
15728
|
+
method: string().optional(),
|
|
15729
|
+
deviceId: number().int().optional(),
|
|
15730
|
+
startedAt: number().optional(),
|
|
15731
|
+
endedAt: number().optional(),
|
|
15732
|
+
outcome: _enum([
|
|
15733
|
+
"ok",
|
|
15734
|
+
"error",
|
|
15735
|
+
"not-run"
|
|
15736
|
+
]),
|
|
15737
|
+
message: string().optional()
|
|
15738
|
+
});
|
|
15739
|
+
var AutomationRunTraceSchema = object({
|
|
15740
|
+
runId: string().min(1),
|
|
15741
|
+
startedAt: number(),
|
|
15742
|
+
endedAt: number(),
|
|
15743
|
+
trigger: AutomationTriggerTraceSchema,
|
|
15744
|
+
conditions: array(AutomationConditionTraceNodeSchema),
|
|
15745
|
+
verdict: AutomationRunVerdictSchema.optional(),
|
|
15746
|
+
steps: array(AutomationStepTraceSchema),
|
|
15747
|
+
outcome: AutomationRunOutcomeSchema,
|
|
15748
|
+
/** How many identical consecutive skips this trace stands for (>= 1). */
|
|
15749
|
+
repeat: number().int().min(1),
|
|
15750
|
+
/** When the latest of those repeats happened. */
|
|
15751
|
+
lastAt: number()
|
|
15752
|
+
});
|
|
15753
|
+
/**
|
|
15754
|
+
* What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
|
|
15755
|
+
* `action:<actionId>`): the cooldown stamp and the trace ring.
|
|
15756
|
+
*/
|
|
15757
|
+
var AutomationStateDocSchema = object({
|
|
15758
|
+
version: literal(1),
|
|
15759
|
+
lastRunStartedAt: number().nullable(),
|
|
15760
|
+
traces: array(AutomationRunTraceSchema)
|
|
15761
|
+
});
|
|
15762
|
+
/**
|
|
15763
|
+
* Actions — named, runnable things an operator (or an automation) can start,
|
|
15764
|
+
* declared as LIST ENTRIES, never as devices.
|
|
15765
|
+
*
|
|
15766
|
+
* An entry is either **internal** (a short list of cap/wait steps kept in the
|
|
15767
|
+
* hub's `actions` record kind) or declared by an addon through the
|
|
15768
|
+
* `action-source` collection (a Home Assistant script, a scene …). The one
|
|
15769
|
+
* surface the UI and automations talk to is the `actions` cap; it keeps no
|
|
15770
|
+
* catalog of its own (D3) — every `list` asks the providers live.
|
|
15771
|
+
*
|
|
15772
|
+
* This file is the data half: the entry schema, the variables an entry may
|
|
15773
|
+
* declare, and the pure resolution of the values a run was given. It holds no
|
|
15774
|
+
* runtime.
|
|
15775
|
+
*/
|
|
15776
|
+
/** What a variable's value is. `select` takes one of `options`. */
|
|
15777
|
+
var ActionVariableTypeSchema = _enum([
|
|
15778
|
+
"string",
|
|
15779
|
+
"number",
|
|
15780
|
+
"boolean",
|
|
15781
|
+
"select"
|
|
15782
|
+
]);
|
|
15783
|
+
var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
15784
|
+
var ActionVariableValueSchema = union([
|
|
15785
|
+
string(),
|
|
15786
|
+
number(),
|
|
15787
|
+
boolean()
|
|
15788
|
+
]);
|
|
15789
|
+
/**
|
|
15790
|
+
* One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
|
|
15791
|
+
* value) or `{{vars.name}}` (inside a string) in their args.
|
|
15792
|
+
*/
|
|
15793
|
+
var ActionVariableSchema = object({
|
|
15794
|
+
name: string().regex(ACTION_VARIABLE_NAME_RE),
|
|
15795
|
+
label: string().min(1).max(120).optional(),
|
|
15796
|
+
type: ActionVariableTypeSchema,
|
|
15797
|
+
required: boolean().optional(),
|
|
15798
|
+
default: ActionVariableValueSchema.optional(),
|
|
15799
|
+
/** The allowed values of a `select`. */
|
|
15800
|
+
options: array(string().min(1)).min(1).optional()
|
|
15801
|
+
}).superRefine((v, ctx) => {
|
|
15802
|
+
if (v.type === "select" && v.options === void 0) ctx.addIssue({
|
|
15803
|
+
code: "custom",
|
|
15804
|
+
message: "a select variable needs options",
|
|
15805
|
+
path: ["options"]
|
|
15806
|
+
});
|
|
15807
|
+
if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
|
|
15808
|
+
code: "custom",
|
|
15809
|
+
message: variableValueError(v, v.default) ?? "invalid default",
|
|
15810
|
+
path: ["default"]
|
|
15811
|
+
});
|
|
15812
|
+
});
|
|
15813
|
+
/** Why `value` is not a legal value for the variable, or null when it is. */
|
|
15814
|
+
function variableValueError(variable, value) {
|
|
15815
|
+
switch (variable.type) {
|
|
15816
|
+
case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
|
|
15817
|
+
case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
|
|
15818
|
+
case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
|
|
15819
|
+
case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
|
|
15820
|
+
}
|
|
15821
|
+
}
|
|
15822
|
+
var ActionSourceKindSchema = _enum(["internal", "addon"]);
|
|
15823
|
+
function isInternalActionId(actionId) {
|
|
15824
|
+
return actionId.startsWith("internal:") && actionId.length > 9;
|
|
15825
|
+
}
|
|
15826
|
+
/** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
|
|
15827
|
+
function addonIdOfActionId(actionId) {
|
|
15828
|
+
if (isInternalActionId(actionId)) return null;
|
|
15829
|
+
const at = actionId.indexOf(":");
|
|
15830
|
+
return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
|
|
15831
|
+
}
|
|
15832
|
+
var ActionStatusSchema = object({
|
|
15833
|
+
isRunning: boolean(),
|
|
15834
|
+
/** Ms epoch the latest run STARTED; null when it never ran. */
|
|
15835
|
+
lastRunAt: number().nullable(),
|
|
15836
|
+
/** The latest run's failure; null on success or when it never ran. */
|
|
15837
|
+
lastError: string().nullable()
|
|
15838
|
+
});
|
|
15839
|
+
var ActionEntrySchema = object({
|
|
15840
|
+
/** `internal:<uuid>` or `<addonId>:<localId>`. */
|
|
15841
|
+
id: string().min(3),
|
|
15842
|
+
name: string().min(1).max(120),
|
|
15843
|
+
/** The addon that declared it; `core-blocks` for an internal one. */
|
|
15844
|
+
addonId: string().min(1),
|
|
15845
|
+
source: ActionSourceKindSchema,
|
|
15846
|
+
/** An addon-declared entry is edited where it was declared. */
|
|
15847
|
+
readOnly: boolean(),
|
|
15848
|
+
/** Internal only: cap and wait steps. No action-in-action, so no cycles. */
|
|
15849
|
+
steps: array(ActionStepSchema).min(1).max(50).optional(),
|
|
15850
|
+
variables: array(ActionVariableSchema).max(20).optional(),
|
|
15851
|
+
status: ActionStatusSchema
|
|
15852
|
+
}).superRefine((entry, ctx) => {
|
|
15853
|
+
if (entry.source === "internal") {
|
|
15854
|
+
if (!isInternalActionId(entry.id)) ctx.addIssue({
|
|
15855
|
+
code: "custom",
|
|
15856
|
+
message: "an internal action id is internal:<uuid>",
|
|
15857
|
+
path: ["id"]
|
|
15858
|
+
});
|
|
15859
|
+
return;
|
|
15860
|
+
}
|
|
15861
|
+
if (entry.steps !== void 0) ctx.addIssue({
|
|
15862
|
+
code: "custom",
|
|
15863
|
+
message: "only an internal action carries steps",
|
|
15864
|
+
path: ["steps"]
|
|
15865
|
+
});
|
|
15866
|
+
if (!entry.readOnly) ctx.addIssue({
|
|
15867
|
+
code: "custom",
|
|
15868
|
+
message: "an addon action is read-only here",
|
|
15869
|
+
path: ["readOnly"]
|
|
15870
|
+
});
|
|
15871
|
+
if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
|
|
15872
|
+
code: "custom",
|
|
15873
|
+
message: "an addon action id is <addonId>:<localId>",
|
|
15874
|
+
path: ["id"]
|
|
15875
|
+
});
|
|
15876
|
+
});
|
|
15877
|
+
/** What an operator writes for an internal action. */
|
|
15878
|
+
var ActionInputSchema = object({
|
|
15879
|
+
name: string().min(1).max(120),
|
|
15880
|
+
steps: array(ActionStepSchema).min(1).max(50),
|
|
15881
|
+
variables: array(ActionVariableSchema).max(20).optional()
|
|
15882
|
+
});
|
|
15883
|
+
/** Who started a run. Absent = the operator pressed it. */
|
|
15884
|
+
var ActionRunOriginSchema = object({
|
|
15885
|
+
/** The automation block whose step started it. */
|
|
15886
|
+
blockId: string().min(1).optional() });
|
|
15887
|
+
/** `started` = the caller did not ask to wait. */
|
|
15888
|
+
var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
|
|
15889
|
+
var ActionRunResultSchema = object({
|
|
15890
|
+
runId: string().min(1),
|
|
15891
|
+
outcome: ActionRunOutcomeSchema,
|
|
15892
|
+
/** Why it did not run, or what failed. */
|
|
15893
|
+
message: string().optional()
|
|
15894
|
+
});
|
|
15895
|
+
_enum([
|
|
15896
|
+
"not-found",
|
|
15897
|
+
"read-only",
|
|
15898
|
+
"invalid-variables",
|
|
15899
|
+
"invalid-steps",
|
|
15900
|
+
"not-linked",
|
|
15901
|
+
"busy"
|
|
15902
|
+
]);
|
|
15903
|
+
object({
|
|
15904
|
+
actionId: string(),
|
|
15905
|
+
runId: string(),
|
|
15906
|
+
isRunning: boolean(),
|
|
15907
|
+
/** Set when the run ended. */
|
|
15908
|
+
outcome: AutomationRunOutcomeSchema.optional(),
|
|
15909
|
+
lastRunAt: number().nullable(),
|
|
15910
|
+
lastError: string().nullable()
|
|
15287
15911
|
});
|
|
15288
15912
|
/**
|
|
15289
15913
|
* `addon-pages` — system-scoped singleton aggregator cap. Public-facing
|
|
@@ -16878,7 +17502,22 @@ var BrokerProviderInfoSchema = object({
|
|
|
16878
17502
|
*/
|
|
16879
17503
|
interactiveConnectPath: string().min(1).optional(),
|
|
16880
17504
|
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
16881
|
-
interactiveDisconnectPath: string().min(1).optional()
|
|
17505
|
+
interactiveDisconnectPath: string().min(1).optional(),
|
|
17506
|
+
/**
|
|
17507
|
+
* The kind signs in through a server-held interactive session instead of
|
|
17508
|
+
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
17509
|
+
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
17510
|
+
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
17511
|
+
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
17512
|
+
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
17513
|
+
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
17514
|
+
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
17515
|
+
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
17516
|
+
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
17517
|
+
* offers it as the default path; the fields the session fills are
|
|
17518
|
+
* marked `filledBySignIn` in the kind's settings schema.
|
|
17519
|
+
*/
|
|
17520
|
+
signIn: object({ basePath: string().min(1) }).optional()
|
|
16882
17521
|
}))
|
|
16883
17522
|
});
|
|
16884
17523
|
var ListInputSchema = object({
|
|
@@ -17955,7 +18594,7 @@ var CompositionEventPlanSchema = object({
|
|
|
17955
18594
|
stateField: string(),
|
|
17956
18595
|
fieldKey: string()
|
|
17957
18596
|
});
|
|
17958
|
-
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.
|
|
18597
|
+
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
|
|
17959
18598
|
var CompositionProblemSchema = object({
|
|
17960
18599
|
code: _enum([
|
|
17961
18600
|
"reserved-variant",
|
|
@@ -17983,7 +18622,8 @@ var CompositionProblemSchema = object({
|
|
|
17983
18622
|
"native-capability-replace",
|
|
17984
18623
|
"mixed-quantity",
|
|
17985
18624
|
"helper-mismatch",
|
|
17986
|
-
"member-constraint"
|
|
18625
|
+
"member-constraint",
|
|
18626
|
+
"retention"
|
|
17987
18627
|
]),
|
|
17988
18628
|
path: string(),
|
|
17989
18629
|
message: string()
|
|
@@ -18217,13 +18857,17 @@ Object.freeze({
|
|
|
18217
18857
|
latchedAt: {
|
|
18218
18858
|
kind: "fixed",
|
|
18219
18859
|
result: ["number", "null"]
|
|
18860
|
+
},
|
|
18861
|
+
schmitt: {
|
|
18862
|
+
kind: "fixed",
|
|
18863
|
+
result: ["boolean", "null"]
|
|
18220
18864
|
}
|
|
18221
18865
|
});
|
|
18222
18866
|
/**
|
|
18223
18867
|
* One catalog row per base reading a group helper can build (D944 §3).
|
|
18224
18868
|
* Data, not a form default written into the block.
|
|
18225
18869
|
*/
|
|
18226
|
-
var NOW = {
|
|
18870
|
+
var NOW$2 = {
|
|
18227
18871
|
fieldPath: "lastFetchedAt",
|
|
18228
18872
|
expr: "now"
|
|
18229
18873
|
};
|
|
@@ -18237,6 +18881,8 @@ function numeric(input) {
|
|
|
18237
18881
|
family: "numeric",
|
|
18238
18882
|
deviceType: DeviceType.Sensor,
|
|
18239
18883
|
memberDeviceType: DeviceType.Sensor,
|
|
18884
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
18885
|
+
activeWhen: void 0,
|
|
18240
18886
|
commandCaps: [],
|
|
18241
18887
|
baseUnit: baseUnitOf(input.quantity),
|
|
18242
18888
|
aggregates: NUMERIC_AGGREGATES,
|
|
@@ -18252,6 +18898,8 @@ function binary(input) {
|
|
|
18252
18898
|
family: "binary",
|
|
18253
18899
|
deviceType: DeviceType.Sensor,
|
|
18254
18900
|
memberDeviceType: DeviceType.Sensor,
|
|
18901
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
18902
|
+
activeWhen: void 0,
|
|
18255
18903
|
commandCaps: [],
|
|
18256
18904
|
quantity: "none",
|
|
18257
18905
|
baseUnit: void 0,
|
|
@@ -18265,73 +18913,73 @@ var GROUP_TEMPLATES = [
|
|
|
18265
18913
|
summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
|
|
18266
18914
|
role: DeviceRole.TemperatureSensor,
|
|
18267
18915
|
outputCap: "temperature-sensor",
|
|
18268
|
-
outputField: "
|
|
18916
|
+
outputField: "temperature",
|
|
18269
18917
|
quantity: "temperature",
|
|
18270
18918
|
defaultAggregate: "mean",
|
|
18271
18919
|
empty: "null",
|
|
18272
|
-
companions: [NOW]
|
|
18920
|
+
companions: [NOW$2]
|
|
18273
18921
|
}),
|
|
18274
18922
|
numeric({
|
|
18275
18923
|
id: "humidity",
|
|
18276
18924
|
summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18277
18925
|
role: DeviceRole.HumiditySensor,
|
|
18278
18926
|
outputCap: "humidity-sensor",
|
|
18279
|
-
outputField: "
|
|
18927
|
+
outputField: "humidity",
|
|
18280
18928
|
quantity: "humidity",
|
|
18281
18929
|
defaultAggregate: "mean",
|
|
18282
18930
|
empty: "null",
|
|
18283
|
-
companions: [NOW]
|
|
18931
|
+
companions: [NOW$2]
|
|
18284
18932
|
}),
|
|
18285
18933
|
numeric({
|
|
18286
18934
|
id: "pressure",
|
|
18287
18935
|
summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
|
|
18288
18936
|
role: DeviceRole.PressureSensor,
|
|
18289
18937
|
outputCap: "pressure-sensor",
|
|
18290
|
-
outputField: "
|
|
18938
|
+
outputField: "pressure",
|
|
18291
18939
|
quantity: "pressure",
|
|
18292
18940
|
defaultAggregate: "mean",
|
|
18293
18941
|
empty: "null",
|
|
18294
|
-
companions: [NOW]
|
|
18942
|
+
companions: [NOW$2]
|
|
18295
18943
|
}),
|
|
18296
18944
|
numeric({
|
|
18297
18945
|
id: "illuminance",
|
|
18298
18946
|
summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
|
|
18299
18947
|
role: DeviceRole.AmbientLightSensor,
|
|
18300
18948
|
outputCap: "ambient-light-sensor",
|
|
18301
|
-
outputField: "
|
|
18949
|
+
outputField: "illuminance",
|
|
18302
18950
|
quantity: "illuminance",
|
|
18303
18951
|
defaultAggregate: "mean",
|
|
18304
18952
|
empty: "null",
|
|
18305
|
-
companions: [NOW]
|
|
18953
|
+
companions: [NOW$2]
|
|
18306
18954
|
}),
|
|
18307
18955
|
numeric({
|
|
18308
18956
|
id: "power",
|
|
18309
18957
|
summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
|
|
18310
18958
|
role: DeviceRole.PowerSensor,
|
|
18311
18959
|
outputCap: "power-meter",
|
|
18312
|
-
outputField: "
|
|
18960
|
+
outputField: "power",
|
|
18313
18961
|
quantity: "power",
|
|
18314
18962
|
defaultAggregate: "sum",
|
|
18315
18963
|
empty: "omit",
|
|
18316
|
-
companions: [NOW]
|
|
18964
|
+
companions: [NOW$2]
|
|
18317
18965
|
}),
|
|
18318
18966
|
numeric({
|
|
18319
18967
|
id: "energy",
|
|
18320
18968
|
summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
|
|
18321
18969
|
role: DeviceRole.EnergySensor,
|
|
18322
18970
|
outputCap: "power-meter",
|
|
18323
|
-
outputField: "
|
|
18971
|
+
outputField: "energy",
|
|
18324
18972
|
quantity: "energy",
|
|
18325
18973
|
defaultAggregate: "sum",
|
|
18326
18974
|
empty: "omit",
|
|
18327
|
-
companions: [NOW]
|
|
18975
|
+
companions: [NOW$2]
|
|
18328
18976
|
}),
|
|
18329
18977
|
numeric({
|
|
18330
18978
|
id: "battery",
|
|
18331
18979
|
summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18332
18980
|
role: DeviceRole.BatterySensor,
|
|
18333
18981
|
outputCap: "battery",
|
|
18334
|
-
outputField: "
|
|
18982
|
+
outputField: "level",
|
|
18335
18983
|
quantity: "percentage",
|
|
18336
18984
|
defaultAggregate: "mean",
|
|
18337
18985
|
empty: "null",
|
|
@@ -18359,7 +19007,7 @@ var GROUP_TEMPLATES = [
|
|
|
18359
19007
|
quantity: "device",
|
|
18360
19008
|
defaultAggregate: "mean",
|
|
18361
19009
|
empty: "null",
|
|
18362
|
-
companions: [NOW]
|
|
19010
|
+
companions: [NOW$2]
|
|
18363
19011
|
}),
|
|
18364
19012
|
binary({
|
|
18365
19013
|
id: "motion",
|
|
@@ -18453,6 +19101,11 @@ var GROUP_TEMPLATES = [
|
|
|
18453
19101
|
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.",
|
|
18454
19102
|
deviceType: DeviceType.Switch,
|
|
18455
19103
|
outputCap: "switch",
|
|
19104
|
+
outputField: "on",
|
|
19105
|
+
member: {
|
|
19106
|
+
cap: "switch",
|
|
19107
|
+
fieldPath: "on"
|
|
19108
|
+
},
|
|
18456
19109
|
commandCaps: ["switch"]
|
|
18457
19110
|
}),
|
|
18458
19111
|
actuator({
|
|
@@ -18460,34 +19113,136 @@ var GROUP_TEMPLATES = [
|
|
|
18460
19113
|
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.",
|
|
18461
19114
|
deviceType: DeviceType.Light,
|
|
18462
19115
|
outputCap: "switch",
|
|
19116
|
+
outputField: "on",
|
|
19117
|
+
member: {
|
|
19118
|
+
cap: "switch",
|
|
19119
|
+
fieldPath: "on"
|
|
19120
|
+
},
|
|
18463
19121
|
commandCaps: [
|
|
18464
19122
|
"switch",
|
|
18465
19123
|
"brightness",
|
|
18466
19124
|
"color"
|
|
18467
19125
|
]
|
|
19126
|
+
}),
|
|
19127
|
+
actuator({
|
|
19128
|
+
id: "fan",
|
|
19129
|
+
summary: "On when at least one fan is on, or when all of them are. Speed, preset, direction and oscillation are offered only when every fan has them. A command is not sent to a fan that lacks it.",
|
|
19130
|
+
deviceType: DeviceType.Fan,
|
|
19131
|
+
outputCap: "switch",
|
|
19132
|
+
outputField: "on",
|
|
19133
|
+
member: {
|
|
19134
|
+
cap: "switch",
|
|
19135
|
+
fieldPath: "on"
|
|
19136
|
+
},
|
|
19137
|
+
commandCaps: ["switch", "fan-control"]
|
|
19138
|
+
}),
|
|
19139
|
+
actuator({
|
|
19140
|
+
id: "cover",
|
|
19141
|
+
summary: "Open when at least one cover is open or part-open, or only when all of them are. Position and tilt are offered only when every cover has them. A cover that reports nothing is left out, never read as closed.",
|
|
19142
|
+
deviceType: DeviceType.Cover,
|
|
19143
|
+
outputCap: "group-active",
|
|
19144
|
+
outputField: "active",
|
|
19145
|
+
member: {
|
|
19146
|
+
cap: "cover",
|
|
19147
|
+
fieldPath: "state"
|
|
19148
|
+
},
|
|
19149
|
+
activeWhen: [
|
|
19150
|
+
"open",
|
|
19151
|
+
"opening",
|
|
19152
|
+
"stopped"
|
|
19153
|
+
],
|
|
19154
|
+
commandCaps: ["cover"]
|
|
19155
|
+
}),
|
|
19156
|
+
actuator({
|
|
19157
|
+
id: "valve",
|
|
19158
|
+
summary: "Open when at least one valve is open or part-open, or only when all of them are. Position is offered only when every valve has it. A valve that reports nothing is left out, never read as closed.",
|
|
19159
|
+
deviceType: DeviceType.Valve,
|
|
19160
|
+
outputCap: "group-active",
|
|
19161
|
+
outputField: "active",
|
|
19162
|
+
member: {
|
|
19163
|
+
cap: "valve",
|
|
19164
|
+
fieldPath: "state"
|
|
19165
|
+
},
|
|
19166
|
+
activeWhen: [
|
|
19167
|
+
"open",
|
|
19168
|
+
"opening",
|
|
19169
|
+
"stopped"
|
|
19170
|
+
],
|
|
19171
|
+
commandCaps: ["valve"]
|
|
19172
|
+
}),
|
|
19173
|
+
actuator({
|
|
19174
|
+
id: "lock",
|
|
19175
|
+
summary: "Locked when at least one lock is locked, or only when all of them are. A jammed or moving lock is not locked. Locking is one tap; unlocking the group needs a confirmation every time, and nothing is sent without it.",
|
|
19176
|
+
deviceType: DeviceType.Lock,
|
|
19177
|
+
outputCap: "group-active",
|
|
19178
|
+
outputField: "active",
|
|
19179
|
+
member: {
|
|
19180
|
+
cap: "lock-control",
|
|
19181
|
+
fieldPath: "state"
|
|
19182
|
+
},
|
|
19183
|
+
activeWhen: ["locked"],
|
|
19184
|
+
commandCaps: ["lock-control"]
|
|
19185
|
+
}),
|
|
19186
|
+
actuator({
|
|
19187
|
+
id: "media-player",
|
|
19188
|
+
summary: "Playing when at least one player is playing, or only when all of them are. Volume is offered only when every player has it. A player that reports nothing is left out, never read as stopped.",
|
|
19189
|
+
deviceType: DeviceType.MediaPlayer,
|
|
19190
|
+
outputCap: "group-active",
|
|
19191
|
+
outputField: "active",
|
|
19192
|
+
member: {
|
|
19193
|
+
cap: "media-player",
|
|
19194
|
+
fieldPath: "state"
|
|
19195
|
+
},
|
|
19196
|
+
activeWhen: ["playing", "buffering"],
|
|
19197
|
+
commandCaps: ["media-player"]
|
|
19198
|
+
}),
|
|
19199
|
+
actuator({
|
|
19200
|
+
id: "climate",
|
|
19201
|
+
summary: "Running when at least one thermostat or air conditioner is not off, or only when all of them are. Target temperature and fan are offered only when every device has them. A device that reports nothing is left out, never read as off.",
|
|
19202
|
+
deviceType: DeviceType.Thermostat,
|
|
19203
|
+
memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
|
|
19204
|
+
outputCap: "group-active",
|
|
19205
|
+
outputField: "active",
|
|
19206
|
+
member: {
|
|
19207
|
+
cap: "climate-control",
|
|
19208
|
+
fieldPath: "mode"
|
|
19209
|
+
},
|
|
19210
|
+
activeWhen: [
|
|
19211
|
+
"heat",
|
|
19212
|
+
"cool",
|
|
19213
|
+
"auto",
|
|
19214
|
+
"heat_cool",
|
|
19215
|
+
"fan_only",
|
|
19216
|
+
"dry"
|
|
19217
|
+
],
|
|
19218
|
+
commandCaps: ["climate-control"]
|
|
18468
19219
|
})
|
|
18469
19220
|
];
|
|
18470
19221
|
function actuator(input) {
|
|
18471
19222
|
return {
|
|
18472
|
-
|
|
19223
|
+
id: input.id,
|
|
19224
|
+
summary: input.summary,
|
|
19225
|
+
deviceType: input.deviceType,
|
|
19226
|
+
outputCap: input.outputCap,
|
|
19227
|
+
outputField: input.outputField,
|
|
19228
|
+
commandCaps: input.commandCaps,
|
|
18473
19229
|
family: "actuator",
|
|
18474
19230
|
role: void 0,
|
|
18475
19231
|
memberDeviceType: input.deviceType,
|
|
18476
|
-
|
|
19232
|
+
memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
|
|
19233
|
+
activeWhen: input.activeWhen,
|
|
18477
19234
|
quantity: "none",
|
|
18478
19235
|
baseUnit: void 0,
|
|
18479
19236
|
aggregates: BINARY_AGGREGATES,
|
|
18480
19237
|
defaultAggregate: "any",
|
|
18481
19238
|
empty: "offline",
|
|
18482
|
-
accept: [
|
|
18483
|
-
cap: "switch",
|
|
18484
|
-
fieldPath: "on"
|
|
18485
|
-
}],
|
|
19239
|
+
accept: [input.member],
|
|
18486
19240
|
companions: [CHANGED]
|
|
18487
19241
|
};
|
|
18488
19242
|
}
|
|
18489
19243
|
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
18490
19244
|
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
19245
|
+
DeviceType.Camera;
|
|
18491
19246
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
18492
19247
|
/**
|
|
18493
19248
|
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
@@ -18802,7 +19557,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
18802
19557
|
kind: "mutation",
|
|
18803
19558
|
auth: "admin",
|
|
18804
19559
|
access: "view"
|
|
18805
|
-
})
|
|
19560
|
+
}), method(object({
|
|
19561
|
+
blockId: string().min(1),
|
|
19562
|
+
trace: AutomationRunTraceSchema.optional(),
|
|
19563
|
+
runStartedAt: number().optional(),
|
|
19564
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
19565
|
+
}), _void(), {
|
|
19566
|
+
kind: "mutation",
|
|
19567
|
+
auth: "admin"
|
|
19568
|
+
}), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
|
|
19569
|
+
traces: array(AutomationRunTraceSchema),
|
|
19570
|
+
lastError: string().nullable()
|
|
19571
|
+
}), { auth: "admin" });
|
|
18806
19572
|
/**
|
|
18807
19573
|
* `custom-model-registry` — collection cap exposing operator-registered
|
|
18808
19574
|
* custom detection models. Each provider (today: `addon-model-studio`)
|
|
@@ -43881,7 +44647,7 @@ var NetworkAccessStatusSchema = object({
|
|
|
43881
44647
|
});
|
|
43882
44648
|
/**
|
|
43883
44649
|
* Optional, richer endpoint shape returned by providers that expose
|
|
43884
|
-
* MORE than one ingress concurrently (Tailscale
|
|
44650
|
+
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
43885
44651
|
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
43886
44652
|
* the originating provider config (mode + sourcePort) so the
|
|
43887
44653
|
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
@@ -44274,51 +45040,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
44274
45040
|
kind: "mutation",
|
|
44275
45041
|
auth: "admin"
|
|
44276
45042
|
});
|
|
44277
|
-
new Set([
|
|
44278
|
-
{
|
|
44279
|
-
id: "person",
|
|
44280
|
-
name: "Person"
|
|
44281
|
-
},
|
|
44282
|
-
{
|
|
44283
|
-
id: "vehicle",
|
|
44284
|
-
name: "Vehicle"
|
|
44285
|
-
},
|
|
44286
|
-
{
|
|
44287
|
-
id: "animal",
|
|
44288
|
-
name: "Animal"
|
|
44289
|
-
},
|
|
44290
|
-
{
|
|
44291
|
-
id: "package",
|
|
44292
|
-
name: "Package"
|
|
44293
|
-
}
|
|
44294
|
-
].map((l) => l.id));
|
|
44295
|
-
var COCO_TO_MACRO = {
|
|
44296
|
-
mapping: {
|
|
44297
|
-
person: "person",
|
|
44298
|
-
bicycle: "vehicle",
|
|
44299
|
-
car: "vehicle",
|
|
44300
|
-
motorcycle: "vehicle",
|
|
44301
|
-
airplane: "vehicle",
|
|
44302
|
-
bus: "vehicle",
|
|
44303
|
-
train: "vehicle",
|
|
44304
|
-
truck: "vehicle",
|
|
44305
|
-
boat: "vehicle",
|
|
44306
|
-
bird: "animal",
|
|
44307
|
-
cat: "animal",
|
|
44308
|
-
dog: "animal",
|
|
44309
|
-
horse: "animal",
|
|
44310
|
-
sheep: "animal",
|
|
44311
|
-
cow: "animal",
|
|
44312
|
-
elephant: "animal",
|
|
44313
|
-
bear: "animal",
|
|
44314
|
-
zebra: "animal",
|
|
44315
|
-
giraffe: "animal",
|
|
44316
|
-
suitcase: "package",
|
|
44317
|
-
backpack: "package",
|
|
44318
|
-
handbag: "package"
|
|
44319
|
-
},
|
|
44320
|
-
preserveOriginal: false
|
|
44321
|
-
};
|
|
44322
45043
|
var AUDIO_MACRO_LABELS = [
|
|
44323
45044
|
{
|
|
44324
45045
|
id: "speech",
|
|
@@ -46568,7 +47289,20 @@ var NcRuleInputSchema = object({
|
|
|
46568
47289
|
* save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
|
|
46569
47290
|
* path does not run Zod defaults.
|
|
46570
47291
|
*/
|
|
46571
|
-
groupingSec: number().int().min(5).max(60).optional()
|
|
47292
|
+
groupingSec: number().int().min(5).max(60).optional(),
|
|
47293
|
+
/**
|
|
47294
|
+
* ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
|
|
47295
|
+
* carries "Correct" / "Wrong" buttons (and, where the channel has room, a
|
|
47296
|
+
* "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
|
|
47297
|
+
* same mark the Library writes, and records whose notification it was and
|
|
47298
|
+
* when. A channel with no buttons gets the same two choices as links.
|
|
47299
|
+
*
|
|
47300
|
+
* `.optional()`, never `.default()` (a Zod default does not run on the addon
|
|
47301
|
+
* cap path): absent and `false` both mean off, which is what every rule
|
|
47302
|
+
* authored before this field meant. An editor turns it off by writing
|
|
47303
|
+
* `false`, so it needs no `clear` key.
|
|
47304
|
+
*/
|
|
47305
|
+
askVerdict: boolean().optional()
|
|
46572
47306
|
});
|
|
46573
47307
|
/**
|
|
46574
47308
|
* Partial patch for `updateRule` — any subset of the input fields, plus the
|
|
@@ -56872,9 +57606,9 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
56872
57606
|
*/
|
|
56873
57607
|
var AirQualitySensorStatusSchema = object({
|
|
56874
57608
|
/** Carbon dioxide concentration in ppm. */
|
|
56875
|
-
|
|
57609
|
+
co2: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
56876
57610
|
/** Total volatile organic compounds in ppb. */
|
|
56877
|
-
|
|
57611
|
+
voc: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
56878
57612
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
56879
57613
|
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
56880
57614
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
@@ -56907,12 +57641,12 @@ var airQualitySensorCapability = {
|
|
|
56907
57641
|
* History (D850): recorded series.
|
|
56908
57642
|
*/
|
|
56909
57643
|
history: { fields: {
|
|
56910
|
-
|
|
57644
|
+
co2: {
|
|
56911
57645
|
kind: "gauge",
|
|
56912
57646
|
unit: "ppm",
|
|
56913
57647
|
deadband: 5
|
|
56914
57648
|
},
|
|
56915
|
-
|
|
57649
|
+
voc: {
|
|
56916
57650
|
kind: "gauge",
|
|
56917
57651
|
unit: "ppb",
|
|
56918
57652
|
deadband: 5
|
|
@@ -56943,7 +57677,7 @@ var airQualitySensorCapability = {
|
|
|
56943
57677
|
*/
|
|
56944
57678
|
var AmbientLightSensorStatusSchema = object({
|
|
56945
57679
|
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
56946
|
-
|
|
57680
|
+
illuminance: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
56947
57681
|
precision: 0,
|
|
56948
57682
|
label: "Illuminance"
|
|
56949
57683
|
})),
|
|
@@ -56972,7 +57706,7 @@ var ambientLightSensorCapability = {
|
|
|
56972
57706
|
/**
|
|
56973
57707
|
* History (D850): recorded series. Wide range, so a RELATIVE deadband (10 %).
|
|
56974
57708
|
*/
|
|
56975
|
-
history: { fields: {
|
|
57709
|
+
history: { fields: { illuminance: {
|
|
56976
57710
|
kind: "gauge",
|
|
56977
57711
|
unit: "lx",
|
|
56978
57712
|
deadbandPct: 10,
|
|
@@ -57111,6 +57845,84 @@ var audioMetricsCapability = {
|
|
|
57111
57845
|
*/
|
|
57112
57846
|
history: "none"
|
|
57113
57847
|
};
|
|
57848
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
57849
|
+
actionId: string().min(1),
|
|
57850
|
+
variables: record(string(), ActionVariableValueSchema).optional()
|
|
57851
|
+
}), _void(), {
|
|
57852
|
+
kind: "mutation",
|
|
57853
|
+
auth: "admin"
|
|
57854
|
+
}), method(object({ actionId: string().min(1) }), _void(), {
|
|
57855
|
+
kind: "mutation",
|
|
57856
|
+
auth: "admin"
|
|
57857
|
+
});
|
|
57858
|
+
/**
|
|
57859
|
+
* actions — the ONE surface for runnable actions, for the UI and for
|
|
57860
|
+
* automations. Provided by the core-blocks builtin on the hub.
|
|
57861
|
+
*
|
|
57862
|
+
* An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
|
|
57863
|
+
* steps, kept in the hub's `actions` record kind) or declared by an addon over
|
|
57864
|
+
* `action-source`. `list` asks every `action-source` provider LIVE and overlays
|
|
57865
|
+
* the run status from the hub's `runs` ledger; there is no catalog here to
|
|
57866
|
+
* drift (D3).
|
|
57867
|
+
*
|
|
57868
|
+
* **Permissions follow the camera, as linked devices do.** An action is linked
|
|
57869
|
+
* to cameras by the operator (`setLinks`, D356: nothing links itself). A
|
|
57870
|
+
* non-admin may `get` / `run` an action only through a camera they may see that
|
|
57871
|
+
* links it: they name the camera (`deviceId`, a top-level field so the scope
|
|
57872
|
+
* gate sees it) and the provider refuses an action that camera does not link.
|
|
57873
|
+
* An admin, and an in-process caller (an automation block), may run any action.
|
|
57874
|
+
* Every refusal is typed (`[not-linked] …`) and logged.
|
|
57875
|
+
*
|
|
57876
|
+
* A second `run` of an action that is already running is REFUSED — traced and
|
|
57877
|
+
* logged — never queued.
|
|
57878
|
+
*/
|
|
57879
|
+
var ActionIdInput = object({ actionId: string().min(1) });
|
|
57880
|
+
var ActionLinksSchema = object({
|
|
57881
|
+
deviceId: number().int().nonnegative(),
|
|
57882
|
+
actionIds: array(string().min(1)).max(100)
|
|
57883
|
+
});
|
|
57884
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
57885
|
+
actionId: string().min(1),
|
|
57886
|
+
deviceId: number().int().nonnegative().optional()
|
|
57887
|
+
}), object({ action: ActionEntrySchema.nullable() }), {
|
|
57888
|
+
role: "viewer",
|
|
57889
|
+
auth: "protected",
|
|
57890
|
+
kind: "query",
|
|
57891
|
+
caller: "required"
|
|
57892
|
+
}), method(object({
|
|
57893
|
+
actionId: string().min(1),
|
|
57894
|
+
variables: record(string(), ActionVariableValueSchema).optional(),
|
|
57895
|
+
wait: boolean().optional(),
|
|
57896
|
+
origin: ActionRunOriginSchema.optional(),
|
|
57897
|
+
deviceId: number().int().nonnegative().optional()
|
|
57898
|
+
}), ActionRunResultSchema, {
|
|
57899
|
+
role: "viewer",
|
|
57900
|
+
auth: "protected",
|
|
57901
|
+
kind: "mutation",
|
|
57902
|
+
caller: "required"
|
|
57903
|
+
}), method(ActionIdInput, _void(), {
|
|
57904
|
+
kind: "mutation",
|
|
57905
|
+
auth: "admin"
|
|
57906
|
+
}), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
|
|
57907
|
+
kind: "mutation",
|
|
57908
|
+
auth: "admin"
|
|
57909
|
+
}), method(object({
|
|
57910
|
+
actionId: string().min(1),
|
|
57911
|
+
action: ActionInputSchema.partial()
|
|
57912
|
+
}), object({ action: ActionEntrySchema }), {
|
|
57913
|
+
kind: "mutation",
|
|
57914
|
+
auth: "admin"
|
|
57915
|
+
}), method(ActionIdInput, _void(), {
|
|
57916
|
+
kind: "mutation",
|
|
57917
|
+
auth: "admin"
|
|
57918
|
+
}), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
|
|
57919
|
+
role: "viewer",
|
|
57920
|
+
auth: "protected",
|
|
57921
|
+
kind: "query"
|
|
57922
|
+
}), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
|
|
57923
|
+
kind: "mutation",
|
|
57924
|
+
auth: "admin"
|
|
57925
|
+
});
|
|
57114
57926
|
/**
|
|
57115
57927
|
* Automation-control cap. Models HA `automation.*` entities on
|
|
57116
57928
|
* `DeviceType.Automation`. An automation is a trigger+condition+
|
|
@@ -57207,7 +58019,7 @@ var BatteryStatusSchema = object({
|
|
|
57207
58019
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
57208
58020
|
* declines to band a non-finite reading, which is the correct shape.
|
|
57209
58021
|
*/
|
|
57210
|
-
|
|
58022
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
57211
58023
|
/**
|
|
57212
58024
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
57213
58025
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -57217,7 +58029,7 @@ var BatteryStatusSchema = object({
|
|
|
57217
58029
|
* `'unknown'` is the absence of an answer: the provider has not read the
|
|
57218
58030
|
* power fields yet, or the firmware reported neither. Before it existed,
|
|
57219
58031
|
* two unreadable fields produced `'none'`, and a camera nobody had reached
|
|
57220
|
-
* was drawn "on battery alone" — the same D315 defect `
|
|
58032
|
+
* was drawn "on battery alone" — the same D315 defect `level` closed
|
|
57221
58033
|
* by going nullable. Consumers SKIP it (no charger state published, no
|
|
57222
58034
|
* glyph, no HomeKit ChargingState) rather than coerce it either way.
|
|
57223
58035
|
*/
|
|
@@ -57241,7 +58053,7 @@ var BatteryStatusSchema = object({
|
|
|
57241
58053
|
* that only report charge. A second reading on this cap, not a second
|
|
57242
58054
|
* `numeric-sensor` on the same device.
|
|
57243
58055
|
*/
|
|
57244
|
-
|
|
58056
|
+
voltage: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
57245
58057
|
/**
|
|
57246
58058
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
57247
58059
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -57275,7 +58087,7 @@ var BatteryStatusSchema = object({
|
|
|
57275
58087
|
/**
|
|
57276
58088
|
* True when the source is a BINARY low-battery indicator (HA
|
|
57277
58089
|
* `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
|
|
57278
|
-
* charge level — `
|
|
58090
|
+
* charge level — `level` is then a coarse stand-in (100 = normal,
|
|
57279
58091
|
* sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
|
|
57280
58092
|
* misleading exact percentage. Absent/false → genuine 0–100 % reading.
|
|
57281
58093
|
*/
|
|
@@ -57288,21 +58100,22 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
57288
58100
|
DeviceType.Switch,
|
|
57289
58101
|
DeviceType.PetFeeder,
|
|
57290
58102
|
DeviceType.Container,
|
|
57291
|
-
DeviceType.Thermostat
|
|
58103
|
+
DeviceType.Thermostat,
|
|
58104
|
+
DeviceType.EventEmitter
|
|
57292
58105
|
];
|
|
57293
58106
|
/** Generic Customize target for the percentage reading (D936). */
|
|
57294
58107
|
var batteryCustomize = {
|
|
57295
58108
|
version: 1,
|
|
57296
58109
|
targets: [{
|
|
57297
|
-
id: "
|
|
58110
|
+
id: "level",
|
|
57298
58111
|
cardinality: "scalar",
|
|
57299
58112
|
vocabulary: {
|
|
57300
58113
|
cap: "battery",
|
|
57301
|
-
stateField: "
|
|
58114
|
+
stateField: "level"
|
|
57302
58115
|
},
|
|
57303
58116
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
57304
58117
|
apply: "add",
|
|
57305
|
-
path: "
|
|
58118
|
+
path: "level",
|
|
57306
58119
|
sources: [{
|
|
57307
58120
|
kind: "field",
|
|
57308
58121
|
valueKinds: ["number", "string"],
|
|
@@ -57396,7 +58209,7 @@ onStatusChanged: { data: object({
|
|
|
57396
58209
|
schema: BatteryStatusSchema,
|
|
57397
58210
|
kind: "push",
|
|
57398
58211
|
empty: {
|
|
57399
|
-
|
|
58212
|
+
level: null,
|
|
57400
58213
|
charging: "unknown",
|
|
57401
58214
|
sleeping: false,
|
|
57402
58215
|
lastUpdated: 0
|
|
@@ -57423,11 +58236,11 @@ onStatusChanged: { data: object({
|
|
|
57423
58236
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
57424
58237
|
*/
|
|
57425
58238
|
history: { fields: {
|
|
57426
|
-
|
|
58239
|
+
level: {
|
|
57427
58240
|
kind: "gauge",
|
|
57428
58241
|
unit: "%"
|
|
57429
58242
|
},
|
|
57430
|
-
|
|
58243
|
+
voltage: {
|
|
57431
58244
|
kind: "gauge",
|
|
57432
58245
|
unit: "V"
|
|
57433
58246
|
}
|
|
@@ -57487,14 +58300,14 @@ var binaryCapability = {
|
|
|
57487
58300
|
* controls to this single-method surface.
|
|
57488
58301
|
*
|
|
57489
58302
|
* The cap is intentionally minimal: a single `setBrightness({deviceId,
|
|
57490
|
-
*
|
|
58303
|
+
* level})` mutation plus the auto-injected `getStatus`. Drivers
|
|
57491
58304
|
* that expose richer controls (color temperature, scenes, schedules)
|
|
57492
58305
|
* should surface those via the device's `getSettingsUISchema()`
|
|
57493
58306
|
* instead of bloating this cap.
|
|
57494
58307
|
*/
|
|
57495
58308
|
var BrightnessStatusSchema = object({
|
|
57496
58309
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
57497
|
-
|
|
58310
|
+
level: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
57498
58311
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
57499
58312
|
lastChangedAt: number()
|
|
57500
58313
|
});
|
|
@@ -57506,7 +58319,7 @@ var brightnessCapability = {
|
|
|
57506
58319
|
deviceTypes: [DeviceType.Light],
|
|
57507
58320
|
methods: { setBrightness: method(object({
|
|
57508
58321
|
deviceId: number().int().nonnegative(),
|
|
57509
|
-
|
|
58322
|
+
level: number().min(0).max(100)
|
|
57510
58323
|
}), _void(), {
|
|
57511
58324
|
kind: "mutation",
|
|
57512
58325
|
auth: "protected"
|
|
@@ -57519,7 +58332,7 @@ var brightnessCapability = {
|
|
|
57519
58332
|
*/
|
|
57520
58333
|
onBrightnessChanged: { data: object({
|
|
57521
58334
|
deviceId: number(),
|
|
57522
|
-
|
|
58335
|
+
level: number().min(0).max(100),
|
|
57523
58336
|
lastChangedAt: number()
|
|
57524
58337
|
}) } },
|
|
57525
58338
|
status: {
|
|
@@ -57542,7 +58355,7 @@ onBrightnessChanged: { data: object({
|
|
|
57542
58355
|
/**
|
|
57543
58356
|
* History (D850): recorded series. Actuator tier: level keeps every change.
|
|
57544
58357
|
*/
|
|
57545
|
-
history: { fields: {
|
|
58358
|
+
history: { fields: { level: {
|
|
57546
58359
|
kind: "gauge",
|
|
57547
58360
|
unit: "%",
|
|
57548
58361
|
minIntervalMs: 0
|
|
@@ -58139,11 +58952,11 @@ var ColorInputSchema = discriminatedUnion("mode", [
|
|
|
58139
58952
|
}),
|
|
58140
58953
|
object({
|
|
58141
58954
|
mode: literal("mired"),
|
|
58142
|
-
|
|
58955
|
+
colorTemperature: number().int().min(50).max(1e3)
|
|
58143
58956
|
})
|
|
58144
58957
|
]);
|
|
58145
58958
|
var ColorStatusSchema = object({
|
|
58146
|
-
/** Active color mode — which of `rgb` / `hsv` / `
|
|
58959
|
+
/** Active color mode — which of `rgb` / `hsv` / `colorTemperature` reflects
|
|
58147
58960
|
* the bulb's current state. */
|
|
58148
58961
|
mode: _enum([
|
|
58149
58962
|
"rgb",
|
|
@@ -58155,7 +58968,7 @@ var ColorStatusSchema = object({
|
|
|
58155
58968
|
/** Populated when `mode === 'hsv'`. */
|
|
58156
58969
|
hsv: HsvTripletSchema.optional(),
|
|
58157
58970
|
/** Populated when `mode === 'mired'`. */
|
|
58158
|
-
|
|
58971
|
+
colorTemperature: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
58159
58972
|
/** Ms epoch of the last operator-driven change. */
|
|
58160
58973
|
lastChangedAt: number()
|
|
58161
58974
|
});
|
|
@@ -58187,7 +59000,7 @@ onColorChanged: { data: object({
|
|
|
58187
59000
|
]),
|
|
58188
59001
|
rgb: RgbTripletSchema.optional(),
|
|
58189
59002
|
hsv: HsvTripletSchema.optional(),
|
|
58190
|
-
|
|
59003
|
+
colorTemperature: number().int().optional(),
|
|
58191
59004
|
lastChangedAt: number()
|
|
58192
59005
|
}) } },
|
|
58193
59006
|
status: {
|
|
@@ -59064,11 +59877,11 @@ var DishwasherStatusSchema = object({
|
|
|
59064
59877
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
59065
59878
|
availablePrograms: array(string()),
|
|
59066
59879
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
59067
|
-
|
|
59880
|
+
remaining: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
59068
59881
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
59069
|
-
|
|
59882
|
+
delayedStart: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
59070
59883
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
59071
|
-
|
|
59884
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59072
59885
|
/** True when the door is reported open; null when the door state is not known. */
|
|
59073
59886
|
doorOpen: boolean().nullable(),
|
|
59074
59887
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -59287,14 +60100,14 @@ var dishwasherCapability = {
|
|
|
59287
60100
|
runtimeState: DishwasherStatusSchema,
|
|
59288
60101
|
durability: "session",
|
|
59289
60102
|
history: { fields: {
|
|
59290
|
-
|
|
60103
|
+
progress: {
|
|
59291
60104
|
kind: "gauge",
|
|
59292
60105
|
unit: "%",
|
|
59293
60106
|
deadband: 2
|
|
59294
60107
|
},
|
|
59295
60108
|
state: { kind: "enum" },
|
|
59296
60109
|
operationState: { kind: "enum" },
|
|
59297
|
-
|
|
60110
|
+
remaining: {
|
|
59298
60111
|
kind: "gauge",
|
|
59299
60112
|
unit: "s",
|
|
59300
60113
|
minIntervalMs: 6e4
|
|
@@ -59885,7 +60698,7 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
59885
60698
|
var FanControlStatusSchema = object({
|
|
59886
60699
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
59887
60700
|
* speed surface (single-speed fan). */
|
|
59888
|
-
|
|
60701
|
+
speed: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59889
60702
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
59890
60703
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
59891
60704
|
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
@@ -59912,7 +60725,7 @@ var fanControlCapability = {
|
|
|
59912
60725
|
methods: {
|
|
59913
60726
|
setPercentage: method(object({
|
|
59914
60727
|
deviceId: number().int().nonnegative(),
|
|
59915
|
-
|
|
60728
|
+
speed: number().min(0).max(100)
|
|
59916
60729
|
}), _void(), {
|
|
59917
60730
|
kind: "mutation",
|
|
59918
60731
|
auth: "protected"
|
|
@@ -59945,7 +60758,7 @@ var fanControlCapability = {
|
|
|
59945
60758
|
},
|
|
59946
60759
|
/**
|
|
59947
60760
|
* Runtime-state slice — mirrored by the kernel. UI fan speed
|
|
59948
|
-
* sliders read `
|
|
60761
|
+
* sliders read `speed` for live updates.
|
|
59949
60762
|
*/
|
|
59950
60763
|
runtimeState: FanControlStatusSchema,
|
|
59951
60764
|
/**
|
|
@@ -59959,7 +60772,7 @@ var fanControlCapability = {
|
|
|
59959
60772
|
* History (D850): recorded series. Actuator tier: speed keeps every change.
|
|
59960
60773
|
*/
|
|
59961
60774
|
history: { fields: {
|
|
59962
|
-
|
|
60775
|
+
speed: {
|
|
59963
60776
|
kind: "gauge",
|
|
59964
60777
|
unit: "%",
|
|
59965
60778
|
minIntervalMs: 0
|
|
@@ -60148,7 +60961,17 @@ var groupCommandsCapability = {
|
|
|
60148
60961
|
scope: "device",
|
|
60149
60962
|
deviceNative: true,
|
|
60150
60963
|
mode: "singleton",
|
|
60151
|
-
deviceTypes: [
|
|
60964
|
+
deviceTypes: [
|
|
60965
|
+
DeviceType.Switch,
|
|
60966
|
+
DeviceType.Light,
|
|
60967
|
+
DeviceType.Fan,
|
|
60968
|
+
DeviceType.Cover,
|
|
60969
|
+
DeviceType.Valve,
|
|
60970
|
+
DeviceType.Lock,
|
|
60971
|
+
DeviceType.MediaPlayer,
|
|
60972
|
+
DeviceType.Thermostat,
|
|
60973
|
+
DeviceType.Climate
|
|
60974
|
+
],
|
|
60152
60975
|
methods: {},
|
|
60153
60976
|
status: {
|
|
60154
60977
|
schema: GroupCommandsSchema,
|
|
@@ -60166,6 +60989,49 @@ var groupCommandsCapability = {
|
|
|
60166
60989
|
history: "none"
|
|
60167
60990
|
};
|
|
60168
60991
|
/**
|
|
60992
|
+
* Whether a helper actuator group counts as active (D944): at least one
|
|
60993
|
+
* included member is, or every included member is. What "active" means is the
|
|
60994
|
+
* type's: a cover is open, a lock is locked, a media player is playing, a
|
|
60995
|
+
* climate device is not off. A group of lights, switches and fans reports
|
|
60996
|
+
* `switch.on` instead; this cap is for the types whose own state is not a
|
|
60997
|
+
* yes/no. Unknown members are left out (`group-status` counts them) and an
|
|
60998
|
+
* empty included set takes the device offline, never `active: false`.
|
|
60999
|
+
*/
|
|
61000
|
+
var GroupActiveSchema = object({
|
|
61001
|
+
active: boolean().meta({ label: "Active" }),
|
|
61002
|
+
/** Ms epoch when the slice was last updated. */
|
|
61003
|
+
lastChangedAt: number()
|
|
61004
|
+
});
|
|
61005
|
+
var groupActiveCapability = {
|
|
61006
|
+
name: "group-active",
|
|
61007
|
+
scope: "device",
|
|
61008
|
+
deviceNative: true,
|
|
61009
|
+
mode: "singleton",
|
|
61010
|
+
deviceTypes: [
|
|
61011
|
+
DeviceType.Cover,
|
|
61012
|
+
DeviceType.Valve,
|
|
61013
|
+
DeviceType.Lock,
|
|
61014
|
+
DeviceType.MediaPlayer,
|
|
61015
|
+
DeviceType.Climate,
|
|
61016
|
+
DeviceType.Thermostat
|
|
61017
|
+
],
|
|
61018
|
+
methods: {},
|
|
61019
|
+
status: {
|
|
61020
|
+
schema: GroupActiveSchema,
|
|
61021
|
+
kind: "push"
|
|
61022
|
+
},
|
|
61023
|
+
runtimeState: GroupActiveSchema,
|
|
61024
|
+
/**
|
|
61025
|
+
* Runtime-state durability: **session** — recomputed from the members on the
|
|
61026
|
+
* next source event; a restored value would outlive the members it named.
|
|
61027
|
+
*
|
|
61028
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
61029
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
61030
|
+
*/
|
|
61031
|
+
durability: "session",
|
|
61032
|
+
history: "none"
|
|
61033
|
+
};
|
|
61034
|
+
/**
|
|
60169
61035
|
* How many members of a helper group answered (D944). Counts are integers,
|
|
60170
61036
|
* not measurements: zero included is a real count, never a stand-in reading.
|
|
60171
61037
|
* The reading itself stays on the output capability. History is none — the
|
|
@@ -60196,7 +61062,14 @@ var groupStatusCapability = {
|
|
|
60196
61062
|
deviceTypes: [
|
|
60197
61063
|
DeviceType.Sensor,
|
|
60198
61064
|
DeviceType.Switch,
|
|
60199
|
-
DeviceType.Light
|
|
61065
|
+
DeviceType.Light,
|
|
61066
|
+
DeviceType.Fan,
|
|
61067
|
+
DeviceType.Cover,
|
|
61068
|
+
DeviceType.Valve,
|
|
61069
|
+
DeviceType.Lock,
|
|
61070
|
+
DeviceType.MediaPlayer,
|
|
61071
|
+
DeviceType.Thermostat,
|
|
61072
|
+
DeviceType.Climate
|
|
60200
61073
|
],
|
|
60201
61074
|
methods: {},
|
|
60202
61075
|
status: {
|
|
@@ -60344,7 +61217,7 @@ var humidifierCapability = {
|
|
|
60344
61217
|
*/
|
|
60345
61218
|
var HumiditySensorStatusSchema = object({
|
|
60346
61219
|
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
60347
|
-
|
|
61220
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
60348
61221
|
precision: 0,
|
|
60349
61222
|
label: "Humidity"
|
|
60350
61223
|
})),
|
|
@@ -60373,7 +61246,7 @@ var humiditySensorCapability = {
|
|
|
60373
61246
|
/**
|
|
60374
61247
|
* History (D850): recorded series.
|
|
60375
61248
|
*/
|
|
60376
|
-
history: { fields: {
|
|
61249
|
+
history: { fields: { humidity: {
|
|
60377
61250
|
kind: "gauge",
|
|
60378
61251
|
unit: "%",
|
|
60379
61252
|
deadband: .5
|
|
@@ -61013,7 +61886,7 @@ var LawnMowerControlStatusSchema = object({
|
|
|
61013
61886
|
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61014
61887
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
61015
61888
|
* task is active / progress is unavailable. */
|
|
61016
|
-
|
|
61889
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61017
61890
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
61018
61891
|
* or null when unknown. */
|
|
61019
61892
|
currentCode: number().nullable().meta(readingMeta("none")),
|
|
@@ -61469,7 +62342,14 @@ var lockControlCapability = {
|
|
|
61469
62342
|
}),
|
|
61470
62343
|
unlock: method(object({
|
|
61471
62344
|
deviceId: number().int().nonnegative(),
|
|
61472
|
-
code: string().min(1).optional()
|
|
62345
|
+
code: string().min(1).optional(),
|
|
62346
|
+
/**
|
|
62347
|
+
* Honoured by a helper lock GROUP only (D944): unlocking several doors
|
|
62348
|
+
* with one call is refused unless the caller says it was confirmed. A
|
|
62349
|
+
* single lock ignores it. The refusal lives in the callee, so no
|
|
62350
|
+
* automation or API caller reaches the doors by skipping a dialog.
|
|
62351
|
+
*/
|
|
62352
|
+
confirm: boolean().optional()
|
|
61473
62353
|
}), _void(), {
|
|
61474
62354
|
kind: "mutation",
|
|
61475
62355
|
auth: "protected"
|
|
@@ -61506,6 +62386,44 @@ var lockControlCapability = {
|
|
|
61506
62386
|
volatileStateFields: ["lastChangedAt"]
|
|
61507
62387
|
};
|
|
61508
62388
|
/**
|
|
62389
|
+
* A monotonic total between resets (D944). Energy uses `power-meter.energy`
|
|
62390
|
+
* so dashboards keep one series; every other instantaneous reading lands here.
|
|
62391
|
+
* `value` is the time integral in the source quantity's accumulated base
|
|
62392
|
+
* (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
|
|
62393
|
+
*/
|
|
62394
|
+
var MeterStatusSchema = object({
|
|
62395
|
+
value: number().nullable().meta(readingMeta("device", { label: "Total" })),
|
|
62396
|
+
lastFetchedAt: number()
|
|
62397
|
+
});
|
|
62398
|
+
var meterCapability = {
|
|
62399
|
+
name: "meter",
|
|
62400
|
+
scope: "device",
|
|
62401
|
+
deviceNative: true,
|
|
62402
|
+
mode: "singleton",
|
|
62403
|
+
deviceTypes: [DeviceType.Sensor],
|
|
62404
|
+
methods: {},
|
|
62405
|
+
status: {
|
|
62406
|
+
schema: MeterStatusSchema,
|
|
62407
|
+
kind: "push"
|
|
62408
|
+
},
|
|
62409
|
+
runtimeState: MeterStatusSchema,
|
|
62410
|
+
/**
|
|
62411
|
+
* Runtime-state durability: **restored** — the total is a cache of the
|
|
62412
|
+
* history integral. A restart shows it only until the history pass
|
|
62413
|
+
* recomputes the open period; it is not an in-memory buffer.
|
|
62414
|
+
*
|
|
62415
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
62416
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
62417
|
+
*/
|
|
62418
|
+
durability: "restored",
|
|
62419
|
+
history: { fields: { value: {
|
|
62420
|
+
kind: "counter",
|
|
62421
|
+
deadband: .001,
|
|
62422
|
+
minIntervalMs: 6e4
|
|
62423
|
+
} } },
|
|
62424
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
62425
|
+
};
|
|
62426
|
+
/**
|
|
61509
62427
|
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
61510
62428
|
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
61511
62429
|
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
@@ -61562,10 +62480,10 @@ var MediaPlayerStatusSchema = object({
|
|
|
61562
62480
|
currentMedia: MediaInfoSchema.nullable(),
|
|
61563
62481
|
/** Current playback position in ms. Null when not seekable or
|
|
61564
62482
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
61565
|
-
|
|
62483
|
+
position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61566
62484
|
/** Total duration of the current media in ms. Null when unknown
|
|
61567
62485
|
* (live stream) or nothing is playing. */
|
|
61568
|
-
|
|
62486
|
+
duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61569
62487
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
61570
62488
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
61571
62489
|
shuffle: boolean().nullable(),
|
|
@@ -61604,7 +62522,7 @@ var mediaPlayerCapability = {
|
|
|
61604
62522
|
}),
|
|
61605
62523
|
seek: method(object({
|
|
61606
62524
|
deviceId: number().int().nonnegative(),
|
|
61607
|
-
|
|
62525
|
+
position: number().int().nonnegative()
|
|
61608
62526
|
}), _void(), {
|
|
61609
62527
|
kind: "mutation",
|
|
61610
62528
|
auth: "protected"
|
|
@@ -61676,7 +62594,7 @@ var mediaPlayerCapability = {
|
|
|
61676
62594
|
/**
|
|
61677
62595
|
* History (D850): volume (a level an operator or an automation sets; the
|
|
61678
62596
|
* default 10 s floor folds a slider drag into its end value) and mute.
|
|
61679
|
-
* `
|
|
62597
|
+
* `position` is not recorded: it moves continuously while playing.
|
|
61680
62598
|
*/
|
|
61681
62599
|
history: { fields: {
|
|
61682
62600
|
volumeLevel: {
|
|
@@ -62638,9 +63556,9 @@ var NetworkLinkStatusSchema = object({
|
|
|
62638
63556
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
62639
63557
|
* SKIP a null rather than coerce it.
|
|
62640
63558
|
*/
|
|
62641
|
-
|
|
63559
|
+
signal: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62642
63560
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
62643
|
-
|
|
63561
|
+
rssi: number().optional().meta(readingMeta("signal-level")),
|
|
62644
63562
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
62645
63563
|
ssid: string().optional(),
|
|
62646
63564
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
@@ -62676,7 +63594,7 @@ onStatusChanged: { data: object({
|
|
|
62676
63594
|
kind: "push",
|
|
62677
63595
|
empty: {
|
|
62678
63596
|
type: "unknown",
|
|
62679
|
-
|
|
63597
|
+
signal: null,
|
|
62680
63598
|
lastUpdated: 0
|
|
62681
63599
|
}
|
|
62682
63600
|
},
|
|
@@ -62699,13 +63617,13 @@ onStatusChanged: { data: object({
|
|
|
62699
63617
|
* History (D850): recorded series.
|
|
62700
63618
|
*/
|
|
62701
63619
|
history: { fields: {
|
|
62702
|
-
|
|
63620
|
+
signal: {
|
|
62703
63621
|
kind: "gauge",
|
|
62704
63622
|
unit: "%",
|
|
62705
63623
|
deadband: 3,
|
|
62706
63624
|
minIntervalMs: 6e4
|
|
62707
63625
|
},
|
|
62708
|
-
|
|
63626
|
+
rssi: {
|
|
62709
63627
|
kind: "gauge",
|
|
62710
63628
|
unit: "dBm",
|
|
62711
63629
|
deadband: 2,
|
|
@@ -63450,10 +64368,20 @@ var PetFeederDeviceStatusSchema = _enum([
|
|
|
63450
64368
|
]);
|
|
63451
64369
|
var gramsPortion = number().int().min(4).max(200);
|
|
63452
64370
|
var PetFeederStatusSchema = object({
|
|
63453
|
-
/** Food
|
|
63454
|
-
*
|
|
63455
|
-
* bowl
|
|
64371
|
+
/** Food in the TANK (hopper), as the feeder's own sensor reports it
|
|
64372
|
+
* (`state.food`). A coarse level code, not grams — `0` is empty. The
|
|
64373
|
+
* amount in the bowl is `bowlFood`. On dual-hopper models this is the
|
|
64374
|
+
* combined reading; per-hopper levels live in `food1`/`food2`. Null when
|
|
64375
|
+
* the device has not reported yet. */
|
|
63456
64376
|
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
64377
|
+
/** Food in the BOWL as the feeder reports it (`state.bowl`), in the
|
|
64378
|
+
* feeder's own units. Null when the model reports none. */
|
|
64379
|
+
bowlFood: number().nullable().meta(readingMeta("none")),
|
|
64380
|
+
/** True while the pet is eating (`state.eating`). */
|
|
64381
|
+
eating: boolean(),
|
|
64382
|
+
/** Whether `markFoodReplenished` does anything on this model. When false
|
|
64383
|
+
* the client must not offer it. */
|
|
64384
|
+
supportsMarkRefilled: boolean(),
|
|
63457
64385
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
63458
64386
|
* single-hopper models. */
|
|
63459
64387
|
food1: number().nullable().meta(readingMeta("none")),
|
|
@@ -63461,8 +64389,8 @@ var PetFeederStatusSchema = object({
|
|
|
63461
64389
|
* single-hopper models. */
|
|
63462
64390
|
food2: number().nullable().meta(readingMeta("none")),
|
|
63463
64391
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
63464
|
-
* (`device_class: problem`, on = low). True when the
|
|
63465
|
-
*
|
|
64392
|
+
* (`device_class: problem`, on = low). True when the tank reports empty,
|
|
64393
|
+
* or, on a dual-hopper model, when EITHER hopper is empty. */
|
|
63466
64394
|
lowFood: boolean(),
|
|
63467
64395
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
63468
64396
|
* device has no battery reading. */
|
|
@@ -63530,7 +64458,9 @@ var petFeederCapability = {
|
|
|
63530
64458
|
kind: "mutation",
|
|
63531
64459
|
auth: "protected"
|
|
63532
64460
|
}),
|
|
63533
|
-
/**
|
|
64461
|
+
/** Tell the PetKit cloud a hopper was refilled so it stops its low-food
|
|
64462
|
+
* reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
|
|
64463
|
+
* change any sensor reading. */
|
|
63534
64464
|
markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
63535
64465
|
kind: "mutation",
|
|
63536
64466
|
auth: "protected"
|
|
@@ -63603,6 +64533,7 @@ var petFeederCapability = {
|
|
|
63603
64533
|
*/
|
|
63604
64534
|
history: { fields: {
|
|
63605
64535
|
foodLevel: { kind: "gauge" },
|
|
64536
|
+
bowlFood: { kind: "gauge" },
|
|
63606
64537
|
food1: { kind: "gauge" },
|
|
63607
64538
|
food2: { kind: "gauge" },
|
|
63608
64539
|
batteryPower: {
|
|
@@ -64012,27 +64943,89 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
64012
64943
|
* in real-world devices.
|
|
64013
64944
|
*/
|
|
64014
64945
|
var PowerMeterStatusSchema = object({
|
|
64015
|
-
/** Instantaneous power draw
|
|
64016
|
-
|
|
64946
|
+
/** Instantaneous power draw (watts). */
|
|
64947
|
+
power: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
64017
64948
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
64018
|
-
|
|
64019
|
-
/** Voltage
|
|
64020
|
-
|
|
64949
|
+
energy: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
64950
|
+
/** Voltage (volts). */
|
|
64951
|
+
voltage: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
64021
64952
|
/** Current in amperes. */
|
|
64022
|
-
|
|
64953
|
+
current: number().optional().meta(readingMeta("current", { label: "Current" })),
|
|
64023
64954
|
/** Apparent power in volt-amperes. */
|
|
64024
|
-
|
|
64955
|
+
apparentPower: number().optional().meta(readingMeta("apparent-power")),
|
|
64025
64956
|
/** Power factor (0..1) if reported. */
|
|
64026
64957
|
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
64027
64958
|
/** Ms epoch when the slice was last updated. */
|
|
64028
64959
|
lastFetchedAt: number()
|
|
64029
64960
|
});
|
|
64961
|
+
/**
|
|
64962
|
+
* Loads that draw metered mains power (D951). The reading is a feature of the
|
|
64963
|
+
* device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
|
|
64964
|
+
* vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
|
|
64965
|
+
* meter or a standalone energy monitor IS the device. Cover, lock, valve,
|
|
64966
|
+
* camera and the like are left out: their draw is not what anyone meters.
|
|
64967
|
+
*/
|
|
64968
|
+
var POWER_METER_DEVICE_TYPES = [
|
|
64969
|
+
DeviceType.Sensor,
|
|
64970
|
+
DeviceType.Switch,
|
|
64971
|
+
DeviceType.Light,
|
|
64972
|
+
DeviceType.Fan,
|
|
64973
|
+
DeviceType.Climate,
|
|
64974
|
+
DeviceType.Thermostat,
|
|
64975
|
+
DeviceType.Humidifier,
|
|
64976
|
+
DeviceType.WaterHeater,
|
|
64977
|
+
DeviceType.MediaPlayer,
|
|
64978
|
+
DeviceType.Washer,
|
|
64979
|
+
DeviceType.Dryer,
|
|
64980
|
+
DeviceType.Dishwasher,
|
|
64981
|
+
DeviceType.Vacuum,
|
|
64982
|
+
DeviceType.LawnMower,
|
|
64983
|
+
DeviceType.PetFeeder
|
|
64984
|
+
];
|
|
64985
|
+
/** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
|
|
64986
|
+
var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
|
|
64987
|
+
/** One scalar target per reading; each is sourced from a device of its own (D951). */
|
|
64988
|
+
function powerTarget(id, unit) {
|
|
64989
|
+
return {
|
|
64990
|
+
id,
|
|
64991
|
+
cardinality: "scalar",
|
|
64992
|
+
vocabulary: {
|
|
64993
|
+
cap: "power-meter",
|
|
64994
|
+
stateField: id
|
|
64995
|
+
},
|
|
64996
|
+
deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
|
|
64997
|
+
apply: "add",
|
|
64998
|
+
path: id,
|
|
64999
|
+
sources: [{
|
|
65000
|
+
kind: "numeric",
|
|
65001
|
+
requiredUnit: unit,
|
|
65002
|
+
transforms: [{ kind: "identity" }]
|
|
65003
|
+
}],
|
|
65004
|
+
companions: [{
|
|
65005
|
+
kind: "clock",
|
|
65006
|
+
path: "lastFetchedAt",
|
|
65007
|
+
when: "always"
|
|
65008
|
+
}],
|
|
65009
|
+
sourceReport: { status: "assignment-status" },
|
|
65010
|
+
advancedExpression: true
|
|
65011
|
+
};
|
|
65012
|
+
}
|
|
65013
|
+
/** "Power and energy" extension: power, energy, voltage and current from other devices (D951). */
|
|
65014
|
+
var powerMeterCustomize = {
|
|
65015
|
+
version: 1,
|
|
65016
|
+
targets: [
|
|
65017
|
+
powerTarget("power", "W"),
|
|
65018
|
+
powerTarget("energy", "kWh"),
|
|
65019
|
+
powerTarget("voltage", "V"),
|
|
65020
|
+
powerTarget("current", "A")
|
|
65021
|
+
]
|
|
65022
|
+
};
|
|
64030
65023
|
var powerMeterCapability = {
|
|
64031
65024
|
name: "power-meter",
|
|
64032
65025
|
scope: "device",
|
|
64033
65026
|
deviceNative: true,
|
|
64034
65027
|
mode: "singleton",
|
|
64035
|
-
deviceTypes:
|
|
65028
|
+
deviceTypes: POWER_METER_DEVICE_TYPES,
|
|
64036
65029
|
methods: {},
|
|
64037
65030
|
status: {
|
|
64038
65031
|
schema: PowerMeterStatusSchema,
|
|
@@ -64040,41 +65033,41 @@ var powerMeterCapability = {
|
|
|
64040
65033
|
},
|
|
64041
65034
|
runtimeState: PowerMeterStatusSchema,
|
|
64042
65035
|
/**
|
|
64043
|
-
* Runtime-state durability: **restored** — as `numeric-sensor`; `
|
|
65036
|
+
* Runtime-state durability: **restored** — as `numeric-sensor`; `energy` is an accumulator whose restored value is the baseline.
|
|
64044
65037
|
*
|
|
64045
65038
|
* See `RuntimeStateDurability`. Enforced by
|
|
64046
65039
|
* `scripts/check-runtime-state-durability.ts`.
|
|
64047
65040
|
*/
|
|
64048
65041
|
durability: "restored",
|
|
64049
65042
|
/**
|
|
64050
|
-
* History (D850): recorded series. Gauges are floored at 30 s; `
|
|
65043
|
+
* History (D850): recorded series. Gauges are floored at 30 s; `energy` is the one monotonic meter (counter).
|
|
64051
65044
|
*/
|
|
64052
65045
|
history: { fields: {
|
|
64053
|
-
|
|
65046
|
+
power: {
|
|
64054
65047
|
kind: "gauge",
|
|
64055
65048
|
unit: "W",
|
|
64056
65049
|
deadband: 1,
|
|
64057
65050
|
minIntervalMs: 3e4
|
|
64058
65051
|
},
|
|
64059
|
-
|
|
65052
|
+
energy: {
|
|
64060
65053
|
kind: "counter",
|
|
64061
65054
|
unit: "kWh",
|
|
64062
65055
|
deadband: .01,
|
|
64063
65056
|
minIntervalMs: 6e4
|
|
64064
65057
|
},
|
|
64065
|
-
|
|
65058
|
+
voltage: {
|
|
64066
65059
|
kind: "gauge",
|
|
64067
65060
|
unit: "V",
|
|
64068
65061
|
deadband: 1,
|
|
64069
65062
|
minIntervalMs: 3e4
|
|
64070
65063
|
},
|
|
64071
|
-
|
|
65064
|
+
current: {
|
|
64072
65065
|
kind: "gauge",
|
|
64073
65066
|
unit: "A",
|
|
64074
65067
|
deadband: .05,
|
|
64075
65068
|
minIntervalMs: 3e4
|
|
64076
65069
|
},
|
|
64077
|
-
|
|
65070
|
+
apparentPower: {
|
|
64078
65071
|
kind: "gauge",
|
|
64079
65072
|
unit: "VA",
|
|
64080
65073
|
deadband: 1,
|
|
@@ -64089,7 +65082,8 @@ var powerMeterCapability = {
|
|
|
64089
65082
|
} },
|
|
64090
65083
|
/** Clock fields: written, but excluded from the compare that decides
|
|
64091
65084
|
* whether persisting is worth a SQLite commit. */
|
|
64092
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
65085
|
+
volatileStateFields: ["lastFetchedAt"],
|
|
65086
|
+
customize: powerMeterCustomize
|
|
64093
65087
|
};
|
|
64094
65088
|
/**
|
|
64095
65089
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
@@ -64123,9 +65117,6 @@ var PresenceStatusSchema = object({
|
|
|
64123
65117
|
location: string().nullable(),
|
|
64124
65118
|
/** GPS coordinates when available. Null otherwise. */
|
|
64125
65119
|
gps: GpsLocationSchema.nullable(),
|
|
64126
|
-
/** Optional battery level of the tracking device (0..100). Null
|
|
64127
|
-
* when not reported. */
|
|
64128
|
-
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
64129
65120
|
/** Ms epoch when the slice was last updated. */
|
|
64130
65121
|
lastChangedAt: number()
|
|
64131
65122
|
});
|
|
@@ -64154,14 +65145,7 @@ var presenceCapability = {
|
|
|
64154
65145
|
* `scripts/check-runtime-state-durability.ts`.
|
|
64155
65146
|
*/
|
|
64156
65147
|
durability: "restored",
|
|
64157
|
-
|
|
64158
|
-
* History (D850): recorded series.
|
|
64159
|
-
*/
|
|
64160
|
-
history: { fields: { batteryPercent: {
|
|
64161
|
-
kind: "gauge",
|
|
64162
|
-
unit: "%",
|
|
64163
|
-
deadband: 1
|
|
64164
|
-
} } },
|
|
65148
|
+
history: "none",
|
|
64165
65149
|
/** Clock fields: written, but excluded from the compare that decides
|
|
64166
65150
|
* whether persisting is worth a SQLite commit. */
|
|
64167
65151
|
volatileStateFields: ["lastChangedAt"]
|
|
@@ -64176,7 +65160,7 @@ var presenceCapability = {
|
|
|
64176
65160
|
*/
|
|
64177
65161
|
var PressureSensorStatusSchema = object({
|
|
64178
65162
|
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
64179
|
-
|
|
65163
|
+
pressure: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
64180
65164
|
/** Ms epoch when the slice was last updated. */
|
|
64181
65165
|
lastFetchedAt: number()
|
|
64182
65166
|
});
|
|
@@ -64202,7 +65186,7 @@ var pressureSensorCapability = {
|
|
|
64202
65186
|
/**
|
|
64203
65187
|
* History (D850): recorded series.
|
|
64204
65188
|
*/
|
|
64205
|
-
history: { fields: {
|
|
65189
|
+
history: { fields: { pressure: {
|
|
64206
65190
|
kind: "gauge",
|
|
64207
65191
|
unit: "hPa",
|
|
64208
65192
|
deadband: .1
|
|
@@ -64770,6 +65754,8 @@ var runningCustomize = {
|
|
|
64770
65754
|
stateField: "running"
|
|
64771
65755
|
},
|
|
64772
65756
|
deviceTypes: [
|
|
65757
|
+
DeviceType.Washer,
|
|
65758
|
+
DeviceType.Dryer,
|
|
64773
65759
|
DeviceType.Dishwasher,
|
|
64774
65760
|
DeviceType.Vacuum,
|
|
64775
65761
|
DeviceType.LawnMower,
|
|
@@ -67259,7 +68245,8 @@ var switchCapability = {
|
|
|
67259
68245
|
deviceTypes: [
|
|
67260
68246
|
DeviceType.Switch,
|
|
67261
68247
|
DeviceType.Siren,
|
|
67262
|
-
DeviceType.Light
|
|
68248
|
+
DeviceType.Light,
|
|
68249
|
+
DeviceType.Fan
|
|
67263
68250
|
],
|
|
67264
68251
|
methods: { setState: method(object({
|
|
67265
68252
|
deviceId: number(),
|
|
@@ -67898,7 +68885,7 @@ var tamperCapability = {
|
|
|
67898
68885
|
*/
|
|
67899
68886
|
var TemperatureSensorStatusSchema = object({
|
|
67900
68887
|
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
67901
|
-
|
|
68888
|
+
temperature: number().nullable().meta(readingMeta("temperature", {
|
|
67902
68889
|
precision: 1,
|
|
67903
68890
|
label: "Temperature"
|
|
67904
68891
|
})),
|
|
@@ -67927,7 +68914,7 @@ var temperatureSensorCapability = {
|
|
|
67927
68914
|
/**
|
|
67928
68915
|
* History (D850): recorded series.
|
|
67929
68916
|
*/
|
|
67930
|
-
history: { fields: {
|
|
68917
|
+
history: { fields: { temperature: {
|
|
67931
68918
|
kind: "gauge",
|
|
67932
68919
|
unit: "°C",
|
|
67933
68920
|
deadband: .1
|
|
@@ -68224,7 +69211,7 @@ var VacuumControlStatusSchema = object({
|
|
|
68224
69211
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
68225
69212
|
availableFanSpeeds: array(string()),
|
|
68226
69213
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
68227
|
-
|
|
69214
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68228
69215
|
/** Current error code (0 / null = no error). */
|
|
68229
69216
|
errorCode: number().nullable().meta(readingMeta("none")),
|
|
68230
69217
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
@@ -69472,6 +70459,7 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
69472
70459
|
kind: "mutation",
|
|
69473
70460
|
auth: "admin"
|
|
69474
70461
|
});
|
|
70462
|
+
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
69475
70463
|
/**
|
|
69476
70464
|
* Frozen unit → spec table. `•` in the doc marks each dimension's canonical
|
|
69477
70465
|
* unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
|
|
@@ -69856,7 +70844,7 @@ function tryConvertUnit(value, from, to) {
|
|
|
69856
70844
|
return fromCanonical(toCanonical(value, fromSpec), toSpec);
|
|
69857
70845
|
}
|
|
69858
70846
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
69859
|
-
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;
|
|
70847
|
+
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
69860
70848
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
69861
70849
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
69862
70850
|
new Set(CAP_NAMES);
|
|
@@ -70185,6 +71173,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
70185
71173
|
featureProbe: featureProbeCapability,
|
|
70186
71174
|
flood: floodCapability,
|
|
70187
71175
|
gas: gasCapability,
|
|
71176
|
+
groupActive: groupActiveCapability,
|
|
70188
71177
|
groupCommands: groupCommandsCapability,
|
|
70189
71178
|
groupStatus: groupStatusCapability,
|
|
70190
71179
|
heatingValve: heatingValveCapability,
|
|
@@ -70196,6 +71185,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
70196
71185
|
lawnMowerControl: lawnMowerControlCapability,
|
|
70197
71186
|
lockControl: lockControlCapability,
|
|
70198
71187
|
mediaPlayer: mediaPlayerCapability,
|
|
71188
|
+
meter: meterCapability,
|
|
70199
71189
|
motion: motionCapability,
|
|
70200
71190
|
motionTrigger: motionTriggerCapability,
|
|
70201
71191
|
motionZones: motionZonesCapability,
|
|
@@ -70258,7 +71248,7 @@ var BaseDevice = class {
|
|
|
70258
71248
|
* `device-local-state.ts`. Drivers access via:
|
|
70259
71249
|
*
|
|
70260
71250
|
* `this.state.battery.sleeping = true` // patches the battery slice
|
|
70261
|
-
* `const pct = this.state.battery.
|
|
71251
|
+
* `const pct = this.state.battery.level` // reads the battery slice
|
|
70262
71252
|
* `this.state.deviceStatus.online = true` // mirrors `markOnline(true)`
|
|
70263
71253
|
*
|
|
70264
71254
|
* Adding a new cap with `runtimeState:` automatically extends this
|
|
@@ -70298,7 +71288,7 @@ var BaseDevice = class {
|
|
|
70298
71288
|
* shape; drivers don't redeclare it, they just write through.
|
|
70299
71289
|
*
|
|
70300
71290
|
* Read: `this.runtimeState.getCapState('battery')` →
|
|
70301
|
-
* `{
|
|
71291
|
+
* `{level, charging, sleeping, lastUpdated}` for any
|
|
70302
71292
|
* provider that registers `batteryCapability`.
|
|
70303
71293
|
* Write: `this.runtimeState.setCapState('battery', { … })`.
|
|
70304
71294
|
*
|
|
@@ -71455,7 +72445,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
71455
72445
|
return toDeviceSummary(device, this.addonId);
|
|
71456
72446
|
}
|
|
71457
72447
|
};
|
|
71458
|
-
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher;
|
|
72448
|
+
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
|
|
71459
72449
|
new Set(Object.values(DeviceType));
|
|
71460
72450
|
Object.freeze({
|
|
71461
72451
|
"accessories.getStatus": {
|
|
@@ -71472,6 +72462,104 @@ Object.freeze({
|
|
|
71472
72462
|
access: "create",
|
|
71473
72463
|
role: "admin"
|
|
71474
72464
|
},
|
|
72465
|
+
"actions.create": {
|
|
72466
|
+
capName: "actions",
|
|
72467
|
+
capScope: "system",
|
|
72468
|
+
addonId: null,
|
|
72469
|
+
access: "create",
|
|
72470
|
+
role: "admin"
|
|
72471
|
+
},
|
|
72472
|
+
"actions.delete": {
|
|
72473
|
+
capName: "actions",
|
|
72474
|
+
capScope: "system",
|
|
72475
|
+
addonId: null,
|
|
72476
|
+
access: "delete",
|
|
72477
|
+
role: "admin"
|
|
72478
|
+
},
|
|
72479
|
+
"actions.get": {
|
|
72480
|
+
capName: "actions",
|
|
72481
|
+
capScope: "system",
|
|
72482
|
+
addonId: null,
|
|
72483
|
+
access: "view",
|
|
72484
|
+
role: "viewer"
|
|
72485
|
+
},
|
|
72486
|
+
"actions.getLinks": {
|
|
72487
|
+
capName: "actions",
|
|
72488
|
+
capScope: "system",
|
|
72489
|
+
addonId: null,
|
|
72490
|
+
access: "view",
|
|
72491
|
+
role: "admin"
|
|
72492
|
+
},
|
|
72493
|
+
"actions.getTraces": {
|
|
72494
|
+
capName: "actions",
|
|
72495
|
+
capScope: "system",
|
|
72496
|
+
addonId: null,
|
|
72497
|
+
access: "view",
|
|
72498
|
+
role: "admin"
|
|
72499
|
+
},
|
|
72500
|
+
"actions.list": {
|
|
72501
|
+
capName: "actions",
|
|
72502
|
+
capScope: "system",
|
|
72503
|
+
addonId: null,
|
|
72504
|
+
access: "view",
|
|
72505
|
+
role: "admin"
|
|
72506
|
+
},
|
|
72507
|
+
"actions.listForCamera": {
|
|
72508
|
+
capName: "actions",
|
|
72509
|
+
capScope: "system",
|
|
72510
|
+
addonId: null,
|
|
72511
|
+
access: "view",
|
|
72512
|
+
role: "viewer"
|
|
72513
|
+
},
|
|
72514
|
+
"actions.run": {
|
|
72515
|
+
capName: "actions",
|
|
72516
|
+
capScope: "system",
|
|
72517
|
+
addonId: null,
|
|
72518
|
+
access: "create",
|
|
72519
|
+
role: "viewer"
|
|
72520
|
+
},
|
|
72521
|
+
"actions.setLinks": {
|
|
72522
|
+
capName: "actions",
|
|
72523
|
+
capScope: "system",
|
|
72524
|
+
addonId: null,
|
|
72525
|
+
access: "create",
|
|
72526
|
+
role: "admin"
|
|
72527
|
+
},
|
|
72528
|
+
"actions.stop": {
|
|
72529
|
+
capName: "actions",
|
|
72530
|
+
capScope: "system",
|
|
72531
|
+
addonId: null,
|
|
72532
|
+
access: "create",
|
|
72533
|
+
role: "admin"
|
|
72534
|
+
},
|
|
72535
|
+
"actions.update": {
|
|
72536
|
+
capName: "actions",
|
|
72537
|
+
capScope: "system",
|
|
72538
|
+
addonId: null,
|
|
72539
|
+
access: "create",
|
|
72540
|
+
role: "admin"
|
|
72541
|
+
},
|
|
72542
|
+
"actionSource.listActions": {
|
|
72543
|
+
capName: "action-source",
|
|
72544
|
+
capScope: "system",
|
|
72545
|
+
addonId: null,
|
|
72546
|
+
access: "view",
|
|
72547
|
+
role: "admin"
|
|
72548
|
+
},
|
|
72549
|
+
"actionSource.runAction": {
|
|
72550
|
+
capName: "action-source",
|
|
72551
|
+
capScope: "system",
|
|
72552
|
+
addonId: null,
|
|
72553
|
+
access: "create",
|
|
72554
|
+
role: "admin"
|
|
72555
|
+
},
|
|
72556
|
+
"actionSource.stopAction": {
|
|
72557
|
+
capName: "action-source",
|
|
72558
|
+
capScope: "system",
|
|
72559
|
+
addonId: null,
|
|
72560
|
+
access: "create",
|
|
72561
|
+
role: "admin"
|
|
72562
|
+
},
|
|
71475
72563
|
"addonPages.listPages": {
|
|
71476
72564
|
capName: "addon-pages",
|
|
71477
72565
|
capScope: "system",
|
|
@@ -72753,6 +73841,13 @@ Object.freeze({
|
|
|
72753
73841
|
access: "view",
|
|
72754
73842
|
role: "admin"
|
|
72755
73843
|
},
|
|
73844
|
+
"coreBlocks.getAutomationRunState": {
|
|
73845
|
+
capName: "core-blocks",
|
|
73846
|
+
capScope: "system",
|
|
73847
|
+
addonId: null,
|
|
73848
|
+
access: "view",
|
|
73849
|
+
role: "admin"
|
|
73850
|
+
},
|
|
72756
73851
|
"coreBlocks.getBlockSettings": {
|
|
72757
73852
|
capName: "core-blocks",
|
|
72758
73853
|
capScope: "system",
|
|
@@ -72774,6 +73869,13 @@ Object.freeze({
|
|
|
72774
73869
|
access: "view",
|
|
72775
73870
|
role: "admin"
|
|
72776
73871
|
},
|
|
73872
|
+
"coreBlocks.getTraces": {
|
|
73873
|
+
capName: "core-blocks",
|
|
73874
|
+
capScope: "system",
|
|
73875
|
+
addonId: null,
|
|
73876
|
+
access: "view",
|
|
73877
|
+
role: "admin"
|
|
73878
|
+
},
|
|
72777
73879
|
"coreBlocks.getTypeDefs": {
|
|
72778
73880
|
capName: "core-blocks",
|
|
72779
73881
|
capScope: "system",
|
|
@@ -72809,6 +73911,13 @@ Object.freeze({
|
|
|
72809
73911
|
access: "create",
|
|
72810
73912
|
role: "admin"
|
|
72811
73913
|
},
|
|
73914
|
+
"coreBlocks.reportAutomationRun": {
|
|
73915
|
+
capName: "core-blocks",
|
|
73916
|
+
capScope: "system",
|
|
73917
|
+
addonId: null,
|
|
73918
|
+
access: "create",
|
|
73919
|
+
role: "admin"
|
|
73920
|
+
},
|
|
72812
73921
|
"coreBlocks.restart": {
|
|
72813
73922
|
capName: "core-blocks",
|
|
72814
73923
|
capScope: "system",
|
|
@@ -74678,6 +75787,13 @@ Object.freeze({
|
|
|
74678
75787
|
access: "view",
|
|
74679
75788
|
role: "viewer"
|
|
74680
75789
|
},
|
|
75790
|
+
"groupActive.getStatus": {
|
|
75791
|
+
capName: "group-active",
|
|
75792
|
+
capScope: "device",
|
|
75793
|
+
addonId: null,
|
|
75794
|
+
access: "view",
|
|
75795
|
+
role: "viewer"
|
|
75796
|
+
},
|
|
74681
75797
|
"groupCommands.getStatus": {
|
|
74682
75798
|
capName: "group-commands",
|
|
74683
75799
|
capScope: "device",
|
|
@@ -75644,6 +76760,13 @@ Object.freeze({
|
|
|
75644
76760
|
access: "create",
|
|
75645
76761
|
role: "admin"
|
|
75646
76762
|
},
|
|
76763
|
+
"meter.getStatus": {
|
|
76764
|
+
capName: "meter",
|
|
76765
|
+
capScope: "device",
|
|
76766
|
+
addonId: null,
|
|
76767
|
+
access: "view",
|
|
76768
|
+
role: "viewer"
|
|
76769
|
+
},
|
|
75647
76770
|
"metricsProvider.collectSnapshot": {
|
|
75648
76771
|
capName: "metrics-provider",
|
|
75649
76772
|
capScope: "system",
|
|
@@ -81368,7 +82491,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81368
82491
|
label: t("Full brightness", "Luminosità massima"),
|
|
81369
82492
|
phrase: t("Set {device} to full brightness", "Porta {device} alla massima luminosità"),
|
|
81370
82493
|
icon: "sun",
|
|
81371
|
-
args: {
|
|
82494
|
+
args: { level: 100 }
|
|
81372
82495
|
},
|
|
81373
82496
|
{
|
|
81374
82497
|
id: "brightness.half",
|
|
@@ -81376,7 +82499,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81376
82499
|
label: t("Half brightness", "Luminosità a metà"),
|
|
81377
82500
|
phrase: t("Dim {device} to half", "Abbassa {device} a metà luminosità"),
|
|
81378
82501
|
icon: "sun-dim",
|
|
81379
|
-
args: {
|
|
82502
|
+
args: { level: 50 }
|
|
81380
82503
|
},
|
|
81381
82504
|
{
|
|
81382
82505
|
id: "brightness.off",
|
|
@@ -81384,16 +82507,16 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81384
82507
|
label: t("Brightness off", "Luminosità a zero"),
|
|
81385
82508
|
phrase: t("Dim {device} to zero", "Abbassa {device} a zero"),
|
|
81386
82509
|
icon: "lightbulb-off",
|
|
81387
|
-
args: {
|
|
82510
|
+
args: { level: 0 }
|
|
81388
82511
|
},
|
|
81389
82512
|
{
|
|
81390
82513
|
id: "brightness.set",
|
|
81391
82514
|
method: "setBrightness",
|
|
81392
82515
|
label: t("Set brightness", "Imposta la luminosità"),
|
|
81393
|
-
phrase: t("Set the brightness of {device} to {
|
|
82516
|
+
phrase: t("Set the brightness of {device} to {level}", "Imposta la luminosità di {device} a {level}"),
|
|
81394
82517
|
icon: "sun-medium",
|
|
81395
82518
|
openArgs: [{
|
|
81396
|
-
key: "
|
|
82519
|
+
key: "level",
|
|
81397
82520
|
label: PERCENT,
|
|
81398
82521
|
offer: {
|
|
81399
82522
|
kind: "number",
|
|
@@ -81407,13 +82530,13 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81407
82530
|
],
|
|
81408
82531
|
state: [{
|
|
81409
82532
|
kind: "numeric",
|
|
81410
|
-
field: "
|
|
82533
|
+
field: "level",
|
|
81411
82534
|
quantity: t("the brightness", "la luminosità"),
|
|
81412
82535
|
label: t("Brightness", "Luminosità"),
|
|
81413
82536
|
unit: "%",
|
|
81414
82537
|
icon: "sun"
|
|
81415
82538
|
}],
|
|
81416
|
-
events: { onBrightnessChanged: { notOperatorFacing: "the `
|
|
82539
|
+
events: { onBrightnessChanged: { notOperatorFacing: "the `level` state trigger reads the same change" } }
|
|
81417
82540
|
},
|
|
81418
82541
|
color: {
|
|
81419
82542
|
scope: "device",
|
|
@@ -81427,7 +82550,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81427
82550
|
icon: "sun",
|
|
81428
82551
|
args: { color: {
|
|
81429
82552
|
mode: "mired",
|
|
81430
|
-
|
|
82553
|
+
colorTemperature: 370
|
|
81431
82554
|
} }
|
|
81432
82555
|
},
|
|
81433
82556
|
{
|
|
@@ -81438,7 +82561,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81438
82561
|
icon: "snowflake",
|
|
81439
82562
|
args: { color: {
|
|
81440
82563
|
mode: "mired",
|
|
81441
|
-
|
|
82564
|
+
colorTemperature: 200
|
|
81442
82565
|
} }
|
|
81443
82566
|
},
|
|
81444
82567
|
{
|
|
@@ -82052,7 +83175,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82052
83175
|
label: t("Fan off", "Ventilatore spento"),
|
|
82053
83176
|
phrase: t("Stop {device}", "Ferma {device}"),
|
|
82054
83177
|
icon: "power-off",
|
|
82055
|
-
args: {
|
|
83178
|
+
args: { speed: 0 }
|
|
82056
83179
|
},
|
|
82057
83180
|
{
|
|
82058
83181
|
id: "fan.full",
|
|
@@ -82060,16 +83183,16 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82060
83183
|
label: t("Fan full", "Ventilatore al massimo"),
|
|
82061
83184
|
phrase: t("Run {device} at full speed", "Porta {device} alla massima velocità"),
|
|
82062
83185
|
icon: "fan",
|
|
82063
|
-
args: {
|
|
83186
|
+
args: { speed: 100 }
|
|
82064
83187
|
},
|
|
82065
83188
|
{
|
|
82066
83189
|
id: "fan.speed",
|
|
82067
83190
|
method: "setPercentage",
|
|
82068
83191
|
label: t("Set speed", "Imposta la velocità"),
|
|
82069
|
-
phrase: t("Set the speed of {device} to {
|
|
83192
|
+
phrase: t("Set the speed of {device} to {speed}", "Imposta la velocità di {device} al {speed}"),
|
|
82070
83193
|
icon: "fan",
|
|
82071
83194
|
openArgs: [{
|
|
82072
|
-
key: "
|
|
83195
|
+
key: "speed",
|
|
82073
83196
|
label: t("Speed", "Velocità"),
|
|
82074
83197
|
offer: {
|
|
82075
83198
|
kind: "number",
|
|
@@ -82131,7 +83254,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82131
83254
|
],
|
|
82132
83255
|
state: [{
|
|
82133
83256
|
kind: "numeric",
|
|
82134
|
-
field: "
|
|
83257
|
+
field: "speed",
|
|
82135
83258
|
quantity: t("the speed", "la velocità"),
|
|
82136
83259
|
label: t("Speed", "Velocità"),
|
|
82137
83260
|
unit: "%",
|
|
@@ -82929,7 +84052,7 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
82929
84052
|
},
|
|
82930
84053
|
{
|
|
82931
84054
|
kind: "numeric",
|
|
82932
|
-
field: "
|
|
84055
|
+
field: "progress",
|
|
82933
84056
|
quantity: t("the cycle progress", "l’avanzamento del lavaggio"),
|
|
82934
84057
|
label: t("Progress", "Avanzamento"),
|
|
82935
84058
|
unit: "%",
|
|
@@ -83287,10 +84410,10 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
83287
84410
|
id: "media.seek",
|
|
83288
84411
|
method: "seek",
|
|
83289
84412
|
label: t("Seek", "Vai al punto"),
|
|
83290
|
-
phrase: t("Move {device} to {
|
|
84413
|
+
phrase: t("Move {device} to {position}", "Porta {device} al punto {position}"),
|
|
83291
84414
|
icon: "fast-forward",
|
|
83292
84415
|
openArgs: [{
|
|
83293
|
-
key: "
|
|
84416
|
+
key: "position",
|
|
83294
84417
|
label: t("Position", "Posizione"),
|
|
83295
84418
|
offer: {
|
|
83296
84419
|
kind: "number",
|
|
@@ -83742,7 +84865,39 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
83742
84865
|
phrase: t("When {device} finishes", "Quando {device} finisce"),
|
|
83743
84866
|
icon: "square"
|
|
83744
84867
|
}
|
|
83745
|
-
}
|
|
84868
|
+
},
|
|
84869
|
+
overrides: { byType: {
|
|
84870
|
+
[DeviceType.Washer]: {
|
|
84871
|
+
state: { running: {
|
|
84872
|
+
whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
|
|
84873
|
+
whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
|
|
84874
|
+
} },
|
|
84875
|
+
events: {
|
|
84876
|
+
onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
|
|
84877
|
+
onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
|
|
84878
|
+
}
|
|
84879
|
+
},
|
|
84880
|
+
[DeviceType.Dryer]: {
|
|
84881
|
+
state: { running: {
|
|
84882
|
+
whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
|
|
84883
|
+
whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
|
|
84884
|
+
} },
|
|
84885
|
+
events: {
|
|
84886
|
+
onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
|
|
84887
|
+
onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
|
|
84888
|
+
}
|
|
84889
|
+
},
|
|
84890
|
+
[DeviceType.Dishwasher]: {
|
|
84891
|
+
state: { running: {
|
|
84892
|
+
whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
|
|
84893
|
+
whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
|
|
84894
|
+
} },
|
|
84895
|
+
events: {
|
|
84896
|
+
onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
|
|
84897
|
+
onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
|
|
84898
|
+
}
|
|
84899
|
+
}
|
|
84900
|
+
} }
|
|
83746
84901
|
},
|
|
83747
84902
|
update: {
|
|
83748
84903
|
scope: "device",
|
|
@@ -84307,7 +85462,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84307
85462
|
state: [
|
|
84308
85463
|
{
|
|
84309
85464
|
kind: "numeric",
|
|
84310
|
-
field: "
|
|
85465
|
+
field: "co2",
|
|
84311
85466
|
quantity: t("the CO₂", "la CO₂"),
|
|
84312
85467
|
label: t("CO₂", "CO₂"),
|
|
84313
85468
|
unit: "ppm",
|
|
@@ -84315,7 +85470,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84315
85470
|
},
|
|
84316
85471
|
{
|
|
84317
85472
|
kind: "numeric",
|
|
84318
|
-
field: "
|
|
85473
|
+
field: "voc",
|
|
84319
85474
|
quantity: t("the VOC level", "il livello di COV"),
|
|
84320
85475
|
label: t("VOC", "COV"),
|
|
84321
85476
|
unit: "ppb",
|
|
@@ -84351,7 +85506,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84351
85506
|
noun: t("Light level", "Luminosità ambiente"),
|
|
84352
85507
|
state: [{
|
|
84353
85508
|
kind: "numeric",
|
|
84354
|
-
field: "
|
|
85509
|
+
field: "illuminance",
|
|
84355
85510
|
quantity: t("the light level", "la luminosità"),
|
|
84356
85511
|
label: t("Illuminance", "Illuminamento"),
|
|
84357
85512
|
unit: "lx",
|
|
@@ -84514,6 +85669,10 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84514
85669
|
icon: "flame"
|
|
84515
85670
|
}]
|
|
84516
85671
|
},
|
|
85672
|
+
"group-active": {
|
|
85673
|
+
scope: "device",
|
|
85674
|
+
noun: t("Group state", "Stato del gruppo")
|
|
85675
|
+
},
|
|
84517
85676
|
"group-commands": {
|
|
84518
85677
|
scope: "device",
|
|
84519
85678
|
noun: t("Group command", "Comando di gruppo")
|
|
@@ -84550,7 +85709,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84550
85709
|
noun: t("Humidity", "Umidità"),
|
|
84551
85710
|
state: [{
|
|
84552
85711
|
kind: "numeric",
|
|
84553
|
-
field: "
|
|
85712
|
+
field: "humidity",
|
|
84554
85713
|
quantity: t("the humidity", "l’umidità"),
|
|
84555
85714
|
label: t("Humidity", "Umidità"),
|
|
84556
85715
|
unit: "%",
|
|
@@ -84570,6 +85729,17 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84570
85729
|
}],
|
|
84571
85730
|
events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
|
|
84572
85731
|
},
|
|
85732
|
+
meter: {
|
|
85733
|
+
scope: "device",
|
|
85734
|
+
noun: t("Meter", "Contatore"),
|
|
85735
|
+
state: [{
|
|
85736
|
+
kind: "numeric",
|
|
85737
|
+
field: "value",
|
|
85738
|
+
quantity: t("the total", "il totale"),
|
|
85739
|
+
label: t("Total", "Totale"),
|
|
85740
|
+
icon: "gauge"
|
|
85741
|
+
}]
|
|
85742
|
+
},
|
|
84573
85743
|
"numeric-sensor": {
|
|
84574
85744
|
scope: "device",
|
|
84575
85745
|
noun: t("Sensor", "Sensore"),
|
|
@@ -84587,7 +85757,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84587
85757
|
state: [
|
|
84588
85758
|
{
|
|
84589
85759
|
kind: "numeric",
|
|
84590
|
-
field: "
|
|
85760
|
+
field: "power",
|
|
84591
85761
|
quantity: t("the power draw", "il consumo"),
|
|
84592
85762
|
label: t("Power", "Potenza"),
|
|
84593
85763
|
unit: "W",
|
|
@@ -84595,7 +85765,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84595
85765
|
},
|
|
84596
85766
|
{
|
|
84597
85767
|
kind: "numeric",
|
|
84598
|
-
field: "
|
|
85768
|
+
field: "energy",
|
|
84599
85769
|
quantity: t("the energy used", "l’energia consumata"),
|
|
84600
85770
|
label: t("Energy", "Energia"),
|
|
84601
85771
|
unit: "kWh",
|
|
@@ -84603,7 +85773,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84603
85773
|
},
|
|
84604
85774
|
{
|
|
84605
85775
|
kind: "numeric",
|
|
84606
|
-
field: "
|
|
85776
|
+
field: "voltage",
|
|
84607
85777
|
quantity: t("the voltage", "la tensione"),
|
|
84608
85778
|
label: t("Voltage", "Tensione"),
|
|
84609
85779
|
unit: "V",
|
|
@@ -84611,7 +85781,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84611
85781
|
},
|
|
84612
85782
|
{
|
|
84613
85783
|
kind: "numeric",
|
|
84614
|
-
field: "
|
|
85784
|
+
field: "current",
|
|
84615
85785
|
quantity: t("the current", "la corrente"),
|
|
84616
85786
|
label: t("Current", "Corrente"),
|
|
84617
85787
|
unit: "A",
|
|
@@ -84628,13 +85798,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84628
85798
|
label: t("Where", "Posizione"),
|
|
84629
85799
|
phrase: t("When {device} is at {value}", "Quando {device} è in {value}"),
|
|
84630
85800
|
icon: "map-pin"
|
|
84631
|
-
}, {
|
|
84632
|
-
kind: "numeric",
|
|
84633
|
-
field: "batteryPercent",
|
|
84634
|
-
quantity: t("the phone battery", "la batteria del telefono"),
|
|
84635
|
-
label: t("Battery", "Batteria"),
|
|
84636
|
-
unit: "%",
|
|
84637
|
-
icon: "battery"
|
|
84638
85801
|
}]
|
|
84639
85802
|
},
|
|
84640
85803
|
"pressure-sensor": {
|
|
@@ -84642,7 +85805,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84642
85805
|
noun: t("Pressure", "Pressione"),
|
|
84643
85806
|
state: [{
|
|
84644
85807
|
kind: "numeric",
|
|
84645
|
-
field: "
|
|
85808
|
+
field: "pressure",
|
|
84646
85809
|
quantity: t("the pressure", "la pressione"),
|
|
84647
85810
|
label: t("Pressure", "Pressione"),
|
|
84648
85811
|
unit: "hPa",
|
|
@@ -84678,7 +85841,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84678
85841
|
noun: t("Temperature", "Temperatura"),
|
|
84679
85842
|
state: [{
|
|
84680
85843
|
kind: "numeric",
|
|
84681
|
-
field: "
|
|
85844
|
+
field: "temperature",
|
|
84682
85845
|
quantity: t("the temperature", "la temperatura"),
|
|
84683
85846
|
label: t("Temperature", "Temperatura"),
|
|
84684
85847
|
unit: "°C",
|
|
@@ -84749,7 +85912,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84749
85912
|
state: [
|
|
84750
85913
|
{
|
|
84751
85914
|
kind: "numeric",
|
|
84752
|
-
field: "
|
|
85915
|
+
field: "level",
|
|
84753
85916
|
quantity: t("the battery level", "il livello della batteria"),
|
|
84754
85917
|
label: t("Battery", "Batteria"),
|
|
84755
85918
|
unit: "%",
|
|
@@ -84791,7 +85954,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84791
85954
|
icon: "battery-charging"
|
|
84792
85955
|
}
|
|
84793
85956
|
],
|
|
84794
|
-
events: { onStatusChanged: { notOperatorFacing: "the
|
|
85957
|
+
events: { onStatusChanged: { notOperatorFacing: "the level / sleeping / charging state triggers each read one field of this change" } },
|
|
84795
85958
|
extraEvents: [{
|
|
84796
85959
|
category: EventCategory.DeviceAwake,
|
|
84797
85960
|
label: t("Woke up", "Risvegliato"),
|
|
@@ -84809,7 +85972,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84809
85972
|
noun: t("Network link", "Collegamento di rete"),
|
|
84810
85973
|
state: [{
|
|
84811
85974
|
kind: "numeric",
|
|
84812
|
-
field: "
|
|
85975
|
+
field: "signal",
|
|
84813
85976
|
quantity: t("the signal strength", "l’intensità del segnale"),
|
|
84814
85977
|
label: t("Signal", "Segnale"),
|
|
84815
85978
|
unit: "%",
|
|
@@ -84902,6 +86065,8 @@ var ML = "model and training machinery — no device action";
|
|
|
84902
86065
|
var INTERNAL = "internal plumbing between addons — never operator-facing";
|
|
84903
86066
|
/** Every system-scoped cap, with why it is not a device's vocabulary. */
|
|
84904
86067
|
var SYSTEM_CAP_VOCABULARY = {
|
|
86068
|
+
"action-source": system(INTERNAL),
|
|
86069
|
+
actions: system("declared actions — run from the Actions page and from the cameras that link them"),
|
|
84905
86070
|
"addon-pages": system(UI_PLUMBING),
|
|
84906
86071
|
"addon-pages-source": system(UI_PLUMBING),
|
|
84907
86072
|
"addon-routes": system(UI_PLUMBING),
|
|
@@ -85042,8 +86207,47 @@ function projectAction(cap, action) {
|
|
|
85042
86207
|
};
|
|
85043
86208
|
}
|
|
85044
86209
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
85045
|
-
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;
|
|
86210
|
+
DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
|
|
85046
86211
|
DeviceFeature.BatteryOperated;
|
|
86212
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
86213
|
+
var HA_AS = "a power sensor (device_class power). Home Assistant has no laundry domain. running-state stays unexported until a null reading can be published as unavailable.";
|
|
86214
|
+
var HOMEKIT_REASON = "HomeKit has no laundry-appliance service. The export tab would pair an accessory with nothing on it: running-state is withheld until a null reading can be unavailable, and power is not a HomeKit service this export honours.";
|
|
86215
|
+
var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
|
|
86216
|
+
var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
|
|
86217
|
+
var MATTER_REASON = "No Matter export is built. The closest device types are Laundry Washer (0x0073) and Laundry Dryer (0x0074); a power threshold is not the Laundry Washer Mode cluster.";
|
|
86218
|
+
function withheld(reason) {
|
|
86219
|
+
return {
|
|
86220
|
+
[DeviceType.Washer]: {
|
|
86221
|
+
exported: false,
|
|
86222
|
+
reason
|
|
86223
|
+
},
|
|
86224
|
+
[DeviceType.Dryer]: {
|
|
86225
|
+
exported: false,
|
|
86226
|
+
reason
|
|
86227
|
+
},
|
|
86228
|
+
[DeviceType.Dishwasher]: {
|
|
86229
|
+
exported: false,
|
|
86230
|
+
reason
|
|
86231
|
+
}
|
|
86232
|
+
};
|
|
86233
|
+
}
|
|
86234
|
+
function offered(received) {
|
|
86235
|
+
return {
|
|
86236
|
+
[DeviceType.Washer]: {
|
|
86237
|
+
exported: true,
|
|
86238
|
+
as: received
|
|
86239
|
+
},
|
|
86240
|
+
[DeviceType.Dryer]: {
|
|
86241
|
+
exported: true,
|
|
86242
|
+
as: received
|
|
86243
|
+
},
|
|
86244
|
+
[DeviceType.Dishwasher]: {
|
|
86245
|
+
exported: true,
|
|
86246
|
+
as: received
|
|
86247
|
+
}
|
|
86248
|
+
};
|
|
86249
|
+
}
|
|
86250
|
+
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
85047
86251
|
Object.freeze({
|
|
85048
86252
|
"accessories.getStatus": [{
|
|
85049
86253
|
name: "deviceId",
|
|
@@ -85059,6 +86263,31 @@ Object.freeze({
|
|
|
85059
86263
|
form: "single",
|
|
85060
86264
|
optional: false
|
|
85061
86265
|
}],
|
|
86266
|
+
"actions.get": [{
|
|
86267
|
+
name: "deviceId",
|
|
86268
|
+
form: "single",
|
|
86269
|
+
optional: true
|
|
86270
|
+
}],
|
|
86271
|
+
"actions.getLinks": [{
|
|
86272
|
+
name: "deviceId",
|
|
86273
|
+
form: "single",
|
|
86274
|
+
optional: false
|
|
86275
|
+
}],
|
|
86276
|
+
"actions.listForCamera": [{
|
|
86277
|
+
name: "deviceId",
|
|
86278
|
+
form: "single",
|
|
86279
|
+
optional: false
|
|
86280
|
+
}],
|
|
86281
|
+
"actions.run": [{
|
|
86282
|
+
name: "deviceId",
|
|
86283
|
+
form: "single",
|
|
86284
|
+
optional: true
|
|
86285
|
+
}],
|
|
86286
|
+
"actions.setLinks": [{
|
|
86287
|
+
name: "deviceId",
|
|
86288
|
+
form: "single",
|
|
86289
|
+
optional: false
|
|
86290
|
+
}],
|
|
85062
86291
|
"addonSettings.getDeviceSettings": [{
|
|
85063
86292
|
name: "deviceId",
|
|
85064
86293
|
form: "single",
|
|
@@ -86010,6 +87239,11 @@ Object.freeze({
|
|
|
86010
87239
|
form: "single",
|
|
86011
87240
|
optional: false
|
|
86012
87241
|
}],
|
|
87242
|
+
"groupActive.getStatus": [{
|
|
87243
|
+
name: "deviceId",
|
|
87244
|
+
form: "single",
|
|
87245
|
+
optional: false
|
|
87246
|
+
}],
|
|
86013
87247
|
"groupCommands.getStatus": [{
|
|
86014
87248
|
name: "deviceId",
|
|
86015
87249
|
form: "single",
|
|
@@ -86220,6 +87454,11 @@ Object.freeze({
|
|
|
86220
87454
|
form: "single",
|
|
86221
87455
|
optional: false
|
|
86222
87456
|
}],
|
|
87457
|
+
"meter.getStatus": [{
|
|
87458
|
+
name: "deviceId",
|
|
87459
|
+
form: "single",
|
|
87460
|
+
optional: false
|
|
87461
|
+
}],
|
|
86223
87462
|
"motion.getStatus": [{
|
|
86224
87463
|
name: "deviceId",
|
|
86225
87464
|
form: "single",
|
|
@@ -87827,7 +89066,6 @@ Object.freeze({
|
|
|
87827
89066
|
"network-access": "ingress",
|
|
87828
89067
|
"smtp-provider": "email"
|
|
87829
89068
|
});
|
|
87830
|
-
DeviceType.Camera;
|
|
87831
89069
|
/**
|
|
87832
89070
|
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
87833
89071
|
* `z.infer`, they do not restate the shape.
|
|
@@ -89221,6 +90459,7 @@ function ingestReading(input) {
|
|
|
89221
90459
|
value: converted
|
|
89222
90460
|
};
|
|
89223
90461
|
}
|
|
90462
|
+
String.raw`(?<=^|["'(\s>,\[{])`;
|
|
89224
90463
|
var MB = 1024 * 1024;
|
|
89225
90464
|
1024 * MB, 3072 * MB;
|
|
89226
90465
|
//#endregion
|
|
@@ -95653,10 +96892,10 @@ function batteryModeForUnit(unit) {
|
|
|
95653
96892
|
/** The canonical "value" field name for a typed sensor cap's slice. */
|
|
95654
96893
|
function valueFieldForCap(cap) {
|
|
95655
96894
|
switch (cap) {
|
|
95656
|
-
case "temperature-sensor": return "
|
|
95657
|
-
case "humidity-sensor": return "
|
|
95658
|
-
case "pressure-sensor": return "
|
|
95659
|
-
case "ambient-light-sensor": return "
|
|
96895
|
+
case "temperature-sensor": return "temperature";
|
|
96896
|
+
case "humidity-sensor": return "humidity";
|
|
96897
|
+
case "pressure-sensor": return "pressure";
|
|
96898
|
+
case "ambient-light-sensor": return "illuminance";
|
|
95660
96899
|
case "numeric-sensor": return "value";
|
|
95661
96900
|
}
|
|
95662
96901
|
}
|
|
@@ -95901,7 +97140,7 @@ var SUPPORTED_CAPS = new Set([
|
|
|
95901
97140
|
* - `'%'` (binary/bar batteries, already decoded to a percent) → the typed
|
|
95902
97141
|
* `battery` cap (which models a 0..100 percentage).
|
|
95903
97142
|
* - `'V'` (raw voltage, e.g. WS90/WH90/WS85 capacitor + soil/leaf push) →
|
|
95904
|
-
* `battery.
|
|
97143
|
+
* `battery.voltage`. A voltage has no honest percentage, so `level`
|
|
95905
97144
|
* stays null. The field lives on `battery`, so a numeric primary sensor
|
|
95906
97145
|
* and a voltage battery do not share a slice.
|
|
95907
97146
|
*/
|
|
@@ -95915,7 +97154,7 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
95915
97154
|
/**
|
|
95916
97155
|
* Which battery cap (if any) was registered for this sensor:
|
|
95917
97156
|
* - `'percent'` → the typed `battery` cap (percentage),
|
|
95918
|
-
* - `'voltage'` → `battery.
|
|
97157
|
+
* - `'voltage'` → `battery.voltage` (level stays null),
|
|
95919
97158
|
* - `'none'` → no battery cap.
|
|
95920
97159
|
*/
|
|
95921
97160
|
batteryMode = "none";
|
|
@@ -95986,7 +97225,7 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
95986
97225
|
this.runtimeState.setCapState(this.capName, slice);
|
|
95987
97226
|
if (this.batteryMode === "percent" && sensor.batteryUnit === "%" && typeof sensor.battery === "number") {
|
|
95988
97227
|
const next = {
|
|
95989
|
-
|
|
97228
|
+
level: Math.max(0, Math.min(100, sensor.battery)),
|
|
95990
97229
|
charging: "none",
|
|
95991
97230
|
sleeping: false,
|
|
95992
97231
|
lastUpdated: lastFetchedAt
|
|
@@ -95995,8 +97234,8 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
95995
97234
|
}
|
|
95996
97235
|
if (this.batteryMode === "voltage" && sensor.batteryUnit === "V" && typeof sensor.battery === "number" && Number.isFinite(sensor.battery)) {
|
|
95997
97236
|
const next = {
|
|
95998
|
-
|
|
95999
|
-
|
|
97237
|
+
level: null,
|
|
97238
|
+
voltage: sensor.battery,
|
|
96000
97239
|
charging: "none",
|
|
96001
97240
|
sleeping: false,
|
|
96002
97241
|
lastUpdated: lastFetchedAt
|
|
@@ -96008,7 +97247,7 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
96008
97247
|
switch (this.capName) {
|
|
96009
97248
|
case "temperature-sensor": {
|
|
96010
97249
|
const COLD = {
|
|
96011
|
-
|
|
97250
|
+
temperature: 0,
|
|
96012
97251
|
lastFetchedAt: 0
|
|
96013
97252
|
};
|
|
96014
97253
|
this.ctx.registerNativeCap(temperatureSensorCapability, { getStatus: async () => this.readSlice(COLD) });
|
|
@@ -96017,7 +97256,7 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
96017
97256
|
}
|
|
96018
97257
|
case "humidity-sensor": {
|
|
96019
97258
|
const COLD = {
|
|
96020
|
-
|
|
97259
|
+
humidity: 0,
|
|
96021
97260
|
lastFetchedAt: 0
|
|
96022
97261
|
};
|
|
96023
97262
|
this.ctx.registerNativeCap(humiditySensorCapability, { getStatus: async () => this.readSlice(COLD) });
|
|
@@ -96026,7 +97265,7 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
96026
97265
|
}
|
|
96027
97266
|
case "pressure-sensor": {
|
|
96028
97267
|
const COLD = {
|
|
96029
|
-
|
|
97268
|
+
pressure: 0,
|
|
96030
97269
|
lastFetchedAt: 0
|
|
96031
97270
|
};
|
|
96032
97271
|
this.ctx.registerNativeCap(pressureSensorCapability, { getStatus: async () => this.readSlice(COLD) });
|
|
@@ -96035,7 +97274,7 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
96035
97274
|
}
|
|
96036
97275
|
case "ambient-light-sensor": {
|
|
96037
97276
|
const COLD = {
|
|
96038
|
-
|
|
97277
|
+
illuminance: 0,
|
|
96039
97278
|
lastFetchedAt: 0
|
|
96040
97279
|
};
|
|
96041
97280
|
this.ctx.registerNativeCap(ambientLightSensorCapability, { getStatus: async () => this.readSlice(COLD) });
|
|
@@ -96054,8 +97293,8 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
96054
97293
|
}
|
|
96055
97294
|
}
|
|
96056
97295
|
/**
|
|
96057
|
-
* Register the `battery` cap. Percent batteries fill `
|
|
96058
|
-
* batteries fill `
|
|
97296
|
+
* Register the `battery` cap. Percent batteries fill `level`. Voltage
|
|
97297
|
+
* batteries fill `voltage` and leave `level` null.
|
|
96059
97298
|
*/
|
|
96060
97299
|
maybeRegisterBatteryCap() {
|
|
96061
97300
|
const sensor = this.resolveSensor();
|
|
@@ -96067,7 +97306,7 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
96067
97306
|
/** Register the typed `battery` (percentage) cap for a `%`-unit battery. */
|
|
96068
97307
|
registerPercentBatteryCap() {
|
|
96069
97308
|
const COLD = {
|
|
96070
|
-
|
|
97309
|
+
level: 0,
|
|
96071
97310
|
charging: "none",
|
|
96072
97311
|
sleeping: false,
|
|
96073
97312
|
lastUpdated: 0
|
|
@@ -96082,11 +97321,11 @@ var EcowittSensorDevice = class extends BaseDevice {
|
|
|
96082
97321
|
this.runtimeState.setCapState("battery", COLD);
|
|
96083
97322
|
this.batteryMode = "percent";
|
|
96084
97323
|
}
|
|
96085
|
-
/** Voltage batteries share the `battery` cap. `
|
|
97324
|
+
/** Voltage batteries share the `battery` cap. `level` stays unknown. */
|
|
96086
97325
|
registerVoltageBatteryCap() {
|
|
96087
97326
|
const COLD = {
|
|
96088
|
-
|
|
96089
|
-
|
|
97327
|
+
level: null,
|
|
97328
|
+
voltage: null,
|
|
96090
97329
|
charging: "unknown",
|
|
96091
97330
|
sleeping: false,
|
|
96092
97331
|
lastUpdated: 0
|