@camstack/addon-provider-homematic 1.2.159 → 1.2.161
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 +31 -9
- package/dist/addon.js +1523 -284
- package/dist/addon.mjs +1523 -284
- package/dist/index.js +7 -7
- package/dist/index.mjs +7 -7
- package/package.json +1 -1
package/dist/addon.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import { join } from "path";
|
|
|
9
9
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
10
10
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
11
11
|
//#endregion
|
|
12
|
-
//#region ../types/dist/event-category-
|
|
12
|
+
//#region ../types/dist/event-category-DmZDPDf_.mjs
|
|
13
13
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
14
14
|
EventCategory["SystemBoot"] = "system.boot";
|
|
15
15
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -636,7 +636,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
636
636
|
* (a track was promoted to a stationary-registry entry) or departs (the
|
|
637
637
|
* object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
|
|
638
638
|
* the dedicated "Stationary" section — never the live event feed. Payload:
|
|
639
|
-
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
|
|
639
|
+
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp, observedAt, zones }`.
|
|
640
640
|
*/
|
|
641
641
|
EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
|
|
642
642
|
/**
|
|
@@ -774,6 +774,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
774
774
|
*/
|
|
775
775
|
EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
|
|
776
776
|
/**
|
|
777
|
+
* An action run started or ended. Emitted by the `actions` surface.
|
|
778
|
+
* Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
|
|
779
|
+
*/
|
|
780
|
+
EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
|
|
781
|
+
/**
|
|
777
782
|
* Progress update from a running model conversion job.
|
|
778
783
|
* Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
|
|
779
784
|
* Emitted by `addon-model-studio` on the converting node.
|
|
@@ -5585,7 +5590,7 @@ var ZodIssueCode = {
|
|
|
5585
5590
|
var ZodFirstPartyTypeKind;
|
|
5586
5591
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5587
5592
|
//#endregion
|
|
5588
|
-
//#region ../types/dist/sleep-
|
|
5593
|
+
//#region ../types/dist/sleep-Crlmu_tO.mjs
|
|
5589
5594
|
/**
|
|
5590
5595
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5591
5596
|
* window to float samples (D455).
|
|
@@ -8893,6 +8898,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
|
|
|
8893
8898
|
/** Every capability name, so addon code can check a name without the schema graph. */
|
|
8894
8899
|
var CAP_NAMES = Object.freeze([
|
|
8895
8900
|
"accessories",
|
|
8901
|
+
"action-source",
|
|
8902
|
+
"actions",
|
|
8896
8903
|
"addon-pages",
|
|
8897
8904
|
"addon-pages-source",
|
|
8898
8905
|
"addon-routes",
|
|
@@ -8966,6 +8973,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8966
8973
|
"filesystem-browse",
|
|
8967
8974
|
"flood",
|
|
8968
8975
|
"gas",
|
|
8976
|
+
"group-active",
|
|
8969
8977
|
"group-commands",
|
|
8970
8978
|
"group-status",
|
|
8971
8979
|
"heating-valve",
|
|
@@ -8988,6 +8996,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8988
8996
|
"login-method",
|
|
8989
8997
|
"media-player",
|
|
8990
8998
|
"mesh-network",
|
|
8999
|
+
"meter",
|
|
8991
9000
|
"metrics-provider",
|
|
8992
9001
|
"model-convert",
|
|
8993
9002
|
"model-distributor",
|
|
@@ -9079,11 +9088,11 @@ var CAP_NAMES = Object.freeze([
|
|
|
9079
9088
|
Object.freeze({
|
|
9080
9089
|
"air-quality-sensor": [
|
|
9081
9090
|
{
|
|
9082
|
-
"path": "
|
|
9091
|
+
"path": "co2",
|
|
9083
9092
|
"valueType": "number"
|
|
9084
9093
|
},
|
|
9085
9094
|
{
|
|
9086
|
-
"path": "
|
|
9095
|
+
"path": "voc",
|
|
9087
9096
|
"valueType": "number"
|
|
9088
9097
|
},
|
|
9089
9098
|
{
|
|
@@ -9133,7 +9142,7 @@ Object.freeze({
|
|
|
9133
9142
|
}
|
|
9134
9143
|
],
|
|
9135
9144
|
"ambient-light-sensor": [{
|
|
9136
|
-
"path": "
|
|
9145
|
+
"path": "illuminance",
|
|
9137
9146
|
"valueType": "number"
|
|
9138
9147
|
}],
|
|
9139
9148
|
"audio-metrics": [
|
|
@@ -9194,7 +9203,7 @@ Object.freeze({
|
|
|
9194
9203
|
],
|
|
9195
9204
|
"battery": [
|
|
9196
9205
|
{
|
|
9197
|
-
"path": "
|
|
9206
|
+
"path": "level",
|
|
9198
9207
|
"valueType": "number"
|
|
9199
9208
|
},
|
|
9200
9209
|
{
|
|
@@ -9216,7 +9225,7 @@ Object.freeze({
|
|
|
9216
9225
|
"valueType": "number"
|
|
9217
9226
|
},
|
|
9218
9227
|
{
|
|
9219
|
-
"path": "
|
|
9228
|
+
"path": "voltage",
|
|
9220
9229
|
"valueType": "number"
|
|
9221
9230
|
},
|
|
9222
9231
|
{
|
|
@@ -9237,7 +9246,7 @@ Object.freeze({
|
|
|
9237
9246
|
"valueType": "boolean"
|
|
9238
9247
|
}],
|
|
9239
9248
|
"brightness": [{
|
|
9240
|
-
"path": "
|
|
9249
|
+
"path": "level",
|
|
9241
9250
|
"valueType": "number"
|
|
9242
9251
|
}],
|
|
9243
9252
|
"broker": [{
|
|
@@ -9438,7 +9447,7 @@ Object.freeze({
|
|
|
9438
9447
|
"valueType": "number"
|
|
9439
9448
|
},
|
|
9440
9449
|
{
|
|
9441
|
-
"path": "
|
|
9450
|
+
"path": "colorTemperature",
|
|
9442
9451
|
"valueType": "number"
|
|
9443
9452
|
}
|
|
9444
9453
|
],
|
|
@@ -9617,15 +9626,15 @@ Object.freeze({
|
|
|
9617
9626
|
"valueType": "string"
|
|
9618
9627
|
},
|
|
9619
9628
|
{
|
|
9620
|
-
"path": "
|
|
9629
|
+
"path": "remaining",
|
|
9621
9630
|
"valueType": "number"
|
|
9622
9631
|
},
|
|
9623
9632
|
{
|
|
9624
|
-
"path": "
|
|
9633
|
+
"path": "delayedStart",
|
|
9625
9634
|
"valueType": "number"
|
|
9626
9635
|
},
|
|
9627
9636
|
{
|
|
9628
|
-
"path": "
|
|
9637
|
+
"path": "progress",
|
|
9629
9638
|
"valueType": "number"
|
|
9630
9639
|
},
|
|
9631
9640
|
{
|
|
@@ -9739,7 +9748,7 @@ Object.freeze({
|
|
|
9739
9748
|
],
|
|
9740
9749
|
"fan-control": [
|
|
9741
9750
|
{
|
|
9742
|
-
"path": "
|
|
9751
|
+
"path": "speed",
|
|
9743
9752
|
"valueType": "number"
|
|
9744
9753
|
},
|
|
9745
9754
|
{
|
|
@@ -9786,6 +9795,10 @@ Object.freeze({
|
|
|
9786
9795
|
"path": "detected",
|
|
9787
9796
|
"valueType": "boolean"
|
|
9788
9797
|
}],
|
|
9798
|
+
"group-active": [{
|
|
9799
|
+
"path": "active",
|
|
9800
|
+
"valueType": "boolean"
|
|
9801
|
+
}],
|
|
9789
9802
|
"group-status": [
|
|
9790
9803
|
{
|
|
9791
9804
|
"path": "included",
|
|
@@ -9835,7 +9848,7 @@ Object.freeze({
|
|
|
9835
9848
|
}
|
|
9836
9849
|
],
|
|
9837
9850
|
"humidity-sensor": [{
|
|
9838
|
-
"path": "
|
|
9851
|
+
"path": "humidity",
|
|
9839
9852
|
"valueType": "number"
|
|
9840
9853
|
}],
|
|
9841
9854
|
"image": [{
|
|
@@ -9945,7 +9958,7 @@ Object.freeze({
|
|
|
9945
9958
|
"valueType": "number"
|
|
9946
9959
|
},
|
|
9947
9960
|
{
|
|
9948
|
-
"path": "
|
|
9961
|
+
"path": "progress",
|
|
9949
9962
|
"valueType": "number"
|
|
9950
9963
|
},
|
|
9951
9964
|
{
|
|
@@ -10024,11 +10037,11 @@ Object.freeze({
|
|
|
10024
10037
|
"valueType": "string"
|
|
10025
10038
|
},
|
|
10026
10039
|
{
|
|
10027
|
-
"path": "
|
|
10040
|
+
"path": "position",
|
|
10028
10041
|
"valueType": "number"
|
|
10029
10042
|
},
|
|
10030
10043
|
{
|
|
10031
|
-
"path": "
|
|
10044
|
+
"path": "duration",
|
|
10032
10045
|
"valueType": "number"
|
|
10033
10046
|
},
|
|
10034
10047
|
{
|
|
@@ -10045,6 +10058,10 @@ Object.freeze({
|
|
|
10045
10058
|
]
|
|
10046
10059
|
}
|
|
10047
10060
|
],
|
|
10061
|
+
"meter": [{
|
|
10062
|
+
"path": "value",
|
|
10063
|
+
"valueType": "number"
|
|
10064
|
+
}],
|
|
10048
10065
|
"motion": [
|
|
10049
10066
|
{
|
|
10050
10067
|
"path": "detected",
|
|
@@ -10126,11 +10143,11 @@ Object.freeze({
|
|
|
10126
10143
|
]
|
|
10127
10144
|
},
|
|
10128
10145
|
{
|
|
10129
|
-
"path": "
|
|
10146
|
+
"path": "signal",
|
|
10130
10147
|
"valueType": "number"
|
|
10131
10148
|
},
|
|
10132
10149
|
{
|
|
10133
|
-
"path": "
|
|
10150
|
+
"path": "rssi",
|
|
10134
10151
|
"valueType": "number"
|
|
10135
10152
|
},
|
|
10136
10153
|
{
|
|
@@ -10185,6 +10202,18 @@ Object.freeze({
|
|
|
10185
10202
|
"path": "foodLevel",
|
|
10186
10203
|
"valueType": "number"
|
|
10187
10204
|
},
|
|
10205
|
+
{
|
|
10206
|
+
"path": "bowlFood",
|
|
10207
|
+
"valueType": "number"
|
|
10208
|
+
},
|
|
10209
|
+
{
|
|
10210
|
+
"path": "eating",
|
|
10211
|
+
"valueType": "boolean"
|
|
10212
|
+
},
|
|
10213
|
+
{
|
|
10214
|
+
"path": "supportsMarkRefilled",
|
|
10215
|
+
"valueType": "boolean"
|
|
10216
|
+
},
|
|
10188
10217
|
{
|
|
10189
10218
|
"path": "food1",
|
|
10190
10219
|
"valueType": "number"
|
|
@@ -10249,23 +10278,23 @@ Object.freeze({
|
|
|
10249
10278
|
],
|
|
10250
10279
|
"power-meter": [
|
|
10251
10280
|
{
|
|
10252
|
-
"path": "
|
|
10281
|
+
"path": "power",
|
|
10253
10282
|
"valueType": "number"
|
|
10254
10283
|
},
|
|
10255
10284
|
{
|
|
10256
|
-
"path": "
|
|
10285
|
+
"path": "energy",
|
|
10257
10286
|
"valueType": "number"
|
|
10258
10287
|
},
|
|
10259
10288
|
{
|
|
10260
|
-
"path": "
|
|
10289
|
+
"path": "voltage",
|
|
10261
10290
|
"valueType": "number"
|
|
10262
10291
|
},
|
|
10263
10292
|
{
|
|
10264
|
-
"path": "
|
|
10293
|
+
"path": "current",
|
|
10265
10294
|
"valueType": "number"
|
|
10266
10295
|
},
|
|
10267
10296
|
{
|
|
10268
|
-
"path": "
|
|
10297
|
+
"path": "apparentPower",
|
|
10269
10298
|
"valueType": "number"
|
|
10270
10299
|
},
|
|
10271
10300
|
{
|
|
@@ -10293,14 +10322,10 @@ Object.freeze({
|
|
|
10293
10322
|
{
|
|
10294
10323
|
"path": "gps.accuracyMeters",
|
|
10295
10324
|
"valueType": "number"
|
|
10296
|
-
},
|
|
10297
|
-
{
|
|
10298
|
-
"path": "batteryPercent",
|
|
10299
|
-
"valueType": "number"
|
|
10300
10325
|
}
|
|
10301
10326
|
],
|
|
10302
10327
|
"pressure-sensor": [{
|
|
10303
|
-
"path": "
|
|
10328
|
+
"path": "pressure",
|
|
10304
10329
|
"valueType": "number"
|
|
10305
10330
|
}],
|
|
10306
10331
|
"privacy-mask": [{
|
|
@@ -10614,7 +10639,7 @@ Object.freeze({
|
|
|
10614
10639
|
"valueType": "boolean"
|
|
10615
10640
|
}],
|
|
10616
10641
|
"temperature-sensor": [{
|
|
10617
|
-
"path": "
|
|
10642
|
+
"path": "temperature",
|
|
10618
10643
|
"valueType": "number"
|
|
10619
10644
|
}],
|
|
10620
10645
|
"update": [
|
|
@@ -10662,7 +10687,7 @@ Object.freeze({
|
|
|
10662
10687
|
"valueType": "string"
|
|
10663
10688
|
},
|
|
10664
10689
|
{
|
|
10665
|
-
"path": "
|
|
10690
|
+
"path": "progress",
|
|
10666
10691
|
"valueType": "number"
|
|
10667
10692
|
},
|
|
10668
10693
|
{
|
|
@@ -10788,7 +10813,7 @@ object({
|
|
|
10788
10813
|
params: record(string(), union([string(), number()])).optional()
|
|
10789
10814
|
});
|
|
10790
10815
|
//#endregion
|
|
10791
|
-
//#region ../types/dist/err-msg-
|
|
10816
|
+
//#region ../types/dist/err-msg-DIorGYeC.mjs
|
|
10792
10817
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
10793
10818
|
DeviceType["Camera"] = "camera";
|
|
10794
10819
|
DeviceType["Hub"] = "hub";
|
|
@@ -10882,8 +10907,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
10882
10907
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
10883
10908
|
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
10884
10909
|
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
10885
|
-
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10910
|
+
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10911
|
+
* A plug-built dishwasher carries running-state and power instead. */
|
|
10886
10912
|
DeviceType["Dishwasher"] = "dishwasher";
|
|
10913
|
+
/** Washing machine. A plug-built one is running while its power stays
|
|
10914
|
+
* above a threshold. No program cap — that surface is the dishwasher's. */
|
|
10915
|
+
DeviceType["Washer"] = "washer";
|
|
10916
|
+
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
10917
|
+
DeviceType["Dryer"] = "dryer";
|
|
10887
10918
|
return DeviceType;
|
|
10888
10919
|
}({});
|
|
10889
10920
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -11141,7 +11172,7 @@ function errMsg(err) {
|
|
|
11141
11172
|
return String(err);
|
|
11142
11173
|
}
|
|
11143
11174
|
//#endregion
|
|
11144
|
-
//#region ../types/dist/composition-
|
|
11175
|
+
//#region ../types/dist/composition-CZImLpOI.mjs
|
|
11145
11176
|
/**
|
|
11146
11177
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11147
11178
|
*
|
|
@@ -11456,6 +11487,13 @@ var table = {
|
|
|
11456
11487
|
apply: () => {
|
|
11457
11488
|
throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
|
|
11458
11489
|
}
|
|
11490
|
+
},
|
|
11491
|
+
schmitt: {
|
|
11492
|
+
minArgs: 3,
|
|
11493
|
+
maxArgs: 5,
|
|
11494
|
+
apply: () => {
|
|
11495
|
+
throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
|
|
11496
|
+
}
|
|
11459
11497
|
}
|
|
11460
11498
|
};
|
|
11461
11499
|
Object.freeze(Object.assign(Object.create(null), table));
|
|
@@ -11897,6 +11935,99 @@ function validateExpressionSource(src) {
|
|
|
11897
11935
|
return null;
|
|
11898
11936
|
}
|
|
11899
11937
|
/**
|
|
11938
|
+
* One power reading becomes a washer, dryer or dishwasher (D944 helper).
|
|
11939
|
+
*
|
|
11940
|
+
* Standby of the control board is under 5 W; a motor or a heater is far
|
|
11941
|
+
* above that, so 5 W is the line. A door lock or a short pump spike is not
|
|
11942
|
+
* a cycle, so running starts only after 30 s above the line.
|
|
11943
|
+
*
|
|
11944
|
+
* A wash, dry or dishwasher pause (soak, heat-wait, heater off between
|
|
11945
|
+
* bursts) lasts several minutes. Ending the cycle on the first dip would
|
|
11946
|
+
* report "finished" in the middle of the program, so the stop dwell is
|
|
11947
|
+
* 10 minutes on every type. The number is stored per type so one machine
|
|
11948
|
+
* can move later without moving the others.
|
|
11949
|
+
*/
|
|
11950
|
+
var APPLIANCE_TEMPLATE_ID = "appliance";
|
|
11951
|
+
var APPLIANCE_KINDS = [
|
|
11952
|
+
"washer",
|
|
11953
|
+
"dryer",
|
|
11954
|
+
"dishwasher"
|
|
11955
|
+
];
|
|
11956
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
11957
|
+
/**
|
|
11958
|
+
* Statistics and integrals over a history read (D944).
|
|
11959
|
+
*
|
|
11960
|
+
* Pure: the same points always give the same answer, so a restart recomputes
|
|
11961
|
+
* rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
|
|
11962
|
+
* never the mean of the samples. A hole with no held value is not integrated
|
|
11963
|
+
* as zero. A truncated read is not published.
|
|
11964
|
+
*/
|
|
11965
|
+
var STATISTIC_STATS = [
|
|
11966
|
+
"min",
|
|
11967
|
+
"max",
|
|
11968
|
+
"mean",
|
|
11969
|
+
"sum",
|
|
11970
|
+
"count",
|
|
11971
|
+
"change"
|
|
11972
|
+
];
|
|
11973
|
+
/**
|
|
11974
|
+
* The range a statistics or integral helper asks history for (D944).
|
|
11975
|
+
*
|
|
11976
|
+
* Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
|
|
11977
|
+
* or 25 hours across a DST change. A tier is eligible only when its retention
|
|
11978
|
+
* is strictly longer than the longest that range can be. Forever qualifies.
|
|
11979
|
+
* Equal to a tier's retention is a refusal, not a short answer.
|
|
11980
|
+
*/
|
|
11981
|
+
var MINUTE_MS = 6e4;
|
|
11982
|
+
var HOUR_MS = 60 * MINUTE_MS;
|
|
11983
|
+
var DAY_MS = 24 * HOUR_MS;
|
|
11984
|
+
var ROLLING_SPANS = [
|
|
11985
|
+
"1h",
|
|
11986
|
+
"24h",
|
|
11987
|
+
"2d",
|
|
11988
|
+
"7d",
|
|
11989
|
+
"30d",
|
|
11990
|
+
"custom"
|
|
11991
|
+
];
|
|
11992
|
+
var CALENDAR_PERIODS = [
|
|
11993
|
+
"today",
|
|
11994
|
+
"yesterday",
|
|
11995
|
+
"this-week",
|
|
11996
|
+
"this-month"
|
|
11997
|
+
];
|
|
11998
|
+
var RESET_PERIODS = [
|
|
11999
|
+
"never",
|
|
12000
|
+
"daily",
|
|
12001
|
+
"weekly",
|
|
12002
|
+
"monthly"
|
|
12003
|
+
];
|
|
12004
|
+
5 * MINUTE_MS;
|
|
12005
|
+
24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
|
|
12006
|
+
7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
|
|
12007
|
+
var rollingWindowObject = object({
|
|
12008
|
+
kind: literal("rolling"),
|
|
12009
|
+
span: _enum(ROLLING_SPANS),
|
|
12010
|
+
customMinutes: number().int().min(5).optional()
|
|
12011
|
+
}).check((payload) => {
|
|
12012
|
+
if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
|
|
12013
|
+
code: "custom",
|
|
12014
|
+
message: "a custom range needs a duration in minutes",
|
|
12015
|
+
path: ["customMinutes"],
|
|
12016
|
+
input: payload.value
|
|
12017
|
+
});
|
|
12018
|
+
if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
|
|
12019
|
+
code: "custom",
|
|
12020
|
+
message: "only a custom range carries a duration",
|
|
12021
|
+
path: ["customMinutes"],
|
|
12022
|
+
input: payload.value
|
|
12023
|
+
});
|
|
12024
|
+
});
|
|
12025
|
+
var RollingWindowSchema = rollingWindowObject;
|
|
12026
|
+
var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
|
|
12027
|
+
kind: literal("calendar"),
|
|
12028
|
+
period: _enum(CALENDAR_PERIODS)
|
|
12029
|
+
})]);
|
|
12030
|
+
/**
|
|
11900
12031
|
* A composition source identity: `(addonId, stableId)`, never a numeric device
|
|
11901
12032
|
* id. Split out so helper schemas can name it without a cycle through
|
|
11902
12033
|
* `composition.ts` (D659).
|
|
@@ -11922,7 +12053,13 @@ var GROUP_TEMPLATE_IDS = [
|
|
|
11922
12053
|
"occupancy",
|
|
11923
12054
|
"binary",
|
|
11924
12055
|
"switch",
|
|
11925
|
-
"light"
|
|
12056
|
+
"light",
|
|
12057
|
+
"fan",
|
|
12058
|
+
"cover",
|
|
12059
|
+
"valve",
|
|
12060
|
+
"lock",
|
|
12061
|
+
"media-player",
|
|
12062
|
+
"climate"
|
|
11926
12063
|
];
|
|
11927
12064
|
var NUMERIC_AGGREGATES = [
|
|
11928
12065
|
"mean",
|
|
@@ -11952,14 +12089,73 @@ var HelperMemberSchema = object({
|
|
|
11952
12089
|
cap: string().min(1),
|
|
11953
12090
|
fieldPath: string().min(1)
|
|
11954
12091
|
});
|
|
11955
|
-
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
|
|
12092
|
+
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
12093
|
+
object({
|
|
12094
|
+
kind: literal("group"),
|
|
12095
|
+
templateId: GroupTemplateIdSchema,
|
|
12096
|
+
aggregate: AggregateReducerSchema,
|
|
12097
|
+
members: array(HelperMemberSchema).min(1).max(64),
|
|
12098
|
+
/** Actuator groups only. Readings leave this unset. */
|
|
12099
|
+
active: ActiveCriterionSchema.optional()
|
|
12100
|
+
}),
|
|
12101
|
+
object({
|
|
12102
|
+
kind: literal("appliance"),
|
|
12103
|
+
templateId: literal(APPLIANCE_TEMPLATE_ID),
|
|
12104
|
+
appliance: _enum(APPLIANCE_KINDS),
|
|
12105
|
+
thresholdWatts: number().positive(),
|
|
12106
|
+
startDwellMs: number().int().nonnegative(),
|
|
12107
|
+
stopDwellMs: number().int().nonnegative(),
|
|
12108
|
+
member: HelperMemberSchema
|
|
12109
|
+
}),
|
|
12110
|
+
object({
|
|
12111
|
+
kind: literal("statistic"),
|
|
12112
|
+
stat: _enum(STATISTIC_STATS),
|
|
12113
|
+
window: StatisticWindowSchema,
|
|
12114
|
+
source: HelperMemberSchema
|
|
12115
|
+
}),
|
|
12116
|
+
object({
|
|
12117
|
+
kind: literal("integral"),
|
|
12118
|
+
reset: _enum(RESET_PERIODS),
|
|
12119
|
+
output: _enum(["energy", "meter"]),
|
|
12120
|
+
source: HelperMemberSchema
|
|
12121
|
+
}),
|
|
12122
|
+
object({
|
|
12123
|
+
kind: literal("threshold"),
|
|
12124
|
+
source: HelperMemberSchema,
|
|
12125
|
+
high: number(),
|
|
12126
|
+
low: number(),
|
|
12127
|
+
onForMs: number().int().nonnegative(),
|
|
12128
|
+
offForMs: number().int().nonnegative()
|
|
12129
|
+
}),
|
|
12130
|
+
object({
|
|
12131
|
+
kind: literal("arithmetic"),
|
|
12132
|
+
op: _enum([
|
|
12133
|
+
"sum",
|
|
12134
|
+
"difference",
|
|
12135
|
+
"ratio"
|
|
12136
|
+
]),
|
|
12137
|
+
inputs: array(HelperMemberSchema).min(2).max(64)
|
|
12138
|
+
}),
|
|
12139
|
+
object({
|
|
12140
|
+
kind: literal("expression"),
|
|
12141
|
+
expr: string().min(1),
|
|
12142
|
+
inputs: array(object({
|
|
12143
|
+
name: string().min(1),
|
|
12144
|
+
member: HelperMemberSchema
|
|
12145
|
+
})).min(1).max(64),
|
|
12146
|
+
output: _enum(["number", "boolean"])
|
|
12147
|
+
}),
|
|
12148
|
+
object({
|
|
12149
|
+
kind: literal("derivative"),
|
|
12150
|
+
window: RollingWindowSchema,
|
|
12151
|
+
per: _enum([
|
|
12152
|
+
"second",
|
|
12153
|
+
"minute",
|
|
12154
|
+
"hour"
|
|
12155
|
+
]),
|
|
12156
|
+
source: HelperMemberSchema
|
|
12157
|
+
})
|
|
12158
|
+
]);
|
|
11963
12159
|
/** A composed device's stableId is this prefix plus the block id. */
|
|
11964
12160
|
var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
|
|
11965
12161
|
/** Longest snippet a `code` source may carry (slice 4). */
|
|
@@ -12020,6 +12216,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
12020
12216
|
"all"
|
|
12021
12217
|
]),
|
|
12022
12218
|
reads: array(CompositionFieldReadSchema).min(1).max(64),
|
|
12219
|
+
/**
|
|
12220
|
+
* Set when the reads are an enum state, not a yes/no: the listed states
|
|
12221
|
+
* count as active (a cover is `open`). A state outside the list is inactive;
|
|
12222
|
+
* a read that is not a state at all is left out. Only `any` / `all` use it.
|
|
12223
|
+
*/
|
|
12224
|
+
activeWhen: array(string().min(1)).min(1).optional(),
|
|
12023
12225
|
/** `value` is the reading; the others are the member counts. */
|
|
12024
12226
|
publish: _enum([
|
|
12025
12227
|
"value",
|
|
@@ -12033,6 +12235,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
12033
12235
|
"omit",
|
|
12034
12236
|
"offline"
|
|
12035
12237
|
])
|
|
12238
|
+
}),
|
|
12239
|
+
object({
|
|
12240
|
+
kind: literal("series"),
|
|
12241
|
+
read: CompositionFieldReadSchema,
|
|
12242
|
+
/** `value` is the statistic or the integral. The others are the gap counts. */
|
|
12243
|
+
publish: _enum([
|
|
12244
|
+
"value",
|
|
12245
|
+
"included",
|
|
12246
|
+
"excluded",
|
|
12247
|
+
"total"
|
|
12248
|
+
]),
|
|
12249
|
+
/**
|
|
12250
|
+
* What an unanswered `value` does. Counts ignore this. `offline` leaves the
|
|
12251
|
+
* device unseeded until history answers, so a restart cannot publish 0.
|
|
12252
|
+
*/
|
|
12253
|
+
empty: _enum([
|
|
12254
|
+
"null",
|
|
12255
|
+
"omit",
|
|
12256
|
+
"offline"
|
|
12257
|
+
])
|
|
12036
12258
|
})
|
|
12037
12259
|
]);
|
|
12038
12260
|
var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
|
|
@@ -15470,6 +15692,14 @@ var AutomationConditionLeafSchema = object({
|
|
|
15470
15692
|
array(union([string(), number()]))
|
|
15471
15693
|
])
|
|
15472
15694
|
});
|
|
15695
|
+
var AutomationConditionStationaryPresentSchema = object({
|
|
15696
|
+
kind: literal("stationary-present"),
|
|
15697
|
+
deviceId: number().int().nonnegative(),
|
|
15698
|
+
/** Class names; a macro class matches every raw class that maps to it. */
|
|
15699
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
15700
|
+
/** The parked object must be in at least one of these zones. */
|
|
15701
|
+
zones: array(string().min(1)).min(1).optional()
|
|
15702
|
+
});
|
|
15473
15703
|
/**
|
|
15474
15704
|
* The expression leaf, declared as a plain object rather than an intersection
|
|
15475
15705
|
* with {@link ExpressionSourceSchema}: a discriminated union has to be able to
|
|
@@ -15503,7 +15733,8 @@ var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
|
|
|
15503
15733
|
child: AutomationConditionSchema
|
|
15504
15734
|
}),
|
|
15505
15735
|
AutomationConditionLeafSchema,
|
|
15506
|
-
AutomationConditionExpressionSchema
|
|
15736
|
+
AutomationConditionExpressionSchema,
|
|
15737
|
+
AutomationConditionStationaryPresentSchema
|
|
15507
15738
|
]));
|
|
15508
15739
|
/**
|
|
15509
15740
|
* What starts a run.
|
|
@@ -15544,7 +15775,34 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
15544
15775
|
kind: literal("schedule"),
|
|
15545
15776
|
cron: string().min(1).max(120)
|
|
15546
15777
|
}),
|
|
15547
|
-
object({ kind: literal("manual") })
|
|
15778
|
+
object({ kind: literal("manual") }),
|
|
15779
|
+
object({
|
|
15780
|
+
kind: literal("detection"),
|
|
15781
|
+
/** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
|
|
15782
|
+
* matches every raw class that maps to it; any other name matches
|
|
15783
|
+
* exactly. */
|
|
15784
|
+
classes: array(string().min(1)).min(1),
|
|
15785
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
15786
|
+
/** The track must have visited at least one of these zones. */
|
|
15787
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
15788
|
+
/** Best-so-far confidence the track must reach. */
|
|
15789
|
+
minScore: number().min(0).max(1).optional()
|
|
15790
|
+
}),
|
|
15791
|
+
object({
|
|
15792
|
+
kind: literal("stationary"),
|
|
15793
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
15794
|
+
/** Class names; a macro class matches like the detection trigger. */
|
|
15795
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
15796
|
+
/** The parked object must be in at least one of these zones. */
|
|
15797
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
15798
|
+
/** `appeared`: it became parked. `departed`: the registry retired it.
|
|
15799
|
+
* The vocabulary of `PipelineAnalyticsStationaryChanged`. */
|
|
15800
|
+
phase: _enum(["appeared", "departed"])
|
|
15801
|
+
}),
|
|
15802
|
+
object({
|
|
15803
|
+
kind: literal("interval"),
|
|
15804
|
+
minutes: number().int().min(1).max(1440)
|
|
15805
|
+
})
|
|
15548
15806
|
]);
|
|
15549
15807
|
/**
|
|
15550
15808
|
* One action step.
|
|
@@ -15564,28 +15822,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
15564
15822
|
* is called. The runner still receives a number and is untouched; the
|
|
15565
15823
|
* resolution is the recipe's job, not the runner's.
|
|
15566
15824
|
*/
|
|
15825
|
+
var AutomationWaitStepSchema = object({
|
|
15826
|
+
kind: literal("wait"),
|
|
15827
|
+
seconds: number().min(0).max(300)
|
|
15828
|
+
});
|
|
15829
|
+
var AutomationCapStepSchema = object({
|
|
15830
|
+
kind: literal("cap"),
|
|
15831
|
+
/**
|
|
15832
|
+
* One device, several same-family devices (locks / climate family / …),
|
|
15833
|
+
* or `{ $var }` for the device that triggered the run. An array is
|
|
15834
|
+
* fan-out at codegen — each id gets the same cap.method call.
|
|
15835
|
+
*/
|
|
15836
|
+
deviceId: union([
|
|
15837
|
+
number().int(),
|
|
15838
|
+
array(number().int()).min(1),
|
|
15839
|
+
object({ $var: string().min(1) })
|
|
15840
|
+
]),
|
|
15841
|
+
cap: string().min(1),
|
|
15842
|
+
method: string().min(1),
|
|
15843
|
+
/** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
|
|
15844
|
+
* SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
|
|
15845
|
+
* the expression leaf. */
|
|
15846
|
+
args: record(string(), unknown()).optional()
|
|
15847
|
+
});
|
|
15848
|
+
/**
|
|
15849
|
+
* The steps an internal Action may contain: cap and wait, and nothing that can
|
|
15850
|
+
* call back into an Action — so an Action can never reach itself (no cycles).
|
|
15851
|
+
*/
|
|
15852
|
+
var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
|
|
15567
15853
|
var AutomationActionSchema = discriminatedUnion("kind", [
|
|
15854
|
+
AutomationWaitStepSchema,
|
|
15855
|
+
AutomationCapStepSchema,
|
|
15568
15856
|
object({
|
|
15569
|
-
kind: literal("
|
|
15570
|
-
|
|
15571
|
-
|
|
15572
|
-
|
|
15573
|
-
|
|
15574
|
-
|
|
15575
|
-
|
|
15576
|
-
|
|
15577
|
-
|
|
15578
|
-
*/
|
|
15579
|
-
deviceId: union([
|
|
15580
|
-
number().int(),
|
|
15581
|
-
array(number().int()).min(1),
|
|
15582
|
-
object({ $var: string().min(1) })
|
|
15583
|
-
]),
|
|
15584
|
-
cap: string().min(1),
|
|
15585
|
-
method: string().min(1),
|
|
15586
|
-
/** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
|
|
15587
|
-
* evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
|
|
15588
|
-
args: record(string(), unknown()).optional()
|
|
15857
|
+
kind: literal("action"),
|
|
15858
|
+
/** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
|
|
15859
|
+
actionId: string().min(1),
|
|
15860
|
+
/** Values for the action's declared variables; the others take their default. */
|
|
15861
|
+
variables: record(string(), union([
|
|
15862
|
+
string(),
|
|
15863
|
+
number(),
|
|
15864
|
+
boolean()
|
|
15865
|
+
])).optional()
|
|
15589
15866
|
}),
|
|
15590
15867
|
object({
|
|
15591
15868
|
kind: literal("code"),
|
|
@@ -15594,10 +15871,357 @@ var AutomationActionSchema = discriminatedUnion("kind", [
|
|
|
15594
15871
|
code: string().min(1).max(2e4)
|
|
15595
15872
|
})
|
|
15596
15873
|
]);
|
|
15874
|
+
/**
|
|
15875
|
+
* How a trigger that arrives while the automation is busy (or cooling down) is
|
|
15876
|
+
* handled. One of the automation's execution modes, never a property of its
|
|
15877
|
+
* actions. Absent means `single`.
|
|
15878
|
+
*
|
|
15879
|
+
* - `single` — drop the trigger while a run is in progress (traced + logged).
|
|
15880
|
+
* - `restart` — abort the run in progress (at its next step boundary), start fresh.
|
|
15881
|
+
* - `queued` — queue up to `max` triggers, drop the rest (traced).
|
|
15882
|
+
* - `parallel` — up to `max` runs at once, drop the rest (traced).
|
|
15883
|
+
* - `cooldown` — `single`, and no new run until `minutes` after the last run
|
|
15884
|
+
* STARTED; persisted across a restart. A manual trigger bypasses only this
|
|
15885
|
+
* wait, never the single/queued limits.
|
|
15886
|
+
*/
|
|
15887
|
+
var AutomationRunModeSchema = discriminatedUnion("mode", [
|
|
15888
|
+
object({ mode: literal("single") }),
|
|
15889
|
+
object({ mode: literal("restart") }),
|
|
15890
|
+
object({
|
|
15891
|
+
mode: literal("queued"),
|
|
15892
|
+
max: number().int().min(1).max(10)
|
|
15893
|
+
}),
|
|
15894
|
+
object({
|
|
15895
|
+
mode: literal("parallel"),
|
|
15896
|
+
max: number().int().min(1).max(10)
|
|
15897
|
+
}),
|
|
15898
|
+
object({
|
|
15899
|
+
mode: literal("cooldown"),
|
|
15900
|
+
minutes: number().int().min(1).max(1440)
|
|
15901
|
+
})
|
|
15902
|
+
]);
|
|
15597
15903
|
var AutomationRecipeSchema = object({
|
|
15598
15904
|
triggers: array(AutomationTriggerSchema),
|
|
15599
15905
|
conditions: AutomationConditionSchema.optional(),
|
|
15600
|
-
actions: array(AutomationActionSchema)
|
|
15906
|
+
actions: array(AutomationActionSchema),
|
|
15907
|
+
runMode: AutomationRunModeSchema.optional(),
|
|
15908
|
+
/** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
|
|
15909
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
15910
|
+
});
|
|
15911
|
+
new Set([
|
|
15912
|
+
{
|
|
15913
|
+
id: "person",
|
|
15914
|
+
name: "Person"
|
|
15915
|
+
},
|
|
15916
|
+
{
|
|
15917
|
+
id: "vehicle",
|
|
15918
|
+
name: "Vehicle"
|
|
15919
|
+
},
|
|
15920
|
+
{
|
|
15921
|
+
id: "animal",
|
|
15922
|
+
name: "Animal"
|
|
15923
|
+
},
|
|
15924
|
+
{
|
|
15925
|
+
id: "package",
|
|
15926
|
+
name: "Package"
|
|
15927
|
+
}
|
|
15928
|
+
].map((l) => l.id));
|
|
15929
|
+
var COCO_TO_MACRO = {
|
|
15930
|
+
mapping: {
|
|
15931
|
+
person: "person",
|
|
15932
|
+
bicycle: "vehicle",
|
|
15933
|
+
car: "vehicle",
|
|
15934
|
+
motorcycle: "vehicle",
|
|
15935
|
+
airplane: "vehicle",
|
|
15936
|
+
bus: "vehicle",
|
|
15937
|
+
train: "vehicle",
|
|
15938
|
+
truck: "vehicle",
|
|
15939
|
+
boat: "vehicle",
|
|
15940
|
+
bird: "animal",
|
|
15941
|
+
cat: "animal",
|
|
15942
|
+
dog: "animal",
|
|
15943
|
+
horse: "animal",
|
|
15944
|
+
sheep: "animal",
|
|
15945
|
+
cow: "animal",
|
|
15946
|
+
elephant: "animal",
|
|
15947
|
+
bear: "animal",
|
|
15948
|
+
zebra: "animal",
|
|
15949
|
+
giraffe: "animal",
|
|
15950
|
+
suitcase: "package",
|
|
15951
|
+
backpack: "package",
|
|
15952
|
+
handbag: "package"
|
|
15953
|
+
},
|
|
15954
|
+
preserveOriginal: false
|
|
15955
|
+
};
|
|
15956
|
+
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
15957
|
+
/**
|
|
15958
|
+
* Run traces — what an automation (or, later, an Action run) DID, node by node,
|
|
15959
|
+
* so the editor can paint every block green or red with its reason.
|
|
15960
|
+
*
|
|
15961
|
+
* A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
|
|
15962
|
+
* every condition node (containers and leaves, including the ones a
|
|
15963
|
+
* short-circuit never reached), the run-mode verdict and every action step —
|
|
15964
|
+
* each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
|
|
15965
|
+
* for an Action run). Skipped
|
|
15966
|
+
* evaluations are traced too, and identical consecutive skips collapse into a
|
|
15967
|
+
* repeat count so a trigger that fails every 5 minutes cannot flush real runs.
|
|
15968
|
+
*
|
|
15969
|
+
* {@link AutomationStepTraceSchema} is deliberately free of automation
|
|
15970
|
+
* vocabulary so an Action run records the same step shape.
|
|
15971
|
+
*/
|
|
15972
|
+
/** A value a trace may show: JSON primitives and short lists, nothing deeper. */
|
|
15973
|
+
var TraceValueSchema = union([
|
|
15974
|
+
string(),
|
|
15975
|
+
number(),
|
|
15976
|
+
boolean(),
|
|
15977
|
+
_null(),
|
|
15978
|
+
array(union([string(), number()]))
|
|
15979
|
+
]);
|
|
15980
|
+
var AutomationRunOutcomeSchema = _enum([
|
|
15981
|
+
"ran",
|
|
15982
|
+
"skipped-trigger",
|
|
15983
|
+
"skipped-conditions",
|
|
15984
|
+
"skipped-cooldown",
|
|
15985
|
+
"skipped-overlap",
|
|
15986
|
+
"failed",
|
|
15987
|
+
"aborted"
|
|
15988
|
+
]);
|
|
15989
|
+
/** `not-evaluated` = a short-circuit never reached the node. */
|
|
15990
|
+
var TraceNodeOutcomeSchema = _enum([
|
|
15991
|
+
"pass",
|
|
15992
|
+
"fail",
|
|
15993
|
+
"not-evaluated"
|
|
15994
|
+
]);
|
|
15995
|
+
var AutomationTriggerTraceSchema = object({
|
|
15996
|
+
kind: string().min(1),
|
|
15997
|
+
outcome: _enum(["pass", "fail"]),
|
|
15998
|
+
/** Why an event was rejected ("class person not in [dog]") or what fired. */
|
|
15999
|
+
reason: string(),
|
|
16000
|
+
/** deviceId, className, trackId, score … — only what the trigger exposes. */
|
|
16001
|
+
vars: record(string(), TraceValueSchema)
|
|
16002
|
+
});
|
|
16003
|
+
var AutomationConditionTraceNodeSchema = object({
|
|
16004
|
+
/** `conditions` for the root, `conditions.<i>` for its i-th child, … */
|
|
16005
|
+
path: string().min(1),
|
|
16006
|
+
kind: _enum([
|
|
16007
|
+
"all",
|
|
16008
|
+
"any",
|
|
16009
|
+
"not",
|
|
16010
|
+
"condition",
|
|
16011
|
+
"expression",
|
|
16012
|
+
"stationary-present"
|
|
16013
|
+
]),
|
|
16014
|
+
outcome: TraceNodeOutcomeSchema,
|
|
16015
|
+
reason: string(),
|
|
16016
|
+
deviceId: number().int().optional(),
|
|
16017
|
+
cap: string().optional(),
|
|
16018
|
+
fieldPath: string().optional(),
|
|
16019
|
+
operator: string().optional(),
|
|
16020
|
+
expected: TraceValueSchema.optional(),
|
|
16021
|
+
/** What the mirror held; `null` when it held nothing (unknown). */
|
|
16022
|
+
observed: TraceValueSchema.optional()
|
|
16023
|
+
});
|
|
16024
|
+
var AutomationRunVerdictSchema = object({
|
|
16025
|
+
mode: AutomationRunModeSchema,
|
|
16026
|
+
decision: _enum([
|
|
16027
|
+
"run",
|
|
16028
|
+
"queued",
|
|
16029
|
+
"restart",
|
|
16030
|
+
"drop",
|
|
16031
|
+
"cooldown",
|
|
16032
|
+
"bypass"
|
|
16033
|
+
]),
|
|
16034
|
+
reason: string()
|
|
16035
|
+
});
|
|
16036
|
+
/** One executed (or not-run) step — shared by automations and Action runs. */
|
|
16037
|
+
var AutomationStepTraceSchema = object({
|
|
16038
|
+
/** `actions.<i>` */
|
|
16039
|
+
path: string().min(1),
|
|
16040
|
+
kind: string().min(1),
|
|
16041
|
+
cap: string().optional(),
|
|
16042
|
+
method: string().optional(),
|
|
16043
|
+
deviceId: number().int().optional(),
|
|
16044
|
+
startedAt: number().optional(),
|
|
16045
|
+
endedAt: number().optional(),
|
|
16046
|
+
outcome: _enum([
|
|
16047
|
+
"ok",
|
|
16048
|
+
"error",
|
|
16049
|
+
"not-run"
|
|
16050
|
+
]),
|
|
16051
|
+
message: string().optional()
|
|
16052
|
+
});
|
|
16053
|
+
var AutomationRunTraceSchema = object({
|
|
16054
|
+
runId: string().min(1),
|
|
16055
|
+
startedAt: number(),
|
|
16056
|
+
endedAt: number(),
|
|
16057
|
+
trigger: AutomationTriggerTraceSchema,
|
|
16058
|
+
conditions: array(AutomationConditionTraceNodeSchema),
|
|
16059
|
+
verdict: AutomationRunVerdictSchema.optional(),
|
|
16060
|
+
steps: array(AutomationStepTraceSchema),
|
|
16061
|
+
outcome: AutomationRunOutcomeSchema,
|
|
16062
|
+
/** How many identical consecutive skips this trace stands for (>= 1). */
|
|
16063
|
+
repeat: number().int().min(1),
|
|
16064
|
+
/** When the latest of those repeats happened. */
|
|
16065
|
+
lastAt: number()
|
|
16066
|
+
});
|
|
16067
|
+
/**
|
|
16068
|
+
* What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
|
|
16069
|
+
* `action:<actionId>`): the cooldown stamp and the trace ring.
|
|
16070
|
+
*/
|
|
16071
|
+
var AutomationStateDocSchema = object({
|
|
16072
|
+
version: literal(1),
|
|
16073
|
+
lastRunStartedAt: number().nullable(),
|
|
16074
|
+
traces: array(AutomationRunTraceSchema)
|
|
16075
|
+
});
|
|
16076
|
+
/**
|
|
16077
|
+
* Actions — named, runnable things an operator (or an automation) can start,
|
|
16078
|
+
* declared as LIST ENTRIES, never as devices.
|
|
16079
|
+
*
|
|
16080
|
+
* An entry is either **internal** (a short list of cap/wait steps kept in the
|
|
16081
|
+
* hub's `actions` record kind) or declared by an addon through the
|
|
16082
|
+
* `action-source` collection (a Home Assistant script, a scene …). The one
|
|
16083
|
+
* surface the UI and automations talk to is the `actions` cap; it keeps no
|
|
16084
|
+
* catalog of its own (D3) — every `list` asks the providers live.
|
|
16085
|
+
*
|
|
16086
|
+
* This file is the data half: the entry schema, the variables an entry may
|
|
16087
|
+
* declare, and the pure resolution of the values a run was given. It holds no
|
|
16088
|
+
* runtime.
|
|
16089
|
+
*/
|
|
16090
|
+
/** What a variable's value is. `select` takes one of `options`. */
|
|
16091
|
+
var ActionVariableTypeSchema = _enum([
|
|
16092
|
+
"string",
|
|
16093
|
+
"number",
|
|
16094
|
+
"boolean",
|
|
16095
|
+
"select"
|
|
16096
|
+
]);
|
|
16097
|
+
var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
16098
|
+
var ActionVariableValueSchema = union([
|
|
16099
|
+
string(),
|
|
16100
|
+
number(),
|
|
16101
|
+
boolean()
|
|
16102
|
+
]);
|
|
16103
|
+
/**
|
|
16104
|
+
* One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
|
|
16105
|
+
* value) or `{{vars.name}}` (inside a string) in their args.
|
|
16106
|
+
*/
|
|
16107
|
+
var ActionVariableSchema = object({
|
|
16108
|
+
name: string().regex(ACTION_VARIABLE_NAME_RE),
|
|
16109
|
+
label: string().min(1).max(120).optional(),
|
|
16110
|
+
type: ActionVariableTypeSchema,
|
|
16111
|
+
required: boolean().optional(),
|
|
16112
|
+
default: ActionVariableValueSchema.optional(),
|
|
16113
|
+
/** The allowed values of a `select`. */
|
|
16114
|
+
options: array(string().min(1)).min(1).optional()
|
|
16115
|
+
}).superRefine((v, ctx) => {
|
|
16116
|
+
if (v.type === "select" && v.options === void 0) ctx.addIssue({
|
|
16117
|
+
code: "custom",
|
|
16118
|
+
message: "a select variable needs options",
|
|
16119
|
+
path: ["options"]
|
|
16120
|
+
});
|
|
16121
|
+
if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
|
|
16122
|
+
code: "custom",
|
|
16123
|
+
message: variableValueError(v, v.default) ?? "invalid default",
|
|
16124
|
+
path: ["default"]
|
|
16125
|
+
});
|
|
16126
|
+
});
|
|
16127
|
+
/** Why `value` is not a legal value for the variable, or null when it is. */
|
|
16128
|
+
function variableValueError(variable, value) {
|
|
16129
|
+
switch (variable.type) {
|
|
16130
|
+
case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
|
|
16131
|
+
case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
|
|
16132
|
+
case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
|
|
16133
|
+
case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
|
|
16134
|
+
}
|
|
16135
|
+
}
|
|
16136
|
+
var ActionSourceKindSchema = _enum(["internal", "addon"]);
|
|
16137
|
+
function isInternalActionId(actionId) {
|
|
16138
|
+
return actionId.startsWith("internal:") && actionId.length > 9;
|
|
16139
|
+
}
|
|
16140
|
+
/** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
|
|
16141
|
+
function addonIdOfActionId(actionId) {
|
|
16142
|
+
if (isInternalActionId(actionId)) return null;
|
|
16143
|
+
const at = actionId.indexOf(":");
|
|
16144
|
+
return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
|
|
16145
|
+
}
|
|
16146
|
+
var ActionStatusSchema = object({
|
|
16147
|
+
isRunning: boolean(),
|
|
16148
|
+
/** Ms epoch the latest run STARTED; null when it never ran. */
|
|
16149
|
+
lastRunAt: number().nullable(),
|
|
16150
|
+
/** The latest run's failure; null on success or when it never ran. */
|
|
16151
|
+
lastError: string().nullable()
|
|
16152
|
+
});
|
|
16153
|
+
var ActionEntrySchema = object({
|
|
16154
|
+
/** `internal:<uuid>` or `<addonId>:<localId>`. */
|
|
16155
|
+
id: string().min(3),
|
|
16156
|
+
name: string().min(1).max(120),
|
|
16157
|
+
/** The addon that declared it; `core-blocks` for an internal one. */
|
|
16158
|
+
addonId: string().min(1),
|
|
16159
|
+
source: ActionSourceKindSchema,
|
|
16160
|
+
/** An addon-declared entry is edited where it was declared. */
|
|
16161
|
+
readOnly: boolean(),
|
|
16162
|
+
/** Internal only: cap and wait steps. No action-in-action, so no cycles. */
|
|
16163
|
+
steps: array(ActionStepSchema).min(1).max(50).optional(),
|
|
16164
|
+
variables: array(ActionVariableSchema).max(20).optional(),
|
|
16165
|
+
status: ActionStatusSchema
|
|
16166
|
+
}).superRefine((entry, ctx) => {
|
|
16167
|
+
if (entry.source === "internal") {
|
|
16168
|
+
if (!isInternalActionId(entry.id)) ctx.addIssue({
|
|
16169
|
+
code: "custom",
|
|
16170
|
+
message: "an internal action id is internal:<uuid>",
|
|
16171
|
+
path: ["id"]
|
|
16172
|
+
});
|
|
16173
|
+
return;
|
|
16174
|
+
}
|
|
16175
|
+
if (entry.steps !== void 0) ctx.addIssue({
|
|
16176
|
+
code: "custom",
|
|
16177
|
+
message: "only an internal action carries steps",
|
|
16178
|
+
path: ["steps"]
|
|
16179
|
+
});
|
|
16180
|
+
if (!entry.readOnly) ctx.addIssue({
|
|
16181
|
+
code: "custom",
|
|
16182
|
+
message: "an addon action is read-only here",
|
|
16183
|
+
path: ["readOnly"]
|
|
16184
|
+
});
|
|
16185
|
+
if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
|
|
16186
|
+
code: "custom",
|
|
16187
|
+
message: "an addon action id is <addonId>:<localId>",
|
|
16188
|
+
path: ["id"]
|
|
16189
|
+
});
|
|
16190
|
+
});
|
|
16191
|
+
/** What an operator writes for an internal action. */
|
|
16192
|
+
var ActionInputSchema = object({
|
|
16193
|
+
name: string().min(1).max(120),
|
|
16194
|
+
steps: array(ActionStepSchema).min(1).max(50),
|
|
16195
|
+
variables: array(ActionVariableSchema).max(20).optional()
|
|
16196
|
+
});
|
|
16197
|
+
/** Who started a run. Absent = the operator pressed it. */
|
|
16198
|
+
var ActionRunOriginSchema = object({
|
|
16199
|
+
/** The automation block whose step started it. */
|
|
16200
|
+
blockId: string().min(1).optional() });
|
|
16201
|
+
/** `started` = the caller did not ask to wait. */
|
|
16202
|
+
var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
|
|
16203
|
+
var ActionRunResultSchema = object({
|
|
16204
|
+
runId: string().min(1),
|
|
16205
|
+
outcome: ActionRunOutcomeSchema,
|
|
16206
|
+
/** Why it did not run, or what failed. */
|
|
16207
|
+
message: string().optional()
|
|
16208
|
+
});
|
|
16209
|
+
_enum([
|
|
16210
|
+
"not-found",
|
|
16211
|
+
"read-only",
|
|
16212
|
+
"invalid-variables",
|
|
16213
|
+
"invalid-steps",
|
|
16214
|
+
"not-linked",
|
|
16215
|
+
"busy"
|
|
16216
|
+
]);
|
|
16217
|
+
object({
|
|
16218
|
+
actionId: string(),
|
|
16219
|
+
runId: string(),
|
|
16220
|
+
isRunning: boolean(),
|
|
16221
|
+
/** Set when the run ended. */
|
|
16222
|
+
outcome: AutomationRunOutcomeSchema.optional(),
|
|
16223
|
+
lastRunAt: number().nullable(),
|
|
16224
|
+
lastError: string().nullable()
|
|
15601
16225
|
});
|
|
15602
16226
|
/**
|
|
15603
16227
|
* `addon-pages` — system-scoped singleton aggregator cap. Public-facing
|
|
@@ -17192,7 +17816,22 @@ var BrokerProviderInfoSchema = object({
|
|
|
17192
17816
|
*/
|
|
17193
17817
|
interactiveConnectPath: string().min(1).optional(),
|
|
17194
17818
|
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
17195
|
-
interactiveDisconnectPath: string().min(1).optional()
|
|
17819
|
+
interactiveDisconnectPath: string().min(1).optional(),
|
|
17820
|
+
/**
|
|
17821
|
+
* The kind signs in through a server-held interactive session instead of
|
|
17822
|
+
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
17823
|
+
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
17824
|
+
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
17825
|
+
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
17826
|
+
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
17827
|
+
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
17828
|
+
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
17829
|
+
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
17830
|
+
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
17831
|
+
* offers it as the default path; the fields the session fills are
|
|
17832
|
+
* marked `filledBySignIn` in the kind's settings schema.
|
|
17833
|
+
*/
|
|
17834
|
+
signIn: object({ basePath: string().min(1) }).optional()
|
|
17196
17835
|
}))
|
|
17197
17836
|
});
|
|
17198
17837
|
var ListInputSchema = object({
|
|
@@ -18318,7 +18957,7 @@ var CompositionEventPlanSchema = object({
|
|
|
18318
18957
|
stateField: string(),
|
|
18319
18958
|
fieldKey: string()
|
|
18320
18959
|
});
|
|
18321
|
-
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.
|
|
18960
|
+
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
|
|
18322
18961
|
var CompositionProblemSchema = object({
|
|
18323
18962
|
code: _enum([
|
|
18324
18963
|
"reserved-variant",
|
|
@@ -18346,7 +18985,8 @@ var CompositionProblemSchema = object({
|
|
|
18346
18985
|
"native-capability-replace",
|
|
18347
18986
|
"mixed-quantity",
|
|
18348
18987
|
"helper-mismatch",
|
|
18349
|
-
"member-constraint"
|
|
18988
|
+
"member-constraint",
|
|
18989
|
+
"retention"
|
|
18350
18990
|
]),
|
|
18351
18991
|
path: string(),
|
|
18352
18992
|
message: string()
|
|
@@ -18580,13 +19220,17 @@ Object.freeze({
|
|
|
18580
19220
|
latchedAt: {
|
|
18581
19221
|
kind: "fixed",
|
|
18582
19222
|
result: ["number", "null"]
|
|
19223
|
+
},
|
|
19224
|
+
schmitt: {
|
|
19225
|
+
kind: "fixed",
|
|
19226
|
+
result: ["boolean", "null"]
|
|
18583
19227
|
}
|
|
18584
19228
|
});
|
|
18585
19229
|
/**
|
|
18586
19230
|
* One catalog row per base reading a group helper can build (D944 §3).
|
|
18587
19231
|
* Data, not a form default written into the block.
|
|
18588
19232
|
*/
|
|
18589
|
-
var NOW = {
|
|
19233
|
+
var NOW$2 = {
|
|
18590
19234
|
fieldPath: "lastFetchedAt",
|
|
18591
19235
|
expr: "now"
|
|
18592
19236
|
};
|
|
@@ -18600,6 +19244,8 @@ function numeric(input) {
|
|
|
18600
19244
|
family: "numeric",
|
|
18601
19245
|
deviceType: DeviceType.Sensor,
|
|
18602
19246
|
memberDeviceType: DeviceType.Sensor,
|
|
19247
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
19248
|
+
activeWhen: void 0,
|
|
18603
19249
|
commandCaps: [],
|
|
18604
19250
|
baseUnit: baseUnitOf(input.quantity),
|
|
18605
19251
|
aggregates: NUMERIC_AGGREGATES,
|
|
@@ -18615,6 +19261,8 @@ function binary(input) {
|
|
|
18615
19261
|
family: "binary",
|
|
18616
19262
|
deviceType: DeviceType.Sensor,
|
|
18617
19263
|
memberDeviceType: DeviceType.Sensor,
|
|
19264
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
19265
|
+
activeWhen: void 0,
|
|
18618
19266
|
commandCaps: [],
|
|
18619
19267
|
quantity: "none",
|
|
18620
19268
|
baseUnit: void 0,
|
|
@@ -18628,73 +19276,73 @@ var GROUP_TEMPLATES = [
|
|
|
18628
19276
|
summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
|
|
18629
19277
|
role: DeviceRole.TemperatureSensor,
|
|
18630
19278
|
outputCap: "temperature-sensor",
|
|
18631
|
-
outputField: "
|
|
19279
|
+
outputField: "temperature",
|
|
18632
19280
|
quantity: "temperature",
|
|
18633
19281
|
defaultAggregate: "mean",
|
|
18634
19282
|
empty: "null",
|
|
18635
|
-
companions: [NOW]
|
|
19283
|
+
companions: [NOW$2]
|
|
18636
19284
|
}),
|
|
18637
19285
|
numeric({
|
|
18638
19286
|
id: "humidity",
|
|
18639
19287
|
summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18640
19288
|
role: DeviceRole.HumiditySensor,
|
|
18641
19289
|
outputCap: "humidity-sensor",
|
|
18642
|
-
outputField: "
|
|
19290
|
+
outputField: "humidity",
|
|
18643
19291
|
quantity: "humidity",
|
|
18644
19292
|
defaultAggregate: "mean",
|
|
18645
19293
|
empty: "null",
|
|
18646
|
-
companions: [NOW]
|
|
19294
|
+
companions: [NOW$2]
|
|
18647
19295
|
}),
|
|
18648
19296
|
numeric({
|
|
18649
19297
|
id: "pressure",
|
|
18650
19298
|
summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
|
|
18651
19299
|
role: DeviceRole.PressureSensor,
|
|
18652
19300
|
outputCap: "pressure-sensor",
|
|
18653
|
-
outputField: "
|
|
19301
|
+
outputField: "pressure",
|
|
18654
19302
|
quantity: "pressure",
|
|
18655
19303
|
defaultAggregate: "mean",
|
|
18656
19304
|
empty: "null",
|
|
18657
|
-
companions: [NOW]
|
|
19305
|
+
companions: [NOW$2]
|
|
18658
19306
|
}),
|
|
18659
19307
|
numeric({
|
|
18660
19308
|
id: "illuminance",
|
|
18661
19309
|
summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
|
|
18662
19310
|
role: DeviceRole.AmbientLightSensor,
|
|
18663
19311
|
outputCap: "ambient-light-sensor",
|
|
18664
|
-
outputField: "
|
|
19312
|
+
outputField: "illuminance",
|
|
18665
19313
|
quantity: "illuminance",
|
|
18666
19314
|
defaultAggregate: "mean",
|
|
18667
19315
|
empty: "null",
|
|
18668
|
-
companions: [NOW]
|
|
19316
|
+
companions: [NOW$2]
|
|
18669
19317
|
}),
|
|
18670
19318
|
numeric({
|
|
18671
19319
|
id: "power",
|
|
18672
19320
|
summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
|
|
18673
19321
|
role: DeviceRole.PowerSensor,
|
|
18674
19322
|
outputCap: "power-meter",
|
|
18675
|
-
outputField: "
|
|
19323
|
+
outputField: "power",
|
|
18676
19324
|
quantity: "power",
|
|
18677
19325
|
defaultAggregate: "sum",
|
|
18678
19326
|
empty: "omit",
|
|
18679
|
-
companions: [NOW]
|
|
19327
|
+
companions: [NOW$2]
|
|
18680
19328
|
}),
|
|
18681
19329
|
numeric({
|
|
18682
19330
|
id: "energy",
|
|
18683
19331
|
summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
|
|
18684
19332
|
role: DeviceRole.EnergySensor,
|
|
18685
19333
|
outputCap: "power-meter",
|
|
18686
|
-
outputField: "
|
|
19334
|
+
outputField: "energy",
|
|
18687
19335
|
quantity: "energy",
|
|
18688
19336
|
defaultAggregate: "sum",
|
|
18689
19337
|
empty: "omit",
|
|
18690
|
-
companions: [NOW]
|
|
19338
|
+
companions: [NOW$2]
|
|
18691
19339
|
}),
|
|
18692
19340
|
numeric({
|
|
18693
19341
|
id: "battery",
|
|
18694
19342
|
summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18695
19343
|
role: DeviceRole.BatterySensor,
|
|
18696
19344
|
outputCap: "battery",
|
|
18697
|
-
outputField: "
|
|
19345
|
+
outputField: "level",
|
|
18698
19346
|
quantity: "percentage",
|
|
18699
19347
|
defaultAggregate: "mean",
|
|
18700
19348
|
empty: "null",
|
|
@@ -18722,7 +19370,7 @@ var GROUP_TEMPLATES = [
|
|
|
18722
19370
|
quantity: "device",
|
|
18723
19371
|
defaultAggregate: "mean",
|
|
18724
19372
|
empty: "null",
|
|
18725
|
-
companions: [NOW]
|
|
19373
|
+
companions: [NOW$2]
|
|
18726
19374
|
}),
|
|
18727
19375
|
binary({
|
|
18728
19376
|
id: "motion",
|
|
@@ -18816,6 +19464,11 @@ var GROUP_TEMPLATES = [
|
|
|
18816
19464
|
summary: "On when at least one switch is on, or when all of them are. Offline and unknown members are left out of that decision, never read as off, and still receive the command.",
|
|
18817
19465
|
deviceType: DeviceType.Switch,
|
|
18818
19466
|
outputCap: "switch",
|
|
19467
|
+
outputField: "on",
|
|
19468
|
+
member: {
|
|
19469
|
+
cap: "switch",
|
|
19470
|
+
fieldPath: "on"
|
|
19471
|
+
},
|
|
18819
19472
|
commandCaps: ["switch"]
|
|
18820
19473
|
}),
|
|
18821
19474
|
actuator({
|
|
@@ -18823,34 +19476,136 @@ var GROUP_TEMPLATES = [
|
|
|
18823
19476
|
summary: "On when at least one light is on, or when all of them are. Brightness and colour are offered only when every member has them. A command is not sent to a member that lacks it.",
|
|
18824
19477
|
deviceType: DeviceType.Light,
|
|
18825
19478
|
outputCap: "switch",
|
|
19479
|
+
outputField: "on",
|
|
19480
|
+
member: {
|
|
19481
|
+
cap: "switch",
|
|
19482
|
+
fieldPath: "on"
|
|
19483
|
+
},
|
|
18826
19484
|
commandCaps: [
|
|
18827
19485
|
"switch",
|
|
18828
19486
|
"brightness",
|
|
18829
19487
|
"color"
|
|
18830
19488
|
]
|
|
19489
|
+
}),
|
|
19490
|
+
actuator({
|
|
19491
|
+
id: "fan",
|
|
19492
|
+
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.",
|
|
19493
|
+
deviceType: DeviceType.Fan,
|
|
19494
|
+
outputCap: "switch",
|
|
19495
|
+
outputField: "on",
|
|
19496
|
+
member: {
|
|
19497
|
+
cap: "switch",
|
|
19498
|
+
fieldPath: "on"
|
|
19499
|
+
},
|
|
19500
|
+
commandCaps: ["switch", "fan-control"]
|
|
19501
|
+
}),
|
|
19502
|
+
actuator({
|
|
19503
|
+
id: "cover",
|
|
19504
|
+
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.",
|
|
19505
|
+
deviceType: DeviceType.Cover,
|
|
19506
|
+
outputCap: "group-active",
|
|
19507
|
+
outputField: "active",
|
|
19508
|
+
member: {
|
|
19509
|
+
cap: "cover",
|
|
19510
|
+
fieldPath: "state"
|
|
19511
|
+
},
|
|
19512
|
+
activeWhen: [
|
|
19513
|
+
"open",
|
|
19514
|
+
"opening",
|
|
19515
|
+
"stopped"
|
|
19516
|
+
],
|
|
19517
|
+
commandCaps: ["cover"]
|
|
19518
|
+
}),
|
|
19519
|
+
actuator({
|
|
19520
|
+
id: "valve",
|
|
19521
|
+
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.",
|
|
19522
|
+
deviceType: DeviceType.Valve,
|
|
19523
|
+
outputCap: "group-active",
|
|
19524
|
+
outputField: "active",
|
|
19525
|
+
member: {
|
|
19526
|
+
cap: "valve",
|
|
19527
|
+
fieldPath: "state"
|
|
19528
|
+
},
|
|
19529
|
+
activeWhen: [
|
|
19530
|
+
"open",
|
|
19531
|
+
"opening",
|
|
19532
|
+
"stopped"
|
|
19533
|
+
],
|
|
19534
|
+
commandCaps: ["valve"]
|
|
19535
|
+
}),
|
|
19536
|
+
actuator({
|
|
19537
|
+
id: "lock",
|
|
19538
|
+
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.",
|
|
19539
|
+
deviceType: DeviceType.Lock,
|
|
19540
|
+
outputCap: "group-active",
|
|
19541
|
+
outputField: "active",
|
|
19542
|
+
member: {
|
|
19543
|
+
cap: "lock-control",
|
|
19544
|
+
fieldPath: "state"
|
|
19545
|
+
},
|
|
19546
|
+
activeWhen: ["locked"],
|
|
19547
|
+
commandCaps: ["lock-control"]
|
|
19548
|
+
}),
|
|
19549
|
+
actuator({
|
|
19550
|
+
id: "media-player",
|
|
19551
|
+
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.",
|
|
19552
|
+
deviceType: DeviceType.MediaPlayer,
|
|
19553
|
+
outputCap: "group-active",
|
|
19554
|
+
outputField: "active",
|
|
19555
|
+
member: {
|
|
19556
|
+
cap: "media-player",
|
|
19557
|
+
fieldPath: "state"
|
|
19558
|
+
},
|
|
19559
|
+
activeWhen: ["playing", "buffering"],
|
|
19560
|
+
commandCaps: ["media-player"]
|
|
19561
|
+
}),
|
|
19562
|
+
actuator({
|
|
19563
|
+
id: "climate",
|
|
19564
|
+
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.",
|
|
19565
|
+
deviceType: DeviceType.Thermostat,
|
|
19566
|
+
memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
|
|
19567
|
+
outputCap: "group-active",
|
|
19568
|
+
outputField: "active",
|
|
19569
|
+
member: {
|
|
19570
|
+
cap: "climate-control",
|
|
19571
|
+
fieldPath: "mode"
|
|
19572
|
+
},
|
|
19573
|
+
activeWhen: [
|
|
19574
|
+
"heat",
|
|
19575
|
+
"cool",
|
|
19576
|
+
"auto",
|
|
19577
|
+
"heat_cool",
|
|
19578
|
+
"fan_only",
|
|
19579
|
+
"dry"
|
|
19580
|
+
],
|
|
19581
|
+
commandCaps: ["climate-control"]
|
|
18831
19582
|
})
|
|
18832
19583
|
];
|
|
18833
19584
|
function actuator(input) {
|
|
18834
19585
|
return {
|
|
18835
|
-
|
|
19586
|
+
id: input.id,
|
|
19587
|
+
summary: input.summary,
|
|
19588
|
+
deviceType: input.deviceType,
|
|
19589
|
+
outputCap: input.outputCap,
|
|
19590
|
+
outputField: input.outputField,
|
|
19591
|
+
commandCaps: input.commandCaps,
|
|
18836
19592
|
family: "actuator",
|
|
18837
19593
|
role: void 0,
|
|
18838
19594
|
memberDeviceType: input.deviceType,
|
|
18839
|
-
|
|
19595
|
+
memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
|
|
19596
|
+
activeWhen: input.activeWhen,
|
|
18840
19597
|
quantity: "none",
|
|
18841
19598
|
baseUnit: void 0,
|
|
18842
19599
|
aggregates: BINARY_AGGREGATES,
|
|
18843
19600
|
defaultAggregate: "any",
|
|
18844
19601
|
empty: "offline",
|
|
18845
|
-
accept: [
|
|
18846
|
-
cap: "switch",
|
|
18847
|
-
fieldPath: "on"
|
|
18848
|
-
}],
|
|
19602
|
+
accept: [input.member],
|
|
18849
19603
|
companions: [CHANGED]
|
|
18850
19604
|
};
|
|
18851
19605
|
}
|
|
18852
19606
|
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
18853
19607
|
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
19608
|
+
DeviceType.Camera;
|
|
18854
19609
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
18855
19610
|
/**
|
|
18856
19611
|
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
@@ -19165,7 +19920,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
19165
19920
|
kind: "mutation",
|
|
19166
19921
|
auth: "admin",
|
|
19167
19922
|
access: "view"
|
|
19168
|
-
})
|
|
19923
|
+
}), method(object({
|
|
19924
|
+
blockId: string().min(1),
|
|
19925
|
+
trace: AutomationRunTraceSchema.optional(),
|
|
19926
|
+
runStartedAt: number().optional(),
|
|
19927
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
19928
|
+
}), _void(), {
|
|
19929
|
+
kind: "mutation",
|
|
19930
|
+
auth: "admin"
|
|
19931
|
+
}), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
|
|
19932
|
+
traces: array(AutomationRunTraceSchema),
|
|
19933
|
+
lastError: string().nullable()
|
|
19934
|
+
}), { auth: "admin" });
|
|
19169
19935
|
/**
|
|
19170
19936
|
* `custom-model-registry` — collection cap exposing operator-registered
|
|
19171
19937
|
* custom detection models. Each provider (today: `addon-model-studio`)
|
|
@@ -44261,7 +45027,7 @@ var NetworkAccessStatusSchema = object({
|
|
|
44261
45027
|
});
|
|
44262
45028
|
/**
|
|
44263
45029
|
* Optional, richer endpoint shape returned by providers that expose
|
|
44264
|
-
* MORE than one ingress concurrently (Tailscale
|
|
45030
|
+
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
44265
45031
|
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
44266
45032
|
* the originating provider config (mode + sourcePort) so the
|
|
44267
45033
|
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
@@ -44654,51 +45420,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
44654
45420
|
kind: "mutation",
|
|
44655
45421
|
auth: "admin"
|
|
44656
45422
|
});
|
|
44657
|
-
new Set([
|
|
44658
|
-
{
|
|
44659
|
-
id: "person",
|
|
44660
|
-
name: "Person"
|
|
44661
|
-
},
|
|
44662
|
-
{
|
|
44663
|
-
id: "vehicle",
|
|
44664
|
-
name: "Vehicle"
|
|
44665
|
-
},
|
|
44666
|
-
{
|
|
44667
|
-
id: "animal",
|
|
44668
|
-
name: "Animal"
|
|
44669
|
-
},
|
|
44670
|
-
{
|
|
44671
|
-
id: "package",
|
|
44672
|
-
name: "Package"
|
|
44673
|
-
}
|
|
44674
|
-
].map((l) => l.id));
|
|
44675
|
-
var COCO_TO_MACRO = {
|
|
44676
|
-
mapping: {
|
|
44677
|
-
person: "person",
|
|
44678
|
-
bicycle: "vehicle",
|
|
44679
|
-
car: "vehicle",
|
|
44680
|
-
motorcycle: "vehicle",
|
|
44681
|
-
airplane: "vehicle",
|
|
44682
|
-
bus: "vehicle",
|
|
44683
|
-
train: "vehicle",
|
|
44684
|
-
truck: "vehicle",
|
|
44685
|
-
boat: "vehicle",
|
|
44686
|
-
bird: "animal",
|
|
44687
|
-
cat: "animal",
|
|
44688
|
-
dog: "animal",
|
|
44689
|
-
horse: "animal",
|
|
44690
|
-
sheep: "animal",
|
|
44691
|
-
cow: "animal",
|
|
44692
|
-
elephant: "animal",
|
|
44693
|
-
bear: "animal",
|
|
44694
|
-
zebra: "animal",
|
|
44695
|
-
giraffe: "animal",
|
|
44696
|
-
suitcase: "package",
|
|
44697
|
-
backpack: "package",
|
|
44698
|
-
handbag: "package"
|
|
44699
|
-
},
|
|
44700
|
-
preserveOriginal: false
|
|
44701
|
-
};
|
|
44702
45423
|
var AUDIO_MACRO_LABELS = [
|
|
44703
45424
|
{
|
|
44704
45425
|
id: "speech",
|
|
@@ -46948,7 +47669,20 @@ var NcRuleInputSchema = object({
|
|
|
46948
47669
|
* save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
|
|
46949
47670
|
* path does not run Zod defaults.
|
|
46950
47671
|
*/
|
|
46951
|
-
groupingSec: number().int().min(5).max(60).optional()
|
|
47672
|
+
groupingSec: number().int().min(5).max(60).optional(),
|
|
47673
|
+
/**
|
|
47674
|
+
* ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
|
|
47675
|
+
* carries "Correct" / "Wrong" buttons (and, where the channel has room, a
|
|
47676
|
+
* "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
|
|
47677
|
+
* same mark the Library writes, and records whose notification it was and
|
|
47678
|
+
* when. A channel with no buttons gets the same two choices as links.
|
|
47679
|
+
*
|
|
47680
|
+
* `.optional()`, never `.default()` (a Zod default does not run on the addon
|
|
47681
|
+
* cap path): absent and `false` both mean off, which is what every rule
|
|
47682
|
+
* authored before this field meant. An editor turns it off by writing
|
|
47683
|
+
* `false`, so it needs no `clear` key.
|
|
47684
|
+
*/
|
|
47685
|
+
askVerdict: boolean().optional()
|
|
46952
47686
|
});
|
|
46953
47687
|
/**
|
|
46954
47688
|
* Partial patch for `updateRule` — any subset of the input fields, plus the
|
|
@@ -57252,9 +57986,9 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
57252
57986
|
*/
|
|
57253
57987
|
var AirQualitySensorStatusSchema = object({
|
|
57254
57988
|
/** Carbon dioxide concentration in ppm. */
|
|
57255
|
-
|
|
57989
|
+
co2: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
57256
57990
|
/** Total volatile organic compounds in ppb. */
|
|
57257
|
-
|
|
57991
|
+
voc: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
57258
57992
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
57259
57993
|
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
57260
57994
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
@@ -57287,12 +58021,12 @@ var airQualitySensorCapability = {
|
|
|
57287
58021
|
* History (D850): recorded series.
|
|
57288
58022
|
*/
|
|
57289
58023
|
history: { fields: {
|
|
57290
|
-
|
|
58024
|
+
co2: {
|
|
57291
58025
|
kind: "gauge",
|
|
57292
58026
|
unit: "ppm",
|
|
57293
58027
|
deadband: 5
|
|
57294
58028
|
},
|
|
57295
|
-
|
|
58029
|
+
voc: {
|
|
57296
58030
|
kind: "gauge",
|
|
57297
58031
|
unit: "ppb",
|
|
57298
58032
|
deadband: 5
|
|
@@ -57323,7 +58057,7 @@ var airQualitySensorCapability = {
|
|
|
57323
58057
|
*/
|
|
57324
58058
|
var AmbientLightSensorStatusSchema = object({
|
|
57325
58059
|
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
57326
|
-
|
|
58060
|
+
illuminance: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
57327
58061
|
precision: 0,
|
|
57328
58062
|
label: "Illuminance"
|
|
57329
58063
|
})),
|
|
@@ -57352,7 +58086,7 @@ var ambientLightSensorCapability = {
|
|
|
57352
58086
|
/**
|
|
57353
58087
|
* History (D850): recorded series. Wide range, so a RELATIVE deadband (10 %).
|
|
57354
58088
|
*/
|
|
57355
|
-
history: { fields: {
|
|
58089
|
+
history: { fields: { illuminance: {
|
|
57356
58090
|
kind: "gauge",
|
|
57357
58091
|
unit: "lx",
|
|
57358
58092
|
deadbandPct: 10,
|
|
@@ -57491,6 +58225,84 @@ var audioMetricsCapability = {
|
|
|
57491
58225
|
*/
|
|
57492
58226
|
history: "none"
|
|
57493
58227
|
};
|
|
58228
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
58229
|
+
actionId: string().min(1),
|
|
58230
|
+
variables: record(string(), ActionVariableValueSchema).optional()
|
|
58231
|
+
}), _void(), {
|
|
58232
|
+
kind: "mutation",
|
|
58233
|
+
auth: "admin"
|
|
58234
|
+
}), method(object({ actionId: string().min(1) }), _void(), {
|
|
58235
|
+
kind: "mutation",
|
|
58236
|
+
auth: "admin"
|
|
58237
|
+
});
|
|
58238
|
+
/**
|
|
58239
|
+
* actions — the ONE surface for runnable actions, for the UI and for
|
|
58240
|
+
* automations. Provided by the core-blocks builtin on the hub.
|
|
58241
|
+
*
|
|
58242
|
+
* An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
|
|
58243
|
+
* steps, kept in the hub's `actions` record kind) or declared by an addon over
|
|
58244
|
+
* `action-source`. `list` asks every `action-source` provider LIVE and overlays
|
|
58245
|
+
* the run status from the hub's `runs` ledger; there is no catalog here to
|
|
58246
|
+
* drift (D3).
|
|
58247
|
+
*
|
|
58248
|
+
* **Permissions follow the camera, as linked devices do.** An action is linked
|
|
58249
|
+
* to cameras by the operator (`setLinks`, D356: nothing links itself). A
|
|
58250
|
+
* non-admin may `get` / `run` an action only through a camera they may see that
|
|
58251
|
+
* links it: they name the camera (`deviceId`, a top-level field so the scope
|
|
58252
|
+
* gate sees it) and the provider refuses an action that camera does not link.
|
|
58253
|
+
* An admin, and an in-process caller (an automation block), may run any action.
|
|
58254
|
+
* Every refusal is typed (`[not-linked] …`) and logged.
|
|
58255
|
+
*
|
|
58256
|
+
* A second `run` of an action that is already running is REFUSED — traced and
|
|
58257
|
+
* logged — never queued.
|
|
58258
|
+
*/
|
|
58259
|
+
var ActionIdInput = object({ actionId: string().min(1) });
|
|
58260
|
+
var ActionLinksSchema = object({
|
|
58261
|
+
deviceId: number().int().nonnegative(),
|
|
58262
|
+
actionIds: array(string().min(1)).max(100)
|
|
58263
|
+
});
|
|
58264
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
58265
|
+
actionId: string().min(1),
|
|
58266
|
+
deviceId: number().int().nonnegative().optional()
|
|
58267
|
+
}), object({ action: ActionEntrySchema.nullable() }), {
|
|
58268
|
+
role: "viewer",
|
|
58269
|
+
auth: "protected",
|
|
58270
|
+
kind: "query",
|
|
58271
|
+
caller: "required"
|
|
58272
|
+
}), method(object({
|
|
58273
|
+
actionId: string().min(1),
|
|
58274
|
+
variables: record(string(), ActionVariableValueSchema).optional(),
|
|
58275
|
+
wait: boolean().optional(),
|
|
58276
|
+
origin: ActionRunOriginSchema.optional(),
|
|
58277
|
+
deviceId: number().int().nonnegative().optional()
|
|
58278
|
+
}), ActionRunResultSchema, {
|
|
58279
|
+
role: "viewer",
|
|
58280
|
+
auth: "protected",
|
|
58281
|
+
kind: "mutation",
|
|
58282
|
+
caller: "required"
|
|
58283
|
+
}), method(ActionIdInput, _void(), {
|
|
58284
|
+
kind: "mutation",
|
|
58285
|
+
auth: "admin"
|
|
58286
|
+
}), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
|
|
58287
|
+
kind: "mutation",
|
|
58288
|
+
auth: "admin"
|
|
58289
|
+
}), method(object({
|
|
58290
|
+
actionId: string().min(1),
|
|
58291
|
+
action: ActionInputSchema.partial()
|
|
58292
|
+
}), object({ action: ActionEntrySchema }), {
|
|
58293
|
+
kind: "mutation",
|
|
58294
|
+
auth: "admin"
|
|
58295
|
+
}), method(ActionIdInput, _void(), {
|
|
58296
|
+
kind: "mutation",
|
|
58297
|
+
auth: "admin"
|
|
58298
|
+
}), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
|
|
58299
|
+
role: "viewer",
|
|
58300
|
+
auth: "protected",
|
|
58301
|
+
kind: "query"
|
|
58302
|
+
}), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
|
|
58303
|
+
kind: "mutation",
|
|
58304
|
+
auth: "admin"
|
|
58305
|
+
});
|
|
57494
58306
|
/**
|
|
57495
58307
|
* Automation-control cap. Models HA `automation.*` entities on
|
|
57496
58308
|
* `DeviceType.Automation`. An automation is a trigger+condition+
|
|
@@ -57587,7 +58399,7 @@ var BatteryStatusSchema = object({
|
|
|
57587
58399
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
57588
58400
|
* declines to band a non-finite reading, which is the correct shape.
|
|
57589
58401
|
*/
|
|
57590
|
-
|
|
58402
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
57591
58403
|
/**
|
|
57592
58404
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
57593
58405
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -57597,7 +58409,7 @@ var BatteryStatusSchema = object({
|
|
|
57597
58409
|
* `'unknown'` is the absence of an answer: the provider has not read the
|
|
57598
58410
|
* power fields yet, or the firmware reported neither. Before it existed,
|
|
57599
58411
|
* two unreadable fields produced `'none'`, and a camera nobody had reached
|
|
57600
|
-
* was drawn "on battery alone" — the same D315 defect `
|
|
58412
|
+
* was drawn "on battery alone" — the same D315 defect `level` closed
|
|
57601
58413
|
* by going nullable. Consumers SKIP it (no charger state published, no
|
|
57602
58414
|
* glyph, no HomeKit ChargingState) rather than coerce it either way.
|
|
57603
58415
|
*/
|
|
@@ -57621,7 +58433,7 @@ var BatteryStatusSchema = object({
|
|
|
57621
58433
|
* that only report charge. A second reading on this cap, not a second
|
|
57622
58434
|
* `numeric-sensor` on the same device.
|
|
57623
58435
|
*/
|
|
57624
|
-
|
|
58436
|
+
voltage: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
57625
58437
|
/**
|
|
57626
58438
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
57627
58439
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -57655,7 +58467,7 @@ var BatteryStatusSchema = object({
|
|
|
57655
58467
|
/**
|
|
57656
58468
|
* True when the source is a BINARY low-battery indicator (HA
|
|
57657
58469
|
* `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
|
|
57658
|
-
* charge level — `
|
|
58470
|
+
* charge level — `level` is then a coarse stand-in (100 = normal,
|
|
57659
58471
|
* sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
|
|
57660
58472
|
* misleading exact percentage. Absent/false → genuine 0–100 % reading.
|
|
57661
58473
|
*/
|
|
@@ -57668,21 +58480,22 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
57668
58480
|
DeviceType.Switch,
|
|
57669
58481
|
DeviceType.PetFeeder,
|
|
57670
58482
|
DeviceType.Container,
|
|
57671
|
-
DeviceType.Thermostat
|
|
58483
|
+
DeviceType.Thermostat,
|
|
58484
|
+
DeviceType.EventEmitter
|
|
57672
58485
|
];
|
|
57673
58486
|
/** Generic Customize target for the percentage reading (D936). */
|
|
57674
58487
|
var batteryCustomize = {
|
|
57675
58488
|
version: 1,
|
|
57676
58489
|
targets: [{
|
|
57677
|
-
id: "
|
|
58490
|
+
id: "level",
|
|
57678
58491
|
cardinality: "scalar",
|
|
57679
58492
|
vocabulary: {
|
|
57680
58493
|
cap: "battery",
|
|
57681
|
-
stateField: "
|
|
58494
|
+
stateField: "level"
|
|
57682
58495
|
},
|
|
57683
58496
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
57684
58497
|
apply: "add",
|
|
57685
|
-
path: "
|
|
58498
|
+
path: "level",
|
|
57686
58499
|
sources: [{
|
|
57687
58500
|
kind: "field",
|
|
57688
58501
|
valueKinds: ["number", "string"],
|
|
@@ -57776,7 +58589,7 @@ onStatusChanged: { data: object({
|
|
|
57776
58589
|
schema: BatteryStatusSchema,
|
|
57777
58590
|
kind: "push",
|
|
57778
58591
|
empty: {
|
|
57779
|
-
|
|
58592
|
+
level: null,
|
|
57780
58593
|
charging: "unknown",
|
|
57781
58594
|
sleeping: false,
|
|
57782
58595
|
lastUpdated: 0
|
|
@@ -57803,11 +58616,11 @@ onStatusChanged: { data: object({
|
|
|
57803
58616
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
57804
58617
|
*/
|
|
57805
58618
|
history: { fields: {
|
|
57806
|
-
|
|
58619
|
+
level: {
|
|
57807
58620
|
kind: "gauge",
|
|
57808
58621
|
unit: "%"
|
|
57809
58622
|
},
|
|
57810
|
-
|
|
58623
|
+
voltage: {
|
|
57811
58624
|
kind: "gauge",
|
|
57812
58625
|
unit: "V"
|
|
57813
58626
|
}
|
|
@@ -57867,14 +58680,14 @@ var binaryCapability = {
|
|
|
57867
58680
|
* controls to this single-method surface.
|
|
57868
58681
|
*
|
|
57869
58682
|
* The cap is intentionally minimal: a single `setBrightness({deviceId,
|
|
57870
|
-
*
|
|
58683
|
+
* level})` mutation plus the auto-injected `getStatus`. Drivers
|
|
57871
58684
|
* that expose richer controls (color temperature, scenes, schedules)
|
|
57872
58685
|
* should surface those via the device's `getSettingsUISchema()`
|
|
57873
58686
|
* instead of bloating this cap.
|
|
57874
58687
|
*/
|
|
57875
58688
|
var BrightnessStatusSchema = object({
|
|
57876
58689
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
57877
|
-
|
|
58690
|
+
level: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
57878
58691
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
57879
58692
|
lastChangedAt: number()
|
|
57880
58693
|
});
|
|
@@ -57886,7 +58699,7 @@ var brightnessCapability = {
|
|
|
57886
58699
|
deviceTypes: [DeviceType.Light],
|
|
57887
58700
|
methods: { setBrightness: method(object({
|
|
57888
58701
|
deviceId: number().int().nonnegative(),
|
|
57889
|
-
|
|
58702
|
+
level: number().min(0).max(100)
|
|
57890
58703
|
}), _void(), {
|
|
57891
58704
|
kind: "mutation",
|
|
57892
58705
|
auth: "protected"
|
|
@@ -57899,7 +58712,7 @@ var brightnessCapability = {
|
|
|
57899
58712
|
*/
|
|
57900
58713
|
onBrightnessChanged: { data: object({
|
|
57901
58714
|
deviceId: number(),
|
|
57902
|
-
|
|
58715
|
+
level: number().min(0).max(100),
|
|
57903
58716
|
lastChangedAt: number()
|
|
57904
58717
|
}) } },
|
|
57905
58718
|
status: {
|
|
@@ -57922,7 +58735,7 @@ onBrightnessChanged: { data: object({
|
|
|
57922
58735
|
/**
|
|
57923
58736
|
* History (D850): recorded series. Actuator tier: level keeps every change.
|
|
57924
58737
|
*/
|
|
57925
|
-
history: { fields: {
|
|
58738
|
+
history: { fields: { level: {
|
|
57926
58739
|
kind: "gauge",
|
|
57927
58740
|
unit: "%",
|
|
57928
58741
|
minIntervalMs: 0
|
|
@@ -58519,11 +59332,11 @@ var ColorInputSchema = discriminatedUnion("mode", [
|
|
|
58519
59332
|
}),
|
|
58520
59333
|
object({
|
|
58521
59334
|
mode: literal("mired"),
|
|
58522
|
-
|
|
59335
|
+
colorTemperature: number().int().min(50).max(1e3)
|
|
58523
59336
|
})
|
|
58524
59337
|
]);
|
|
58525
59338
|
var ColorStatusSchema = object({
|
|
58526
|
-
/** Active color mode — which of `rgb` / `hsv` / `
|
|
59339
|
+
/** Active color mode — which of `rgb` / `hsv` / `colorTemperature` reflects
|
|
58527
59340
|
* the bulb's current state. */
|
|
58528
59341
|
mode: _enum([
|
|
58529
59342
|
"rgb",
|
|
@@ -58535,7 +59348,7 @@ var ColorStatusSchema = object({
|
|
|
58535
59348
|
/** Populated when `mode === 'hsv'`. */
|
|
58536
59349
|
hsv: HsvTripletSchema.optional(),
|
|
58537
59350
|
/** Populated when `mode === 'mired'`. */
|
|
58538
|
-
|
|
59351
|
+
colorTemperature: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
58539
59352
|
/** Ms epoch of the last operator-driven change. */
|
|
58540
59353
|
lastChangedAt: number()
|
|
58541
59354
|
});
|
|
@@ -58567,7 +59380,7 @@ onColorChanged: { data: object({
|
|
|
58567
59380
|
]),
|
|
58568
59381
|
rgb: RgbTripletSchema.optional(),
|
|
58569
59382
|
hsv: HsvTripletSchema.optional(),
|
|
58570
|
-
|
|
59383
|
+
colorTemperature: number().int().optional(),
|
|
58571
59384
|
lastChangedAt: number()
|
|
58572
59385
|
}) } },
|
|
58573
59386
|
status: {
|
|
@@ -59444,11 +60257,11 @@ var DishwasherStatusSchema = object({
|
|
|
59444
60257
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
59445
60258
|
availablePrograms: array(string()),
|
|
59446
60259
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
59447
|
-
|
|
60260
|
+
remaining: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
59448
60261
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
59449
|
-
|
|
60262
|
+
delayedStart: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
59450
60263
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
59451
|
-
|
|
60264
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59452
60265
|
/** True when the door is reported open; null when the door state is not known. */
|
|
59453
60266
|
doorOpen: boolean().nullable(),
|
|
59454
60267
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -59667,14 +60480,14 @@ var dishwasherCapability = {
|
|
|
59667
60480
|
runtimeState: DishwasherStatusSchema,
|
|
59668
60481
|
durability: "session",
|
|
59669
60482
|
history: { fields: {
|
|
59670
|
-
|
|
60483
|
+
progress: {
|
|
59671
60484
|
kind: "gauge",
|
|
59672
60485
|
unit: "%",
|
|
59673
60486
|
deadband: 2
|
|
59674
60487
|
},
|
|
59675
60488
|
state: { kind: "enum" },
|
|
59676
60489
|
operationState: { kind: "enum" },
|
|
59677
|
-
|
|
60490
|
+
remaining: {
|
|
59678
60491
|
kind: "gauge",
|
|
59679
60492
|
unit: "s",
|
|
59680
60493
|
minIntervalMs: 6e4
|
|
@@ -60265,7 +61078,7 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
60265
61078
|
var FanControlStatusSchema = object({
|
|
60266
61079
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
60267
61080
|
* speed surface (single-speed fan). */
|
|
60268
|
-
|
|
61081
|
+
speed: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60269
61082
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
60270
61083
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
60271
61084
|
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
@@ -60292,7 +61105,7 @@ var fanControlCapability = {
|
|
|
60292
61105
|
methods: {
|
|
60293
61106
|
setPercentage: method(object({
|
|
60294
61107
|
deviceId: number().int().nonnegative(),
|
|
60295
|
-
|
|
61108
|
+
speed: number().min(0).max(100)
|
|
60296
61109
|
}), _void(), {
|
|
60297
61110
|
kind: "mutation",
|
|
60298
61111
|
auth: "protected"
|
|
@@ -60325,7 +61138,7 @@ var fanControlCapability = {
|
|
|
60325
61138
|
},
|
|
60326
61139
|
/**
|
|
60327
61140
|
* Runtime-state slice — mirrored by the kernel. UI fan speed
|
|
60328
|
-
* sliders read `
|
|
61141
|
+
* sliders read `speed` for live updates.
|
|
60329
61142
|
*/
|
|
60330
61143
|
runtimeState: FanControlStatusSchema,
|
|
60331
61144
|
/**
|
|
@@ -60339,7 +61152,7 @@ var fanControlCapability = {
|
|
|
60339
61152
|
* History (D850): recorded series. Actuator tier: speed keeps every change.
|
|
60340
61153
|
*/
|
|
60341
61154
|
history: { fields: {
|
|
60342
|
-
|
|
61155
|
+
speed: {
|
|
60343
61156
|
kind: "gauge",
|
|
60344
61157
|
unit: "%",
|
|
60345
61158
|
minIntervalMs: 0
|
|
@@ -60528,7 +61341,17 @@ var groupCommandsCapability = {
|
|
|
60528
61341
|
scope: "device",
|
|
60529
61342
|
deviceNative: true,
|
|
60530
61343
|
mode: "singleton",
|
|
60531
|
-
deviceTypes: [
|
|
61344
|
+
deviceTypes: [
|
|
61345
|
+
DeviceType.Switch,
|
|
61346
|
+
DeviceType.Light,
|
|
61347
|
+
DeviceType.Fan,
|
|
61348
|
+
DeviceType.Cover,
|
|
61349
|
+
DeviceType.Valve,
|
|
61350
|
+
DeviceType.Lock,
|
|
61351
|
+
DeviceType.MediaPlayer,
|
|
61352
|
+
DeviceType.Thermostat,
|
|
61353
|
+
DeviceType.Climate
|
|
61354
|
+
],
|
|
60532
61355
|
methods: {},
|
|
60533
61356
|
status: {
|
|
60534
61357
|
schema: GroupCommandsSchema,
|
|
@@ -60546,6 +61369,49 @@ var groupCommandsCapability = {
|
|
|
60546
61369
|
history: "none"
|
|
60547
61370
|
};
|
|
60548
61371
|
/**
|
|
61372
|
+
* Whether a helper actuator group counts as active (D944): at least one
|
|
61373
|
+
* included member is, or every included member is. What "active" means is the
|
|
61374
|
+
* type's: a cover is open, a lock is locked, a media player is playing, a
|
|
61375
|
+
* climate device is not off. A group of lights, switches and fans reports
|
|
61376
|
+
* `switch.on` instead; this cap is for the types whose own state is not a
|
|
61377
|
+
* yes/no. Unknown members are left out (`group-status` counts them) and an
|
|
61378
|
+
* empty included set takes the device offline, never `active: false`.
|
|
61379
|
+
*/
|
|
61380
|
+
var GroupActiveSchema = object({
|
|
61381
|
+
active: boolean().meta({ label: "Active" }),
|
|
61382
|
+
/** Ms epoch when the slice was last updated. */
|
|
61383
|
+
lastChangedAt: number()
|
|
61384
|
+
});
|
|
61385
|
+
var groupActiveCapability = {
|
|
61386
|
+
name: "group-active",
|
|
61387
|
+
scope: "device",
|
|
61388
|
+
deviceNative: true,
|
|
61389
|
+
mode: "singleton",
|
|
61390
|
+
deviceTypes: [
|
|
61391
|
+
DeviceType.Cover,
|
|
61392
|
+
DeviceType.Valve,
|
|
61393
|
+
DeviceType.Lock,
|
|
61394
|
+
DeviceType.MediaPlayer,
|
|
61395
|
+
DeviceType.Climate,
|
|
61396
|
+
DeviceType.Thermostat
|
|
61397
|
+
],
|
|
61398
|
+
methods: {},
|
|
61399
|
+
status: {
|
|
61400
|
+
schema: GroupActiveSchema,
|
|
61401
|
+
kind: "push"
|
|
61402
|
+
},
|
|
61403
|
+
runtimeState: GroupActiveSchema,
|
|
61404
|
+
/**
|
|
61405
|
+
* Runtime-state durability: **session** — recomputed from the members on the
|
|
61406
|
+
* next source event; a restored value would outlive the members it named.
|
|
61407
|
+
*
|
|
61408
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
61409
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
61410
|
+
*/
|
|
61411
|
+
durability: "session",
|
|
61412
|
+
history: "none"
|
|
61413
|
+
};
|
|
61414
|
+
/**
|
|
60549
61415
|
* How many members of a helper group answered (D944). Counts are integers,
|
|
60550
61416
|
* not measurements: zero included is a real count, never a stand-in reading.
|
|
60551
61417
|
* The reading itself stays on the output capability. History is none — the
|
|
@@ -60576,7 +61442,14 @@ var groupStatusCapability = {
|
|
|
60576
61442
|
deviceTypes: [
|
|
60577
61443
|
DeviceType.Sensor,
|
|
60578
61444
|
DeviceType.Switch,
|
|
60579
|
-
DeviceType.Light
|
|
61445
|
+
DeviceType.Light,
|
|
61446
|
+
DeviceType.Fan,
|
|
61447
|
+
DeviceType.Cover,
|
|
61448
|
+
DeviceType.Valve,
|
|
61449
|
+
DeviceType.Lock,
|
|
61450
|
+
DeviceType.MediaPlayer,
|
|
61451
|
+
DeviceType.Thermostat,
|
|
61452
|
+
DeviceType.Climate
|
|
60580
61453
|
],
|
|
60581
61454
|
methods: {},
|
|
60582
61455
|
status: {
|
|
@@ -60724,7 +61597,7 @@ var humidifierCapability = {
|
|
|
60724
61597
|
*/
|
|
60725
61598
|
var HumiditySensorStatusSchema = object({
|
|
60726
61599
|
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
60727
|
-
|
|
61600
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
60728
61601
|
precision: 0,
|
|
60729
61602
|
label: "Humidity"
|
|
60730
61603
|
})),
|
|
@@ -60753,7 +61626,7 @@ var humiditySensorCapability = {
|
|
|
60753
61626
|
/**
|
|
60754
61627
|
* History (D850): recorded series.
|
|
60755
61628
|
*/
|
|
60756
|
-
history: { fields: {
|
|
61629
|
+
history: { fields: { humidity: {
|
|
60757
61630
|
kind: "gauge",
|
|
60758
61631
|
unit: "%",
|
|
60759
61632
|
deadband: .5
|
|
@@ -61393,7 +62266,7 @@ var LawnMowerControlStatusSchema = object({
|
|
|
61393
62266
|
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61394
62267
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
61395
62268
|
* task is active / progress is unavailable. */
|
|
61396
|
-
|
|
62269
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61397
62270
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
61398
62271
|
* or null when unknown. */
|
|
61399
62272
|
currentCode: number().nullable().meta(readingMeta("none")),
|
|
@@ -61849,7 +62722,14 @@ var lockControlCapability = {
|
|
|
61849
62722
|
}),
|
|
61850
62723
|
unlock: method(object({
|
|
61851
62724
|
deviceId: number().int().nonnegative(),
|
|
61852
|
-
code: string().min(1).optional()
|
|
62725
|
+
code: string().min(1).optional(),
|
|
62726
|
+
/**
|
|
62727
|
+
* Honoured by a helper lock GROUP only (D944): unlocking several doors
|
|
62728
|
+
* with one call is refused unless the caller says it was confirmed. A
|
|
62729
|
+
* single lock ignores it. The refusal lives in the callee, so no
|
|
62730
|
+
* automation or API caller reaches the doors by skipping a dialog.
|
|
62731
|
+
*/
|
|
62732
|
+
confirm: boolean().optional()
|
|
61853
62733
|
}), _void(), {
|
|
61854
62734
|
kind: "mutation",
|
|
61855
62735
|
auth: "protected"
|
|
@@ -61886,6 +62766,44 @@ var lockControlCapability = {
|
|
|
61886
62766
|
volatileStateFields: ["lastChangedAt"]
|
|
61887
62767
|
};
|
|
61888
62768
|
/**
|
|
62769
|
+
* A monotonic total between resets (D944). Energy uses `power-meter.energy`
|
|
62770
|
+
* so dashboards keep one series; every other instantaneous reading lands here.
|
|
62771
|
+
* `value` is the time integral in the source quantity's accumulated base
|
|
62772
|
+
* (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
|
|
62773
|
+
*/
|
|
62774
|
+
var MeterStatusSchema = object({
|
|
62775
|
+
value: number().nullable().meta(readingMeta("device", { label: "Total" })),
|
|
62776
|
+
lastFetchedAt: number()
|
|
62777
|
+
});
|
|
62778
|
+
var meterCapability = {
|
|
62779
|
+
name: "meter",
|
|
62780
|
+
scope: "device",
|
|
62781
|
+
deviceNative: true,
|
|
62782
|
+
mode: "singleton",
|
|
62783
|
+
deviceTypes: [DeviceType.Sensor],
|
|
62784
|
+
methods: {},
|
|
62785
|
+
status: {
|
|
62786
|
+
schema: MeterStatusSchema,
|
|
62787
|
+
kind: "push"
|
|
62788
|
+
},
|
|
62789
|
+
runtimeState: MeterStatusSchema,
|
|
62790
|
+
/**
|
|
62791
|
+
* Runtime-state durability: **restored** — the total is a cache of the
|
|
62792
|
+
* history integral. A restart shows it only until the history pass
|
|
62793
|
+
* recomputes the open period; it is not an in-memory buffer.
|
|
62794
|
+
*
|
|
62795
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
62796
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
62797
|
+
*/
|
|
62798
|
+
durability: "restored",
|
|
62799
|
+
history: { fields: { value: {
|
|
62800
|
+
kind: "counter",
|
|
62801
|
+
deadband: .001,
|
|
62802
|
+
minIntervalMs: 6e4
|
|
62803
|
+
} } },
|
|
62804
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
62805
|
+
};
|
|
62806
|
+
/**
|
|
61889
62807
|
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
61890
62808
|
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
61891
62809
|
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
@@ -61942,10 +62860,10 @@ var MediaPlayerStatusSchema = object({
|
|
|
61942
62860
|
currentMedia: MediaInfoSchema.nullable(),
|
|
61943
62861
|
/** Current playback position in ms. Null when not seekable or
|
|
61944
62862
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
61945
|
-
|
|
62863
|
+
position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61946
62864
|
/** Total duration of the current media in ms. Null when unknown
|
|
61947
62865
|
* (live stream) or nothing is playing. */
|
|
61948
|
-
|
|
62866
|
+
duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61949
62867
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
61950
62868
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
61951
62869
|
shuffle: boolean().nullable(),
|
|
@@ -61984,7 +62902,7 @@ var mediaPlayerCapability = {
|
|
|
61984
62902
|
}),
|
|
61985
62903
|
seek: method(object({
|
|
61986
62904
|
deviceId: number().int().nonnegative(),
|
|
61987
|
-
|
|
62905
|
+
position: number().int().nonnegative()
|
|
61988
62906
|
}), _void(), {
|
|
61989
62907
|
kind: "mutation",
|
|
61990
62908
|
auth: "protected"
|
|
@@ -62056,7 +62974,7 @@ var mediaPlayerCapability = {
|
|
|
62056
62974
|
/**
|
|
62057
62975
|
* History (D850): volume (a level an operator or an automation sets; the
|
|
62058
62976
|
* default 10 s floor folds a slider drag into its end value) and mute.
|
|
62059
|
-
* `
|
|
62977
|
+
* `position` is not recorded: it moves continuously while playing.
|
|
62060
62978
|
*/
|
|
62061
62979
|
history: { fields: {
|
|
62062
62980
|
volumeLevel: {
|
|
@@ -63018,9 +63936,9 @@ var NetworkLinkStatusSchema = object({
|
|
|
63018
63936
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
63019
63937
|
* SKIP a null rather than coerce it.
|
|
63020
63938
|
*/
|
|
63021
|
-
|
|
63939
|
+
signal: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63022
63940
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
63023
|
-
|
|
63941
|
+
rssi: number().optional().meta(readingMeta("signal-level")),
|
|
63024
63942
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
63025
63943
|
ssid: string().optional(),
|
|
63026
63944
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
@@ -63056,7 +63974,7 @@ onStatusChanged: { data: object({
|
|
|
63056
63974
|
kind: "push",
|
|
63057
63975
|
empty: {
|
|
63058
63976
|
type: "unknown",
|
|
63059
|
-
|
|
63977
|
+
signal: null,
|
|
63060
63978
|
lastUpdated: 0
|
|
63061
63979
|
}
|
|
63062
63980
|
},
|
|
@@ -63079,13 +63997,13 @@ onStatusChanged: { data: object({
|
|
|
63079
63997
|
* History (D850): recorded series.
|
|
63080
63998
|
*/
|
|
63081
63999
|
history: { fields: {
|
|
63082
|
-
|
|
64000
|
+
signal: {
|
|
63083
64001
|
kind: "gauge",
|
|
63084
64002
|
unit: "%",
|
|
63085
64003
|
deadband: 3,
|
|
63086
64004
|
minIntervalMs: 6e4
|
|
63087
64005
|
},
|
|
63088
|
-
|
|
64006
|
+
rssi: {
|
|
63089
64007
|
kind: "gauge",
|
|
63090
64008
|
unit: "dBm",
|
|
63091
64009
|
deadband: 2,
|
|
@@ -63830,10 +64748,20 @@ var PetFeederDeviceStatusSchema = _enum([
|
|
|
63830
64748
|
]);
|
|
63831
64749
|
var gramsPortion = number().int().min(4).max(200);
|
|
63832
64750
|
var PetFeederStatusSchema = object({
|
|
63833
|
-
/** Food
|
|
63834
|
-
*
|
|
63835
|
-
* bowl
|
|
64751
|
+
/** Food in the TANK (hopper), as the feeder's own sensor reports it
|
|
64752
|
+
* (`state.food`). A coarse level code, not grams — `0` is empty. The
|
|
64753
|
+
* amount in the bowl is `bowlFood`. On dual-hopper models this is the
|
|
64754
|
+
* combined reading; per-hopper levels live in `food1`/`food2`. Null when
|
|
64755
|
+
* the device has not reported yet. */
|
|
63836
64756
|
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
64757
|
+
/** Food in the BOWL as the feeder reports it (`state.bowl`), in the
|
|
64758
|
+
* feeder's own units. Null when the model reports none. */
|
|
64759
|
+
bowlFood: number().nullable().meta(readingMeta("none")),
|
|
64760
|
+
/** True while the pet is eating (`state.eating`). */
|
|
64761
|
+
eating: boolean(),
|
|
64762
|
+
/** Whether `markFoodReplenished` does anything on this model. When false
|
|
64763
|
+
* the client must not offer it. */
|
|
64764
|
+
supportsMarkRefilled: boolean(),
|
|
63837
64765
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
63838
64766
|
* single-hopper models. */
|
|
63839
64767
|
food1: number().nullable().meta(readingMeta("none")),
|
|
@@ -63841,8 +64769,8 @@ var PetFeederStatusSchema = object({
|
|
|
63841
64769
|
* single-hopper models. */
|
|
63842
64770
|
food2: number().nullable().meta(readingMeta("none")),
|
|
63843
64771
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
63844
|
-
* (`device_class: problem`, on = low). True when the
|
|
63845
|
-
*
|
|
64772
|
+
* (`device_class: problem`, on = low). True when the tank reports empty,
|
|
64773
|
+
* or, on a dual-hopper model, when EITHER hopper is empty. */
|
|
63846
64774
|
lowFood: boolean(),
|
|
63847
64775
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
63848
64776
|
* device has no battery reading. */
|
|
@@ -63910,7 +64838,9 @@ var petFeederCapability = {
|
|
|
63910
64838
|
kind: "mutation",
|
|
63911
64839
|
auth: "protected"
|
|
63912
64840
|
}),
|
|
63913
|
-
/**
|
|
64841
|
+
/** Tell the PetKit cloud a hopper was refilled so it stops its low-food
|
|
64842
|
+
* reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
|
|
64843
|
+
* change any sensor reading. */
|
|
63914
64844
|
markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
63915
64845
|
kind: "mutation",
|
|
63916
64846
|
auth: "protected"
|
|
@@ -63983,6 +64913,7 @@ var petFeederCapability = {
|
|
|
63983
64913
|
*/
|
|
63984
64914
|
history: { fields: {
|
|
63985
64915
|
foodLevel: { kind: "gauge" },
|
|
64916
|
+
bowlFood: { kind: "gauge" },
|
|
63986
64917
|
food1: { kind: "gauge" },
|
|
63987
64918
|
food2: { kind: "gauge" },
|
|
63988
64919
|
batteryPower: {
|
|
@@ -64392,27 +65323,89 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
64392
65323
|
* in real-world devices.
|
|
64393
65324
|
*/
|
|
64394
65325
|
var PowerMeterStatusSchema = object({
|
|
64395
|
-
/** Instantaneous power draw
|
|
64396
|
-
|
|
65326
|
+
/** Instantaneous power draw (watts). */
|
|
65327
|
+
power: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
64397
65328
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
64398
|
-
|
|
64399
|
-
/** Voltage
|
|
64400
|
-
|
|
65329
|
+
energy: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
65330
|
+
/** Voltage (volts). */
|
|
65331
|
+
voltage: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
64401
65332
|
/** Current in amperes. */
|
|
64402
|
-
|
|
65333
|
+
current: number().optional().meta(readingMeta("current", { label: "Current" })),
|
|
64403
65334
|
/** Apparent power in volt-amperes. */
|
|
64404
|
-
|
|
65335
|
+
apparentPower: number().optional().meta(readingMeta("apparent-power")),
|
|
64405
65336
|
/** Power factor (0..1) if reported. */
|
|
64406
65337
|
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
64407
65338
|
/** Ms epoch when the slice was last updated. */
|
|
64408
65339
|
lastFetchedAt: number()
|
|
64409
65340
|
});
|
|
65341
|
+
/**
|
|
65342
|
+
* Loads that draw metered mains power (D951). The reading is a feature of the
|
|
65343
|
+
* device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
|
|
65344
|
+
* vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
|
|
65345
|
+
* meter or a standalone energy monitor IS the device. Cover, lock, valve,
|
|
65346
|
+
* camera and the like are left out: their draw is not what anyone meters.
|
|
65347
|
+
*/
|
|
65348
|
+
var POWER_METER_DEVICE_TYPES = [
|
|
65349
|
+
DeviceType.Sensor,
|
|
65350
|
+
DeviceType.Switch,
|
|
65351
|
+
DeviceType.Light,
|
|
65352
|
+
DeviceType.Fan,
|
|
65353
|
+
DeviceType.Climate,
|
|
65354
|
+
DeviceType.Thermostat,
|
|
65355
|
+
DeviceType.Humidifier,
|
|
65356
|
+
DeviceType.WaterHeater,
|
|
65357
|
+
DeviceType.MediaPlayer,
|
|
65358
|
+
DeviceType.Washer,
|
|
65359
|
+
DeviceType.Dryer,
|
|
65360
|
+
DeviceType.Dishwasher,
|
|
65361
|
+
DeviceType.Vacuum,
|
|
65362
|
+
DeviceType.LawnMower,
|
|
65363
|
+
DeviceType.PetFeeder
|
|
65364
|
+
];
|
|
65365
|
+
/** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
|
|
65366
|
+
var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
|
|
65367
|
+
/** One scalar target per reading; each is sourced from a device of its own (D951). */
|
|
65368
|
+
function powerTarget(id, unit) {
|
|
65369
|
+
return {
|
|
65370
|
+
id,
|
|
65371
|
+
cardinality: "scalar",
|
|
65372
|
+
vocabulary: {
|
|
65373
|
+
cap: "power-meter",
|
|
65374
|
+
stateField: id
|
|
65375
|
+
},
|
|
65376
|
+
deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
|
|
65377
|
+
apply: "add",
|
|
65378
|
+
path: id,
|
|
65379
|
+
sources: [{
|
|
65380
|
+
kind: "numeric",
|
|
65381
|
+
requiredUnit: unit,
|
|
65382
|
+
transforms: [{ kind: "identity" }]
|
|
65383
|
+
}],
|
|
65384
|
+
companions: [{
|
|
65385
|
+
kind: "clock",
|
|
65386
|
+
path: "lastFetchedAt",
|
|
65387
|
+
when: "always"
|
|
65388
|
+
}],
|
|
65389
|
+
sourceReport: { status: "assignment-status" },
|
|
65390
|
+
advancedExpression: true
|
|
65391
|
+
};
|
|
65392
|
+
}
|
|
65393
|
+
/** "Power and energy" extension: power, energy, voltage and current from other devices (D951). */
|
|
65394
|
+
var powerMeterCustomize = {
|
|
65395
|
+
version: 1,
|
|
65396
|
+
targets: [
|
|
65397
|
+
powerTarget("power", "W"),
|
|
65398
|
+
powerTarget("energy", "kWh"),
|
|
65399
|
+
powerTarget("voltage", "V"),
|
|
65400
|
+
powerTarget("current", "A")
|
|
65401
|
+
]
|
|
65402
|
+
};
|
|
64410
65403
|
var powerMeterCapability = {
|
|
64411
65404
|
name: "power-meter",
|
|
64412
65405
|
scope: "device",
|
|
64413
65406
|
deviceNative: true,
|
|
64414
65407
|
mode: "singleton",
|
|
64415
|
-
deviceTypes:
|
|
65408
|
+
deviceTypes: POWER_METER_DEVICE_TYPES,
|
|
64416
65409
|
methods: {},
|
|
64417
65410
|
status: {
|
|
64418
65411
|
schema: PowerMeterStatusSchema,
|
|
@@ -64420,41 +65413,41 @@ var powerMeterCapability = {
|
|
|
64420
65413
|
},
|
|
64421
65414
|
runtimeState: PowerMeterStatusSchema,
|
|
64422
65415
|
/**
|
|
64423
|
-
* Runtime-state durability: **restored** — as `numeric-sensor`; `
|
|
65416
|
+
* Runtime-state durability: **restored** — as `numeric-sensor`; `energy` is an accumulator whose restored value is the baseline.
|
|
64424
65417
|
*
|
|
64425
65418
|
* See `RuntimeStateDurability`. Enforced by
|
|
64426
65419
|
* `scripts/check-runtime-state-durability.ts`.
|
|
64427
65420
|
*/
|
|
64428
65421
|
durability: "restored",
|
|
64429
65422
|
/**
|
|
64430
|
-
* History (D850): recorded series. Gauges are floored at 30 s; `
|
|
65423
|
+
* History (D850): recorded series. Gauges are floored at 30 s; `energy` is the one monotonic meter (counter).
|
|
64431
65424
|
*/
|
|
64432
65425
|
history: { fields: {
|
|
64433
|
-
|
|
65426
|
+
power: {
|
|
64434
65427
|
kind: "gauge",
|
|
64435
65428
|
unit: "W",
|
|
64436
65429
|
deadband: 1,
|
|
64437
65430
|
minIntervalMs: 3e4
|
|
64438
65431
|
},
|
|
64439
|
-
|
|
65432
|
+
energy: {
|
|
64440
65433
|
kind: "counter",
|
|
64441
65434
|
unit: "kWh",
|
|
64442
65435
|
deadband: .01,
|
|
64443
65436
|
minIntervalMs: 6e4
|
|
64444
65437
|
},
|
|
64445
|
-
|
|
65438
|
+
voltage: {
|
|
64446
65439
|
kind: "gauge",
|
|
64447
65440
|
unit: "V",
|
|
64448
65441
|
deadband: 1,
|
|
64449
65442
|
minIntervalMs: 3e4
|
|
64450
65443
|
},
|
|
64451
|
-
|
|
65444
|
+
current: {
|
|
64452
65445
|
kind: "gauge",
|
|
64453
65446
|
unit: "A",
|
|
64454
65447
|
deadband: .05,
|
|
64455
65448
|
minIntervalMs: 3e4
|
|
64456
65449
|
},
|
|
64457
|
-
|
|
65450
|
+
apparentPower: {
|
|
64458
65451
|
kind: "gauge",
|
|
64459
65452
|
unit: "VA",
|
|
64460
65453
|
deadband: 1,
|
|
@@ -64469,7 +65462,8 @@ var powerMeterCapability = {
|
|
|
64469
65462
|
} },
|
|
64470
65463
|
/** Clock fields: written, but excluded from the compare that decides
|
|
64471
65464
|
* whether persisting is worth a SQLite commit. */
|
|
64472
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
65465
|
+
volatileStateFields: ["lastFetchedAt"],
|
|
65466
|
+
customize: powerMeterCustomize
|
|
64473
65467
|
};
|
|
64474
65468
|
/**
|
|
64475
65469
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
@@ -64503,9 +65497,6 @@ var PresenceStatusSchema = object({
|
|
|
64503
65497
|
location: string().nullable(),
|
|
64504
65498
|
/** GPS coordinates when available. Null otherwise. */
|
|
64505
65499
|
gps: GpsLocationSchema.nullable(),
|
|
64506
|
-
/** Optional battery level of the tracking device (0..100). Null
|
|
64507
|
-
* when not reported. */
|
|
64508
|
-
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
64509
65500
|
/** Ms epoch when the slice was last updated. */
|
|
64510
65501
|
lastChangedAt: number()
|
|
64511
65502
|
});
|
|
@@ -64534,14 +65525,7 @@ var presenceCapability = {
|
|
|
64534
65525
|
* `scripts/check-runtime-state-durability.ts`.
|
|
64535
65526
|
*/
|
|
64536
65527
|
durability: "restored",
|
|
64537
|
-
|
|
64538
|
-
* History (D850): recorded series.
|
|
64539
|
-
*/
|
|
64540
|
-
history: { fields: { batteryPercent: {
|
|
64541
|
-
kind: "gauge",
|
|
64542
|
-
unit: "%",
|
|
64543
|
-
deadband: 1
|
|
64544
|
-
} } },
|
|
65528
|
+
history: "none",
|
|
64545
65529
|
/** Clock fields: written, but excluded from the compare that decides
|
|
64546
65530
|
* whether persisting is worth a SQLite commit. */
|
|
64547
65531
|
volatileStateFields: ["lastChangedAt"]
|
|
@@ -64556,7 +65540,7 @@ var presenceCapability = {
|
|
|
64556
65540
|
*/
|
|
64557
65541
|
var PressureSensorStatusSchema = object({
|
|
64558
65542
|
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
64559
|
-
|
|
65543
|
+
pressure: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
64560
65544
|
/** Ms epoch when the slice was last updated. */
|
|
64561
65545
|
lastFetchedAt: number()
|
|
64562
65546
|
});
|
|
@@ -64582,7 +65566,7 @@ var pressureSensorCapability = {
|
|
|
64582
65566
|
/**
|
|
64583
65567
|
* History (D850): recorded series.
|
|
64584
65568
|
*/
|
|
64585
|
-
history: { fields: {
|
|
65569
|
+
history: { fields: { pressure: {
|
|
64586
65570
|
kind: "gauge",
|
|
64587
65571
|
unit: "hPa",
|
|
64588
65572
|
deadband: .1
|
|
@@ -65150,6 +66134,8 @@ var runningCustomize = {
|
|
|
65150
66134
|
stateField: "running"
|
|
65151
66135
|
},
|
|
65152
66136
|
deviceTypes: [
|
|
66137
|
+
DeviceType.Washer,
|
|
66138
|
+
DeviceType.Dryer,
|
|
65153
66139
|
DeviceType.Dishwasher,
|
|
65154
66140
|
DeviceType.Vacuum,
|
|
65155
66141
|
DeviceType.LawnMower,
|
|
@@ -67639,7 +68625,8 @@ var switchCapability = {
|
|
|
67639
68625
|
deviceTypes: [
|
|
67640
68626
|
DeviceType.Switch,
|
|
67641
68627
|
DeviceType.Siren,
|
|
67642
|
-
DeviceType.Light
|
|
68628
|
+
DeviceType.Light,
|
|
68629
|
+
DeviceType.Fan
|
|
67643
68630
|
],
|
|
67644
68631
|
methods: { setState: method(object({
|
|
67645
68632
|
deviceId: number(),
|
|
@@ -68278,7 +69265,7 @@ var tamperCapability = {
|
|
|
68278
69265
|
*/
|
|
68279
69266
|
var TemperatureSensorStatusSchema = object({
|
|
68280
69267
|
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
68281
|
-
|
|
69268
|
+
temperature: number().nullable().meta(readingMeta("temperature", {
|
|
68282
69269
|
precision: 1,
|
|
68283
69270
|
label: "Temperature"
|
|
68284
69271
|
})),
|
|
@@ -68307,7 +69294,7 @@ var temperatureSensorCapability = {
|
|
|
68307
69294
|
/**
|
|
68308
69295
|
* History (D850): recorded series.
|
|
68309
69296
|
*/
|
|
68310
|
-
history: { fields: {
|
|
69297
|
+
history: { fields: { temperature: {
|
|
68311
69298
|
kind: "gauge",
|
|
68312
69299
|
unit: "°C",
|
|
68313
69300
|
deadband: .1
|
|
@@ -68604,7 +69591,7 @@ var VacuumControlStatusSchema = object({
|
|
|
68604
69591
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
68605
69592
|
availableFanSpeeds: array(string()),
|
|
68606
69593
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
68607
|
-
|
|
69594
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68608
69595
|
/** Current error code (0 / null = no error). */
|
|
68609
69596
|
errorCode: number().nullable().meta(readingMeta("none")),
|
|
68610
69597
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
@@ -69852,6 +70839,7 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
69852
70839
|
kind: "mutation",
|
|
69853
70840
|
auth: "admin"
|
|
69854
70841
|
});
|
|
70842
|
+
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
69855
70843
|
/**
|
|
69856
70844
|
* Frozen unit → spec table. `•` in the doc marks each dimension's canonical
|
|
69857
70845
|
* unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
|
|
@@ -70236,7 +71224,7 @@ function tryConvertUnit(value, from, to) {
|
|
|
70236
71224
|
return fromCanonical(toCanonical(value, fromSpec), toSpec);
|
|
70237
71225
|
}
|
|
70238
71226
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
70239
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Camera, DeviceType.Camera, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
71227
|
+
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;
|
|
70240
71228
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
70241
71229
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
70242
71230
|
new Set(CAP_NAMES);
|
|
@@ -70565,6 +71553,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
70565
71553
|
featureProbe: featureProbeCapability,
|
|
70566
71554
|
flood: floodCapability,
|
|
70567
71555
|
gas: gasCapability,
|
|
71556
|
+
groupActive: groupActiveCapability,
|
|
70568
71557
|
groupCommands: groupCommandsCapability,
|
|
70569
71558
|
groupStatus: groupStatusCapability,
|
|
70570
71559
|
heatingValve: heatingValveCapability,
|
|
@@ -70576,6 +71565,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
70576
71565
|
lawnMowerControl: lawnMowerControlCapability,
|
|
70577
71566
|
lockControl: lockControlCapability,
|
|
70578
71567
|
mediaPlayer: mediaPlayerCapability,
|
|
71568
|
+
meter: meterCapability,
|
|
70579
71569
|
motion: motionCapability,
|
|
70580
71570
|
motionTrigger: motionTriggerCapability,
|
|
70581
71571
|
motionZones: motionZonesCapability,
|
|
@@ -70638,7 +71628,7 @@ var BaseDevice = class {
|
|
|
70638
71628
|
* `device-local-state.ts`. Drivers access via:
|
|
70639
71629
|
*
|
|
70640
71630
|
* `this.state.battery.sleeping = true` // patches the battery slice
|
|
70641
|
-
* `const pct = this.state.battery.
|
|
71631
|
+
* `const pct = this.state.battery.level` // reads the battery slice
|
|
70642
71632
|
* `this.state.deviceStatus.online = true` // mirrors `markOnline(true)`
|
|
70643
71633
|
*
|
|
70644
71634
|
* Adding a new cap with `runtimeState:` automatically extends this
|
|
@@ -70678,7 +71668,7 @@ var BaseDevice = class {
|
|
|
70678
71668
|
* shape; drivers don't redeclare it, they just write through.
|
|
70679
71669
|
*
|
|
70680
71670
|
* Read: `this.runtimeState.getCapState('battery')` →
|
|
70681
|
-
* `{
|
|
71671
|
+
* `{level, charging, sleeping, lastUpdated}` for any
|
|
70682
71672
|
* provider that registers `batteryCapability`.
|
|
70683
71673
|
* Write: `this.runtimeState.setCapState('battery', { … })`.
|
|
70684
71674
|
*
|
|
@@ -71835,7 +72825,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
71835
72825
|
return toDeviceSummary(device, this.addonId);
|
|
71836
72826
|
}
|
|
71837
72827
|
};
|
|
71838
|
-
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;
|
|
72828
|
+
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;
|
|
71839
72829
|
new Set(Object.values(DeviceType));
|
|
71840
72830
|
Object.freeze({
|
|
71841
72831
|
"accessories.getStatus": {
|
|
@@ -71852,6 +72842,104 @@ Object.freeze({
|
|
|
71852
72842
|
access: "create",
|
|
71853
72843
|
role: "admin"
|
|
71854
72844
|
},
|
|
72845
|
+
"actions.create": {
|
|
72846
|
+
capName: "actions",
|
|
72847
|
+
capScope: "system",
|
|
72848
|
+
addonId: null,
|
|
72849
|
+
access: "create",
|
|
72850
|
+
role: "admin"
|
|
72851
|
+
},
|
|
72852
|
+
"actions.delete": {
|
|
72853
|
+
capName: "actions",
|
|
72854
|
+
capScope: "system",
|
|
72855
|
+
addonId: null,
|
|
72856
|
+
access: "delete",
|
|
72857
|
+
role: "admin"
|
|
72858
|
+
},
|
|
72859
|
+
"actions.get": {
|
|
72860
|
+
capName: "actions",
|
|
72861
|
+
capScope: "system",
|
|
72862
|
+
addonId: null,
|
|
72863
|
+
access: "view",
|
|
72864
|
+
role: "viewer"
|
|
72865
|
+
},
|
|
72866
|
+
"actions.getLinks": {
|
|
72867
|
+
capName: "actions",
|
|
72868
|
+
capScope: "system",
|
|
72869
|
+
addonId: null,
|
|
72870
|
+
access: "view",
|
|
72871
|
+
role: "admin"
|
|
72872
|
+
},
|
|
72873
|
+
"actions.getTraces": {
|
|
72874
|
+
capName: "actions",
|
|
72875
|
+
capScope: "system",
|
|
72876
|
+
addonId: null,
|
|
72877
|
+
access: "view",
|
|
72878
|
+
role: "admin"
|
|
72879
|
+
},
|
|
72880
|
+
"actions.list": {
|
|
72881
|
+
capName: "actions",
|
|
72882
|
+
capScope: "system",
|
|
72883
|
+
addonId: null,
|
|
72884
|
+
access: "view",
|
|
72885
|
+
role: "admin"
|
|
72886
|
+
},
|
|
72887
|
+
"actions.listForCamera": {
|
|
72888
|
+
capName: "actions",
|
|
72889
|
+
capScope: "system",
|
|
72890
|
+
addonId: null,
|
|
72891
|
+
access: "view",
|
|
72892
|
+
role: "viewer"
|
|
72893
|
+
},
|
|
72894
|
+
"actions.run": {
|
|
72895
|
+
capName: "actions",
|
|
72896
|
+
capScope: "system",
|
|
72897
|
+
addonId: null,
|
|
72898
|
+
access: "create",
|
|
72899
|
+
role: "viewer"
|
|
72900
|
+
},
|
|
72901
|
+
"actions.setLinks": {
|
|
72902
|
+
capName: "actions",
|
|
72903
|
+
capScope: "system",
|
|
72904
|
+
addonId: null,
|
|
72905
|
+
access: "create",
|
|
72906
|
+
role: "admin"
|
|
72907
|
+
},
|
|
72908
|
+
"actions.stop": {
|
|
72909
|
+
capName: "actions",
|
|
72910
|
+
capScope: "system",
|
|
72911
|
+
addonId: null,
|
|
72912
|
+
access: "create",
|
|
72913
|
+
role: "admin"
|
|
72914
|
+
},
|
|
72915
|
+
"actions.update": {
|
|
72916
|
+
capName: "actions",
|
|
72917
|
+
capScope: "system",
|
|
72918
|
+
addonId: null,
|
|
72919
|
+
access: "create",
|
|
72920
|
+
role: "admin"
|
|
72921
|
+
},
|
|
72922
|
+
"actionSource.listActions": {
|
|
72923
|
+
capName: "action-source",
|
|
72924
|
+
capScope: "system",
|
|
72925
|
+
addonId: null,
|
|
72926
|
+
access: "view",
|
|
72927
|
+
role: "admin"
|
|
72928
|
+
},
|
|
72929
|
+
"actionSource.runAction": {
|
|
72930
|
+
capName: "action-source",
|
|
72931
|
+
capScope: "system",
|
|
72932
|
+
addonId: null,
|
|
72933
|
+
access: "create",
|
|
72934
|
+
role: "admin"
|
|
72935
|
+
},
|
|
72936
|
+
"actionSource.stopAction": {
|
|
72937
|
+
capName: "action-source",
|
|
72938
|
+
capScope: "system",
|
|
72939
|
+
addonId: null,
|
|
72940
|
+
access: "create",
|
|
72941
|
+
role: "admin"
|
|
72942
|
+
},
|
|
71855
72943
|
"addonPages.listPages": {
|
|
71856
72944
|
capName: "addon-pages",
|
|
71857
72945
|
capScope: "system",
|
|
@@ -73133,6 +74221,13 @@ Object.freeze({
|
|
|
73133
74221
|
access: "view",
|
|
73134
74222
|
role: "admin"
|
|
73135
74223
|
},
|
|
74224
|
+
"coreBlocks.getAutomationRunState": {
|
|
74225
|
+
capName: "core-blocks",
|
|
74226
|
+
capScope: "system",
|
|
74227
|
+
addonId: null,
|
|
74228
|
+
access: "view",
|
|
74229
|
+
role: "admin"
|
|
74230
|
+
},
|
|
73136
74231
|
"coreBlocks.getBlockSettings": {
|
|
73137
74232
|
capName: "core-blocks",
|
|
73138
74233
|
capScope: "system",
|
|
@@ -73154,6 +74249,13 @@ Object.freeze({
|
|
|
73154
74249
|
access: "view",
|
|
73155
74250
|
role: "admin"
|
|
73156
74251
|
},
|
|
74252
|
+
"coreBlocks.getTraces": {
|
|
74253
|
+
capName: "core-blocks",
|
|
74254
|
+
capScope: "system",
|
|
74255
|
+
addonId: null,
|
|
74256
|
+
access: "view",
|
|
74257
|
+
role: "admin"
|
|
74258
|
+
},
|
|
73157
74259
|
"coreBlocks.getTypeDefs": {
|
|
73158
74260
|
capName: "core-blocks",
|
|
73159
74261
|
capScope: "system",
|
|
@@ -73189,6 +74291,13 @@ Object.freeze({
|
|
|
73189
74291
|
access: "create",
|
|
73190
74292
|
role: "admin"
|
|
73191
74293
|
},
|
|
74294
|
+
"coreBlocks.reportAutomationRun": {
|
|
74295
|
+
capName: "core-blocks",
|
|
74296
|
+
capScope: "system",
|
|
74297
|
+
addonId: null,
|
|
74298
|
+
access: "create",
|
|
74299
|
+
role: "admin"
|
|
74300
|
+
},
|
|
73192
74301
|
"coreBlocks.restart": {
|
|
73193
74302
|
capName: "core-blocks",
|
|
73194
74303
|
capScope: "system",
|
|
@@ -75058,6 +76167,13 @@ Object.freeze({
|
|
|
75058
76167
|
access: "view",
|
|
75059
76168
|
role: "viewer"
|
|
75060
76169
|
},
|
|
76170
|
+
"groupActive.getStatus": {
|
|
76171
|
+
capName: "group-active",
|
|
76172
|
+
capScope: "device",
|
|
76173
|
+
addonId: null,
|
|
76174
|
+
access: "view",
|
|
76175
|
+
role: "viewer"
|
|
76176
|
+
},
|
|
75061
76177
|
"groupCommands.getStatus": {
|
|
75062
76178
|
capName: "group-commands",
|
|
75063
76179
|
capScope: "device",
|
|
@@ -76024,6 +77140,13 @@ Object.freeze({
|
|
|
76024
77140
|
access: "create",
|
|
76025
77141
|
role: "admin"
|
|
76026
77142
|
},
|
|
77143
|
+
"meter.getStatus": {
|
|
77144
|
+
capName: "meter",
|
|
77145
|
+
capScope: "device",
|
|
77146
|
+
addonId: null,
|
|
77147
|
+
access: "view",
|
|
77148
|
+
role: "viewer"
|
|
77149
|
+
},
|
|
76027
77150
|
"metricsProvider.collectSnapshot": {
|
|
76028
77151
|
capName: "metrics-provider",
|
|
76029
77152
|
capScope: "system",
|
|
@@ -81748,7 +82871,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81748
82871
|
label: t("Full brightness", "Luminosità massima"),
|
|
81749
82872
|
phrase: t("Set {device} to full brightness", "Porta {device} alla massima luminosità"),
|
|
81750
82873
|
icon: "sun",
|
|
81751
|
-
args: {
|
|
82874
|
+
args: { level: 100 }
|
|
81752
82875
|
},
|
|
81753
82876
|
{
|
|
81754
82877
|
id: "brightness.half",
|
|
@@ -81756,7 +82879,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81756
82879
|
label: t("Half brightness", "Luminosità a metà"),
|
|
81757
82880
|
phrase: t("Dim {device} to half", "Abbassa {device} a metà luminosità"),
|
|
81758
82881
|
icon: "sun-dim",
|
|
81759
|
-
args: {
|
|
82882
|
+
args: { level: 50 }
|
|
81760
82883
|
},
|
|
81761
82884
|
{
|
|
81762
82885
|
id: "brightness.off",
|
|
@@ -81764,16 +82887,16 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81764
82887
|
label: t("Brightness off", "Luminosità a zero"),
|
|
81765
82888
|
phrase: t("Dim {device} to zero", "Abbassa {device} a zero"),
|
|
81766
82889
|
icon: "lightbulb-off",
|
|
81767
|
-
args: {
|
|
82890
|
+
args: { level: 0 }
|
|
81768
82891
|
},
|
|
81769
82892
|
{
|
|
81770
82893
|
id: "brightness.set",
|
|
81771
82894
|
method: "setBrightness",
|
|
81772
82895
|
label: t("Set brightness", "Imposta la luminosità"),
|
|
81773
|
-
phrase: t("Set the brightness of {device} to {
|
|
82896
|
+
phrase: t("Set the brightness of {device} to {level}", "Imposta la luminosità di {device} a {level}"),
|
|
81774
82897
|
icon: "sun-medium",
|
|
81775
82898
|
openArgs: [{
|
|
81776
|
-
key: "
|
|
82899
|
+
key: "level",
|
|
81777
82900
|
label: PERCENT,
|
|
81778
82901
|
offer: {
|
|
81779
82902
|
kind: "number",
|
|
@@ -81787,13 +82910,13 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81787
82910
|
],
|
|
81788
82911
|
state: [{
|
|
81789
82912
|
kind: "numeric",
|
|
81790
|
-
field: "
|
|
82913
|
+
field: "level",
|
|
81791
82914
|
quantity: t("the brightness", "la luminosità"),
|
|
81792
82915
|
label: t("Brightness", "Luminosità"),
|
|
81793
82916
|
unit: "%",
|
|
81794
82917
|
icon: "sun"
|
|
81795
82918
|
}],
|
|
81796
|
-
events: { onBrightnessChanged: { notOperatorFacing: "the `
|
|
82919
|
+
events: { onBrightnessChanged: { notOperatorFacing: "the `level` state trigger reads the same change" } }
|
|
81797
82920
|
},
|
|
81798
82921
|
color: {
|
|
81799
82922
|
scope: "device",
|
|
@@ -81807,7 +82930,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81807
82930
|
icon: "sun",
|
|
81808
82931
|
args: { color: {
|
|
81809
82932
|
mode: "mired",
|
|
81810
|
-
|
|
82933
|
+
colorTemperature: 370
|
|
81811
82934
|
} }
|
|
81812
82935
|
},
|
|
81813
82936
|
{
|
|
@@ -81818,7 +82941,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81818
82941
|
icon: "snowflake",
|
|
81819
82942
|
args: { color: {
|
|
81820
82943
|
mode: "mired",
|
|
81821
|
-
|
|
82944
|
+
colorTemperature: 200
|
|
81822
82945
|
} }
|
|
81823
82946
|
},
|
|
81824
82947
|
{
|
|
@@ -82432,7 +83555,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82432
83555
|
label: t("Fan off", "Ventilatore spento"),
|
|
82433
83556
|
phrase: t("Stop {device}", "Ferma {device}"),
|
|
82434
83557
|
icon: "power-off",
|
|
82435
|
-
args: {
|
|
83558
|
+
args: { speed: 0 }
|
|
82436
83559
|
},
|
|
82437
83560
|
{
|
|
82438
83561
|
id: "fan.full",
|
|
@@ -82440,16 +83563,16 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82440
83563
|
label: t("Fan full", "Ventilatore al massimo"),
|
|
82441
83564
|
phrase: t("Run {device} at full speed", "Porta {device} alla massima velocità"),
|
|
82442
83565
|
icon: "fan",
|
|
82443
|
-
args: {
|
|
83566
|
+
args: { speed: 100 }
|
|
82444
83567
|
},
|
|
82445
83568
|
{
|
|
82446
83569
|
id: "fan.speed",
|
|
82447
83570
|
method: "setPercentage",
|
|
82448
83571
|
label: t("Set speed", "Imposta la velocità"),
|
|
82449
|
-
phrase: t("Set the speed of {device} to {
|
|
83572
|
+
phrase: t("Set the speed of {device} to {speed}", "Imposta la velocità di {device} al {speed}"),
|
|
82450
83573
|
icon: "fan",
|
|
82451
83574
|
openArgs: [{
|
|
82452
|
-
key: "
|
|
83575
|
+
key: "speed",
|
|
82453
83576
|
label: t("Speed", "Velocità"),
|
|
82454
83577
|
offer: {
|
|
82455
83578
|
kind: "number",
|
|
@@ -82511,7 +83634,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82511
83634
|
],
|
|
82512
83635
|
state: [{
|
|
82513
83636
|
kind: "numeric",
|
|
82514
|
-
field: "
|
|
83637
|
+
field: "speed",
|
|
82515
83638
|
quantity: t("the speed", "la velocità"),
|
|
82516
83639
|
label: t("Speed", "Velocità"),
|
|
82517
83640
|
unit: "%",
|
|
@@ -83309,7 +84432,7 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
83309
84432
|
},
|
|
83310
84433
|
{
|
|
83311
84434
|
kind: "numeric",
|
|
83312
|
-
field: "
|
|
84435
|
+
field: "progress",
|
|
83313
84436
|
quantity: t("the cycle progress", "l’avanzamento del lavaggio"),
|
|
83314
84437
|
label: t("Progress", "Avanzamento"),
|
|
83315
84438
|
unit: "%",
|
|
@@ -83667,10 +84790,10 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
83667
84790
|
id: "media.seek",
|
|
83668
84791
|
method: "seek",
|
|
83669
84792
|
label: t("Seek", "Vai al punto"),
|
|
83670
|
-
phrase: t("Move {device} to {
|
|
84793
|
+
phrase: t("Move {device} to {position}", "Porta {device} al punto {position}"),
|
|
83671
84794
|
icon: "fast-forward",
|
|
83672
84795
|
openArgs: [{
|
|
83673
|
-
key: "
|
|
84796
|
+
key: "position",
|
|
83674
84797
|
label: t("Position", "Posizione"),
|
|
83675
84798
|
offer: {
|
|
83676
84799
|
kind: "number",
|
|
@@ -84122,7 +85245,39 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
84122
85245
|
phrase: t("When {device} finishes", "Quando {device} finisce"),
|
|
84123
85246
|
icon: "square"
|
|
84124
85247
|
}
|
|
84125
|
-
}
|
|
85248
|
+
},
|
|
85249
|
+
overrides: { byType: {
|
|
85250
|
+
[DeviceType.Washer]: {
|
|
85251
|
+
state: { running: {
|
|
85252
|
+
whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
|
|
85253
|
+
whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
|
|
85254
|
+
} },
|
|
85255
|
+
events: {
|
|
85256
|
+
onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
|
|
85257
|
+
onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
|
|
85258
|
+
}
|
|
85259
|
+
},
|
|
85260
|
+
[DeviceType.Dryer]: {
|
|
85261
|
+
state: { running: {
|
|
85262
|
+
whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
|
|
85263
|
+
whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
|
|
85264
|
+
} },
|
|
85265
|
+
events: {
|
|
85266
|
+
onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
|
|
85267
|
+
onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
|
|
85268
|
+
}
|
|
85269
|
+
},
|
|
85270
|
+
[DeviceType.Dishwasher]: {
|
|
85271
|
+
state: { running: {
|
|
85272
|
+
whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
|
|
85273
|
+
whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
|
|
85274
|
+
} },
|
|
85275
|
+
events: {
|
|
85276
|
+
onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
|
|
85277
|
+
onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
|
|
85278
|
+
}
|
|
85279
|
+
}
|
|
85280
|
+
} }
|
|
84126
85281
|
},
|
|
84127
85282
|
update: {
|
|
84128
85283
|
scope: "device",
|
|
@@ -84687,7 +85842,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84687
85842
|
state: [
|
|
84688
85843
|
{
|
|
84689
85844
|
kind: "numeric",
|
|
84690
|
-
field: "
|
|
85845
|
+
field: "co2",
|
|
84691
85846
|
quantity: t("the CO₂", "la CO₂"),
|
|
84692
85847
|
label: t("CO₂", "CO₂"),
|
|
84693
85848
|
unit: "ppm",
|
|
@@ -84695,7 +85850,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84695
85850
|
},
|
|
84696
85851
|
{
|
|
84697
85852
|
kind: "numeric",
|
|
84698
|
-
field: "
|
|
85853
|
+
field: "voc",
|
|
84699
85854
|
quantity: t("the VOC level", "il livello di COV"),
|
|
84700
85855
|
label: t("VOC", "COV"),
|
|
84701
85856
|
unit: "ppb",
|
|
@@ -84731,7 +85886,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84731
85886
|
noun: t("Light level", "Luminosità ambiente"),
|
|
84732
85887
|
state: [{
|
|
84733
85888
|
kind: "numeric",
|
|
84734
|
-
field: "
|
|
85889
|
+
field: "illuminance",
|
|
84735
85890
|
quantity: t("the light level", "la luminosità"),
|
|
84736
85891
|
label: t("Illuminance", "Illuminamento"),
|
|
84737
85892
|
unit: "lx",
|
|
@@ -84894,6 +86049,10 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84894
86049
|
icon: "flame"
|
|
84895
86050
|
}]
|
|
84896
86051
|
},
|
|
86052
|
+
"group-active": {
|
|
86053
|
+
scope: "device",
|
|
86054
|
+
noun: t("Group state", "Stato del gruppo")
|
|
86055
|
+
},
|
|
84897
86056
|
"group-commands": {
|
|
84898
86057
|
scope: "device",
|
|
84899
86058
|
noun: t("Group command", "Comando di gruppo")
|
|
@@ -84930,7 +86089,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84930
86089
|
noun: t("Humidity", "Umidità"),
|
|
84931
86090
|
state: [{
|
|
84932
86091
|
kind: "numeric",
|
|
84933
|
-
field: "
|
|
86092
|
+
field: "humidity",
|
|
84934
86093
|
quantity: t("the humidity", "l’umidità"),
|
|
84935
86094
|
label: t("Humidity", "Umidità"),
|
|
84936
86095
|
unit: "%",
|
|
@@ -84950,6 +86109,17 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84950
86109
|
}],
|
|
84951
86110
|
events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
|
|
84952
86111
|
},
|
|
86112
|
+
meter: {
|
|
86113
|
+
scope: "device",
|
|
86114
|
+
noun: t("Meter", "Contatore"),
|
|
86115
|
+
state: [{
|
|
86116
|
+
kind: "numeric",
|
|
86117
|
+
field: "value",
|
|
86118
|
+
quantity: t("the total", "il totale"),
|
|
86119
|
+
label: t("Total", "Totale"),
|
|
86120
|
+
icon: "gauge"
|
|
86121
|
+
}]
|
|
86122
|
+
},
|
|
84953
86123
|
"numeric-sensor": {
|
|
84954
86124
|
scope: "device",
|
|
84955
86125
|
noun: t("Sensor", "Sensore"),
|
|
@@ -84967,7 +86137,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84967
86137
|
state: [
|
|
84968
86138
|
{
|
|
84969
86139
|
kind: "numeric",
|
|
84970
|
-
field: "
|
|
86140
|
+
field: "power",
|
|
84971
86141
|
quantity: t("the power draw", "il consumo"),
|
|
84972
86142
|
label: t("Power", "Potenza"),
|
|
84973
86143
|
unit: "W",
|
|
@@ -84975,7 +86145,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84975
86145
|
},
|
|
84976
86146
|
{
|
|
84977
86147
|
kind: "numeric",
|
|
84978
|
-
field: "
|
|
86148
|
+
field: "energy",
|
|
84979
86149
|
quantity: t("the energy used", "l’energia consumata"),
|
|
84980
86150
|
label: t("Energy", "Energia"),
|
|
84981
86151
|
unit: "kWh",
|
|
@@ -84983,7 +86153,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84983
86153
|
},
|
|
84984
86154
|
{
|
|
84985
86155
|
kind: "numeric",
|
|
84986
|
-
field: "
|
|
86156
|
+
field: "voltage",
|
|
84987
86157
|
quantity: t("the voltage", "la tensione"),
|
|
84988
86158
|
label: t("Voltage", "Tensione"),
|
|
84989
86159
|
unit: "V",
|
|
@@ -84991,7 +86161,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84991
86161
|
},
|
|
84992
86162
|
{
|
|
84993
86163
|
kind: "numeric",
|
|
84994
|
-
field: "
|
|
86164
|
+
field: "current",
|
|
84995
86165
|
quantity: t("the current", "la corrente"),
|
|
84996
86166
|
label: t("Current", "Corrente"),
|
|
84997
86167
|
unit: "A",
|
|
@@ -85008,13 +86178,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85008
86178
|
label: t("Where", "Posizione"),
|
|
85009
86179
|
phrase: t("When {device} is at {value}", "Quando {device} è in {value}"),
|
|
85010
86180
|
icon: "map-pin"
|
|
85011
|
-
}, {
|
|
85012
|
-
kind: "numeric",
|
|
85013
|
-
field: "batteryPercent",
|
|
85014
|
-
quantity: t("the phone battery", "la batteria del telefono"),
|
|
85015
|
-
label: t("Battery", "Batteria"),
|
|
85016
|
-
unit: "%",
|
|
85017
|
-
icon: "battery"
|
|
85018
86181
|
}]
|
|
85019
86182
|
},
|
|
85020
86183
|
"pressure-sensor": {
|
|
@@ -85022,7 +86185,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85022
86185
|
noun: t("Pressure", "Pressione"),
|
|
85023
86186
|
state: [{
|
|
85024
86187
|
kind: "numeric",
|
|
85025
|
-
field: "
|
|
86188
|
+
field: "pressure",
|
|
85026
86189
|
quantity: t("the pressure", "la pressione"),
|
|
85027
86190
|
label: t("Pressure", "Pressione"),
|
|
85028
86191
|
unit: "hPa",
|
|
@@ -85058,7 +86221,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85058
86221
|
noun: t("Temperature", "Temperatura"),
|
|
85059
86222
|
state: [{
|
|
85060
86223
|
kind: "numeric",
|
|
85061
|
-
field: "
|
|
86224
|
+
field: "temperature",
|
|
85062
86225
|
quantity: t("the temperature", "la temperatura"),
|
|
85063
86226
|
label: t("Temperature", "Temperatura"),
|
|
85064
86227
|
unit: "°C",
|
|
@@ -85129,7 +86292,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85129
86292
|
state: [
|
|
85130
86293
|
{
|
|
85131
86294
|
kind: "numeric",
|
|
85132
|
-
field: "
|
|
86295
|
+
field: "level",
|
|
85133
86296
|
quantity: t("the battery level", "il livello della batteria"),
|
|
85134
86297
|
label: t("Battery", "Batteria"),
|
|
85135
86298
|
unit: "%",
|
|
@@ -85171,7 +86334,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85171
86334
|
icon: "battery-charging"
|
|
85172
86335
|
}
|
|
85173
86336
|
],
|
|
85174
|
-
events: { onStatusChanged: { notOperatorFacing: "the
|
|
86337
|
+
events: { onStatusChanged: { notOperatorFacing: "the level / sleeping / charging state triggers each read one field of this change" } },
|
|
85175
86338
|
extraEvents: [{
|
|
85176
86339
|
category: EventCategory.DeviceAwake,
|
|
85177
86340
|
label: t("Woke up", "Risvegliato"),
|
|
@@ -85189,7 +86352,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85189
86352
|
noun: t("Network link", "Collegamento di rete"),
|
|
85190
86353
|
state: [{
|
|
85191
86354
|
kind: "numeric",
|
|
85192
|
-
field: "
|
|
86355
|
+
field: "signal",
|
|
85193
86356
|
quantity: t("the signal strength", "l’intensità del segnale"),
|
|
85194
86357
|
label: t("Signal", "Segnale"),
|
|
85195
86358
|
unit: "%",
|
|
@@ -85282,6 +86445,8 @@ var ML = "model and training machinery — no device action";
|
|
|
85282
86445
|
var INTERNAL = "internal plumbing between addons — never operator-facing";
|
|
85283
86446
|
/** Every system-scoped cap, with why it is not a device's vocabulary. */
|
|
85284
86447
|
var SYSTEM_CAP_VOCABULARY = {
|
|
86448
|
+
"action-source": system(INTERNAL),
|
|
86449
|
+
actions: system("declared actions — run from the Actions page and from the cameras that link them"),
|
|
85285
86450
|
"addon-pages": system(UI_PLUMBING),
|
|
85286
86451
|
"addon-pages-source": system(UI_PLUMBING),
|
|
85287
86452
|
"addon-routes": system(UI_PLUMBING),
|
|
@@ -85422,8 +86587,47 @@ function projectAction(cap, action) {
|
|
|
85422
86587
|
};
|
|
85423
86588
|
}
|
|
85424
86589
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
85425
|
-
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;
|
|
86590
|
+
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;
|
|
85426
86591
|
DeviceFeature.BatteryOperated;
|
|
86592
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
86593
|
+
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.";
|
|
86594
|
+
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.";
|
|
86595
|
+
var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
|
|
86596
|
+
var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
|
|
86597
|
+
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.";
|
|
86598
|
+
function withheld(reason) {
|
|
86599
|
+
return {
|
|
86600
|
+
[DeviceType.Washer]: {
|
|
86601
|
+
exported: false,
|
|
86602
|
+
reason
|
|
86603
|
+
},
|
|
86604
|
+
[DeviceType.Dryer]: {
|
|
86605
|
+
exported: false,
|
|
86606
|
+
reason
|
|
86607
|
+
},
|
|
86608
|
+
[DeviceType.Dishwasher]: {
|
|
86609
|
+
exported: false,
|
|
86610
|
+
reason
|
|
86611
|
+
}
|
|
86612
|
+
};
|
|
86613
|
+
}
|
|
86614
|
+
function offered(received) {
|
|
86615
|
+
return {
|
|
86616
|
+
[DeviceType.Washer]: {
|
|
86617
|
+
exported: true,
|
|
86618
|
+
as: received
|
|
86619
|
+
},
|
|
86620
|
+
[DeviceType.Dryer]: {
|
|
86621
|
+
exported: true,
|
|
86622
|
+
as: received
|
|
86623
|
+
},
|
|
86624
|
+
[DeviceType.Dishwasher]: {
|
|
86625
|
+
exported: true,
|
|
86626
|
+
as: received
|
|
86627
|
+
}
|
|
86628
|
+
};
|
|
86629
|
+
}
|
|
86630
|
+
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
85427
86631
|
Object.freeze({
|
|
85428
86632
|
"accessories.getStatus": [{
|
|
85429
86633
|
name: "deviceId",
|
|
@@ -85439,6 +86643,31 @@ Object.freeze({
|
|
|
85439
86643
|
form: "single",
|
|
85440
86644
|
optional: false
|
|
85441
86645
|
}],
|
|
86646
|
+
"actions.get": [{
|
|
86647
|
+
name: "deviceId",
|
|
86648
|
+
form: "single",
|
|
86649
|
+
optional: true
|
|
86650
|
+
}],
|
|
86651
|
+
"actions.getLinks": [{
|
|
86652
|
+
name: "deviceId",
|
|
86653
|
+
form: "single",
|
|
86654
|
+
optional: false
|
|
86655
|
+
}],
|
|
86656
|
+
"actions.listForCamera": [{
|
|
86657
|
+
name: "deviceId",
|
|
86658
|
+
form: "single",
|
|
86659
|
+
optional: false
|
|
86660
|
+
}],
|
|
86661
|
+
"actions.run": [{
|
|
86662
|
+
name: "deviceId",
|
|
86663
|
+
form: "single",
|
|
86664
|
+
optional: true
|
|
86665
|
+
}],
|
|
86666
|
+
"actions.setLinks": [{
|
|
86667
|
+
name: "deviceId",
|
|
86668
|
+
form: "single",
|
|
86669
|
+
optional: false
|
|
86670
|
+
}],
|
|
85442
86671
|
"addonSettings.getDeviceSettings": [{
|
|
85443
86672
|
name: "deviceId",
|
|
85444
86673
|
form: "single",
|
|
@@ -86390,6 +87619,11 @@ Object.freeze({
|
|
|
86390
87619
|
form: "single",
|
|
86391
87620
|
optional: false
|
|
86392
87621
|
}],
|
|
87622
|
+
"groupActive.getStatus": [{
|
|
87623
|
+
name: "deviceId",
|
|
87624
|
+
form: "single",
|
|
87625
|
+
optional: false
|
|
87626
|
+
}],
|
|
86393
87627
|
"groupCommands.getStatus": [{
|
|
86394
87628
|
name: "deviceId",
|
|
86395
87629
|
form: "single",
|
|
@@ -86600,6 +87834,11 @@ Object.freeze({
|
|
|
86600
87834
|
form: "single",
|
|
86601
87835
|
optional: false
|
|
86602
87836
|
}],
|
|
87837
|
+
"meter.getStatus": [{
|
|
87838
|
+
name: "deviceId",
|
|
87839
|
+
form: "single",
|
|
87840
|
+
optional: false
|
|
87841
|
+
}],
|
|
86603
87842
|
"motion.getStatus": [{
|
|
86604
87843
|
name: "deviceId",
|
|
86605
87844
|
form: "single",
|
|
@@ -88207,7 +89446,6 @@ Object.freeze({
|
|
|
88207
89446
|
"network-access": "ingress",
|
|
88208
89447
|
"smtp-provider": "email"
|
|
88209
89448
|
});
|
|
88210
|
-
DeviceType.Camera;
|
|
88211
89449
|
/**
|
|
88212
89450
|
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
88213
89451
|
* `z.infer`, they do not restate the shape.
|
|
@@ -89653,6 +90891,7 @@ function bestLocationMatch(externalName, existing, threshold = .8) {
|
|
|
89653
90891
|
}
|
|
89654
90892
|
return best;
|
|
89655
90893
|
}
|
|
90894
|
+
String.raw`(?<=^|["'(\s>,\[{])`;
|
|
89656
90895
|
var MB = 1024 * 1024;
|
|
89657
90896
|
1024 * MB, 3072 * MB;
|
|
89658
90897
|
//#endregion
|
|
@@ -115005,15 +116244,15 @@ var HmActuatorDevice = class extends HmChildDevice {
|
|
|
115005
116244
|
* keep that class a thin actuator-wiring layer.
|
|
115006
116245
|
*/
|
|
115007
116246
|
var COLD_TEMPERATURE = {
|
|
115008
|
-
|
|
116247
|
+
temperature: 0,
|
|
115009
116248
|
lastFetchedAt: 0
|
|
115010
116249
|
};
|
|
115011
116250
|
var COLD_HUMIDITY = {
|
|
115012
|
-
|
|
116251
|
+
humidity: 0,
|
|
115013
116252
|
lastFetchedAt: 0
|
|
115014
116253
|
};
|
|
115015
116254
|
var COLD_AMBIENT_LIGHT = {
|
|
115016
|
-
|
|
116255
|
+
illuminance: 0,
|
|
115017
116256
|
lastFetchedAt: 0
|
|
115018
116257
|
};
|
|
115019
116258
|
var COLD_POWER = { lastFetchedAt: 0 };
|
|
@@ -115105,15 +116344,15 @@ var HmSensorDevice = class extends HmChildDevice {
|
|
|
115105
116344
|
decodeSensorPush(parameter, value, now) {
|
|
115106
116345
|
if (typeof value !== "number") return;
|
|
115107
116346
|
if (this.caps.has("temperature-sensor") && parameter === "ACTUAL_TEMPERATURE") this.runtimeState.setCapState("temperature-sensor", {
|
|
115108
|
-
|
|
116347
|
+
temperature: value,
|
|
115109
116348
|
lastFetchedAt: now
|
|
115110
116349
|
});
|
|
115111
116350
|
if (this.caps.has("humidity-sensor") && parameter === "HUMIDITY") this.runtimeState.setCapState("humidity-sensor", {
|
|
115112
|
-
|
|
116351
|
+
humidity: value,
|
|
115113
116352
|
lastFetchedAt: now
|
|
115114
116353
|
});
|
|
115115
116354
|
if (this.caps.has("ambient-light-sensor") && parameter === "ILLUMINATION") this.runtimeState.setCapState("ambient-light-sensor", {
|
|
115116
|
-
|
|
116355
|
+
illuminance: Math.max(0, value),
|
|
115117
116356
|
lastFetchedAt: now
|
|
115118
116357
|
});
|
|
115119
116358
|
this.decodePowerPush(parameter, value, now);
|
|
@@ -115126,28 +116365,28 @@ var HmSensorDevice = class extends HmChildDevice {
|
|
|
115126
116365
|
case "POWER":
|
|
115127
116366
|
this.runtimeState.setCapState("power-meter", {
|
|
115128
116367
|
...prev,
|
|
115129
|
-
|
|
116368
|
+
power: value,
|
|
115130
116369
|
lastFetchedAt: now
|
|
115131
116370
|
});
|
|
115132
116371
|
return;
|
|
115133
116372
|
case "ENERGY_COUNTER":
|
|
115134
116373
|
this.runtimeState.setCapState("power-meter", {
|
|
115135
116374
|
...prev,
|
|
115136
|
-
|
|
116375
|
+
energy: value / 1e3,
|
|
115137
116376
|
lastFetchedAt: now
|
|
115138
116377
|
});
|
|
115139
116378
|
return;
|
|
115140
116379
|
case "VOLTAGE":
|
|
115141
116380
|
this.runtimeState.setCapState("power-meter", {
|
|
115142
116381
|
...prev,
|
|
115143
|
-
|
|
116382
|
+
voltage: value,
|
|
115144
116383
|
lastFetchedAt: now
|
|
115145
116384
|
});
|
|
115146
116385
|
return;
|
|
115147
116386
|
case "CURRENT":
|
|
115148
116387
|
this.runtimeState.setCapState("power-meter", {
|
|
115149
116388
|
...prev,
|
|
115150
|
-
|
|
116389
|
+
current: value / 1e3,
|
|
115151
116390
|
lastFetchedAt: now
|
|
115152
116391
|
});
|
|
115153
116392
|
return;
|
|
@@ -115245,7 +116484,7 @@ var COLD_BINARY = {
|
|
|
115245
116484
|
lastChangedAt: 0
|
|
115246
116485
|
};
|
|
115247
116486
|
var COLD_TEMP = {
|
|
115248
|
-
|
|
116487
|
+
temperature: 0,
|
|
115249
116488
|
lastFetchedAt: 0
|
|
115250
116489
|
};
|
|
115251
116490
|
/**
|
|
@@ -115309,7 +116548,7 @@ var HmDiagnosticDevice = class extends HmChildDevice {
|
|
|
115309
116548
|
if (stored === null) return;
|
|
115310
116549
|
if (this.capName === "temperature-sensor") {
|
|
115311
116550
|
this.runtimeState.setCapState("temperature-sensor", {
|
|
115312
|
-
|
|
116551
|
+
temperature: stored,
|
|
115313
116552
|
lastFetchedAt: now
|
|
115314
116553
|
});
|
|
115315
116554
|
return;
|