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