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