@camstack/addon-matter-broker 0.2.150 → 0.2.152
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 +8 -1
- package/README.md +27 -8
- package/dist/addon.js +1529 -290
- package/dist/addon.mjs +1529 -290
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -13,7 +13,7 @@ let node_stream_promises = require("node:stream/promises");
|
|
|
13
13
|
let node_net = require("node:net");
|
|
14
14
|
let node_dgram = require("node:dgram");
|
|
15
15
|
node_dgram = require_esm.__toESM(node_dgram, 1);
|
|
16
|
-
//#region ../types/dist/event-category-
|
|
16
|
+
//#region ../types/dist/event-category-DmZDPDf_.mjs
|
|
17
17
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
18
18
|
EventCategory["SystemBoot"] = "system.boot";
|
|
19
19
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -640,7 +640,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
640
640
|
* (a track was promoted to a stationary-registry entry) or departs (the
|
|
641
641
|
* object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
|
|
642
642
|
* the dedicated "Stationary" section — never the live event feed. Payload:
|
|
643
|
-
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
|
|
643
|
+
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp, observedAt, zones }`.
|
|
644
644
|
*/
|
|
645
645
|
EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
|
|
646
646
|
/**
|
|
@@ -778,6 +778,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
778
778
|
*/
|
|
779
779
|
EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
|
|
780
780
|
/**
|
|
781
|
+
* An action run started or ended. Emitted by the `actions` surface.
|
|
782
|
+
* Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
|
|
783
|
+
*/
|
|
784
|
+
EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
|
|
785
|
+
/**
|
|
781
786
|
* Progress update from a running model conversion job.
|
|
782
787
|
* Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
|
|
783
788
|
* Emitted by `addon-model-studio` on the converting node.
|
|
@@ -5589,7 +5594,7 @@ var ZodIssueCode = {
|
|
|
5589
5594
|
var ZodFirstPartyTypeKind;
|
|
5590
5595
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5591
5596
|
//#endregion
|
|
5592
|
-
//#region ../types/dist/sleep-
|
|
5597
|
+
//#region ../types/dist/sleep-Crlmu_tO.mjs
|
|
5593
5598
|
/**
|
|
5594
5599
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5595
5600
|
* window to float samples (D455).
|
|
@@ -8897,6 +8902,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
|
|
|
8897
8902
|
/** Every capability name, so addon code can check a name without the schema graph. */
|
|
8898
8903
|
var CAP_NAMES = Object.freeze([
|
|
8899
8904
|
"accessories",
|
|
8905
|
+
"action-source",
|
|
8906
|
+
"actions",
|
|
8900
8907
|
"addon-pages",
|
|
8901
8908
|
"addon-pages-source",
|
|
8902
8909
|
"addon-routes",
|
|
@@ -8970,6 +8977,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8970
8977
|
"filesystem-browse",
|
|
8971
8978
|
"flood",
|
|
8972
8979
|
"gas",
|
|
8980
|
+
"group-active",
|
|
8973
8981
|
"group-commands",
|
|
8974
8982
|
"group-status",
|
|
8975
8983
|
"heating-valve",
|
|
@@ -8992,6 +9000,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8992
9000
|
"login-method",
|
|
8993
9001
|
"media-player",
|
|
8994
9002
|
"mesh-network",
|
|
9003
|
+
"meter",
|
|
8995
9004
|
"metrics-provider",
|
|
8996
9005
|
"model-convert",
|
|
8997
9006
|
"model-distributor",
|
|
@@ -9083,11 +9092,11 @@ var CAP_NAMES = Object.freeze([
|
|
|
9083
9092
|
Object.freeze({
|
|
9084
9093
|
"air-quality-sensor": [
|
|
9085
9094
|
{
|
|
9086
|
-
"path": "
|
|
9095
|
+
"path": "co2",
|
|
9087
9096
|
"valueType": "number"
|
|
9088
9097
|
},
|
|
9089
9098
|
{
|
|
9090
|
-
"path": "
|
|
9099
|
+
"path": "voc",
|
|
9091
9100
|
"valueType": "number"
|
|
9092
9101
|
},
|
|
9093
9102
|
{
|
|
@@ -9137,7 +9146,7 @@ Object.freeze({
|
|
|
9137
9146
|
}
|
|
9138
9147
|
],
|
|
9139
9148
|
"ambient-light-sensor": [{
|
|
9140
|
-
"path": "
|
|
9149
|
+
"path": "illuminance",
|
|
9141
9150
|
"valueType": "number"
|
|
9142
9151
|
}],
|
|
9143
9152
|
"audio-metrics": [
|
|
@@ -9198,7 +9207,7 @@ Object.freeze({
|
|
|
9198
9207
|
],
|
|
9199
9208
|
"battery": [
|
|
9200
9209
|
{
|
|
9201
|
-
"path": "
|
|
9210
|
+
"path": "level",
|
|
9202
9211
|
"valueType": "number"
|
|
9203
9212
|
},
|
|
9204
9213
|
{
|
|
@@ -9220,7 +9229,7 @@ Object.freeze({
|
|
|
9220
9229
|
"valueType": "number"
|
|
9221
9230
|
},
|
|
9222
9231
|
{
|
|
9223
|
-
"path": "
|
|
9232
|
+
"path": "voltage",
|
|
9224
9233
|
"valueType": "number"
|
|
9225
9234
|
},
|
|
9226
9235
|
{
|
|
@@ -9241,7 +9250,7 @@ Object.freeze({
|
|
|
9241
9250
|
"valueType": "boolean"
|
|
9242
9251
|
}],
|
|
9243
9252
|
"brightness": [{
|
|
9244
|
-
"path": "
|
|
9253
|
+
"path": "level",
|
|
9245
9254
|
"valueType": "number"
|
|
9246
9255
|
}],
|
|
9247
9256
|
"broker": [{
|
|
@@ -9442,7 +9451,7 @@ Object.freeze({
|
|
|
9442
9451
|
"valueType": "number"
|
|
9443
9452
|
},
|
|
9444
9453
|
{
|
|
9445
|
-
"path": "
|
|
9454
|
+
"path": "colorTemperature",
|
|
9446
9455
|
"valueType": "number"
|
|
9447
9456
|
}
|
|
9448
9457
|
],
|
|
@@ -9621,15 +9630,15 @@ Object.freeze({
|
|
|
9621
9630
|
"valueType": "string"
|
|
9622
9631
|
},
|
|
9623
9632
|
{
|
|
9624
|
-
"path": "
|
|
9633
|
+
"path": "remaining",
|
|
9625
9634
|
"valueType": "number"
|
|
9626
9635
|
},
|
|
9627
9636
|
{
|
|
9628
|
-
"path": "
|
|
9637
|
+
"path": "delayedStart",
|
|
9629
9638
|
"valueType": "number"
|
|
9630
9639
|
},
|
|
9631
9640
|
{
|
|
9632
|
-
"path": "
|
|
9641
|
+
"path": "progress",
|
|
9633
9642
|
"valueType": "number"
|
|
9634
9643
|
},
|
|
9635
9644
|
{
|
|
@@ -9743,7 +9752,7 @@ Object.freeze({
|
|
|
9743
9752
|
],
|
|
9744
9753
|
"fan-control": [
|
|
9745
9754
|
{
|
|
9746
|
-
"path": "
|
|
9755
|
+
"path": "speed",
|
|
9747
9756
|
"valueType": "number"
|
|
9748
9757
|
},
|
|
9749
9758
|
{
|
|
@@ -9790,6 +9799,10 @@ Object.freeze({
|
|
|
9790
9799
|
"path": "detected",
|
|
9791
9800
|
"valueType": "boolean"
|
|
9792
9801
|
}],
|
|
9802
|
+
"group-active": [{
|
|
9803
|
+
"path": "active",
|
|
9804
|
+
"valueType": "boolean"
|
|
9805
|
+
}],
|
|
9793
9806
|
"group-status": [
|
|
9794
9807
|
{
|
|
9795
9808
|
"path": "included",
|
|
@@ -9839,7 +9852,7 @@ Object.freeze({
|
|
|
9839
9852
|
}
|
|
9840
9853
|
],
|
|
9841
9854
|
"humidity-sensor": [{
|
|
9842
|
-
"path": "
|
|
9855
|
+
"path": "humidity",
|
|
9843
9856
|
"valueType": "number"
|
|
9844
9857
|
}],
|
|
9845
9858
|
"image": [{
|
|
@@ -9949,7 +9962,7 @@ Object.freeze({
|
|
|
9949
9962
|
"valueType": "number"
|
|
9950
9963
|
},
|
|
9951
9964
|
{
|
|
9952
|
-
"path": "
|
|
9965
|
+
"path": "progress",
|
|
9953
9966
|
"valueType": "number"
|
|
9954
9967
|
},
|
|
9955
9968
|
{
|
|
@@ -10028,11 +10041,11 @@ Object.freeze({
|
|
|
10028
10041
|
"valueType": "string"
|
|
10029
10042
|
},
|
|
10030
10043
|
{
|
|
10031
|
-
"path": "
|
|
10044
|
+
"path": "position",
|
|
10032
10045
|
"valueType": "number"
|
|
10033
10046
|
},
|
|
10034
10047
|
{
|
|
10035
|
-
"path": "
|
|
10048
|
+
"path": "duration",
|
|
10036
10049
|
"valueType": "number"
|
|
10037
10050
|
},
|
|
10038
10051
|
{
|
|
@@ -10049,6 +10062,10 @@ Object.freeze({
|
|
|
10049
10062
|
]
|
|
10050
10063
|
}
|
|
10051
10064
|
],
|
|
10065
|
+
"meter": [{
|
|
10066
|
+
"path": "value",
|
|
10067
|
+
"valueType": "number"
|
|
10068
|
+
}],
|
|
10052
10069
|
"motion": [
|
|
10053
10070
|
{
|
|
10054
10071
|
"path": "detected",
|
|
@@ -10130,11 +10147,11 @@ Object.freeze({
|
|
|
10130
10147
|
]
|
|
10131
10148
|
},
|
|
10132
10149
|
{
|
|
10133
|
-
"path": "
|
|
10150
|
+
"path": "signal",
|
|
10134
10151
|
"valueType": "number"
|
|
10135
10152
|
},
|
|
10136
10153
|
{
|
|
10137
|
-
"path": "
|
|
10154
|
+
"path": "rssi",
|
|
10138
10155
|
"valueType": "number"
|
|
10139
10156
|
},
|
|
10140
10157
|
{
|
|
@@ -10189,6 +10206,18 @@ Object.freeze({
|
|
|
10189
10206
|
"path": "foodLevel",
|
|
10190
10207
|
"valueType": "number"
|
|
10191
10208
|
},
|
|
10209
|
+
{
|
|
10210
|
+
"path": "bowlFood",
|
|
10211
|
+
"valueType": "number"
|
|
10212
|
+
},
|
|
10213
|
+
{
|
|
10214
|
+
"path": "eating",
|
|
10215
|
+
"valueType": "boolean"
|
|
10216
|
+
},
|
|
10217
|
+
{
|
|
10218
|
+
"path": "supportsMarkRefilled",
|
|
10219
|
+
"valueType": "boolean"
|
|
10220
|
+
},
|
|
10192
10221
|
{
|
|
10193
10222
|
"path": "food1",
|
|
10194
10223
|
"valueType": "number"
|
|
@@ -10253,23 +10282,23 @@ Object.freeze({
|
|
|
10253
10282
|
],
|
|
10254
10283
|
"power-meter": [
|
|
10255
10284
|
{
|
|
10256
|
-
"path": "
|
|
10285
|
+
"path": "power",
|
|
10257
10286
|
"valueType": "number"
|
|
10258
10287
|
},
|
|
10259
10288
|
{
|
|
10260
|
-
"path": "
|
|
10289
|
+
"path": "energy",
|
|
10261
10290
|
"valueType": "number"
|
|
10262
10291
|
},
|
|
10263
10292
|
{
|
|
10264
|
-
"path": "
|
|
10293
|
+
"path": "voltage",
|
|
10265
10294
|
"valueType": "number"
|
|
10266
10295
|
},
|
|
10267
10296
|
{
|
|
10268
|
-
"path": "
|
|
10297
|
+
"path": "current",
|
|
10269
10298
|
"valueType": "number"
|
|
10270
10299
|
},
|
|
10271
10300
|
{
|
|
10272
|
-
"path": "
|
|
10301
|
+
"path": "apparentPower",
|
|
10273
10302
|
"valueType": "number"
|
|
10274
10303
|
},
|
|
10275
10304
|
{
|
|
@@ -10297,14 +10326,10 @@ Object.freeze({
|
|
|
10297
10326
|
{
|
|
10298
10327
|
"path": "gps.accuracyMeters",
|
|
10299
10328
|
"valueType": "number"
|
|
10300
|
-
},
|
|
10301
|
-
{
|
|
10302
|
-
"path": "batteryPercent",
|
|
10303
|
-
"valueType": "number"
|
|
10304
10329
|
}
|
|
10305
10330
|
],
|
|
10306
10331
|
"pressure-sensor": [{
|
|
10307
|
-
"path": "
|
|
10332
|
+
"path": "pressure",
|
|
10308
10333
|
"valueType": "number"
|
|
10309
10334
|
}],
|
|
10310
10335
|
"privacy-mask": [{
|
|
@@ -10618,7 +10643,7 @@ Object.freeze({
|
|
|
10618
10643
|
"valueType": "boolean"
|
|
10619
10644
|
}],
|
|
10620
10645
|
"temperature-sensor": [{
|
|
10621
|
-
"path": "
|
|
10646
|
+
"path": "temperature",
|
|
10622
10647
|
"valueType": "number"
|
|
10623
10648
|
}],
|
|
10624
10649
|
"update": [
|
|
@@ -10666,7 +10691,7 @@ Object.freeze({
|
|
|
10666
10691
|
"valueType": "string"
|
|
10667
10692
|
},
|
|
10668
10693
|
{
|
|
10669
|
-
"path": "
|
|
10694
|
+
"path": "progress",
|
|
10670
10695
|
"valueType": "number"
|
|
10671
10696
|
},
|
|
10672
10697
|
{
|
|
@@ -10792,7 +10817,7 @@ object({
|
|
|
10792
10817
|
params: record(string$2(), union([string$2(), number()])).optional()
|
|
10793
10818
|
});
|
|
10794
10819
|
//#endregion
|
|
10795
|
-
//#region ../types/dist/err-msg-
|
|
10820
|
+
//#region ../types/dist/err-msg-DIorGYeC.mjs
|
|
10796
10821
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
10797
10822
|
DeviceType["Camera"] = "camera";
|
|
10798
10823
|
DeviceType["Hub"] = "hub";
|
|
@@ -10886,8 +10911,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
10886
10911
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
10887
10912
|
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
10888
10913
|
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
10889
|
-
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10914
|
+
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10915
|
+
* A plug-built dishwasher carries running-state and power instead. */
|
|
10890
10916
|
DeviceType["Dishwasher"] = "dishwasher";
|
|
10917
|
+
/** Washing machine. A plug-built one is running while its power stays
|
|
10918
|
+
* above a threshold. No program cap — that surface is the dishwasher's. */
|
|
10919
|
+
DeviceType["Washer"] = "washer";
|
|
10920
|
+
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
10921
|
+
DeviceType["Dryer"] = "dryer";
|
|
10891
10922
|
return DeviceType;
|
|
10892
10923
|
}({});
|
|
10893
10924
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -11145,7 +11176,7 @@ function errMsg(err) {
|
|
|
11145
11176
|
return String(err);
|
|
11146
11177
|
}
|
|
11147
11178
|
//#endregion
|
|
11148
|
-
//#region ../types/dist/composition-
|
|
11179
|
+
//#region ../types/dist/composition-CZImLpOI.mjs
|
|
11149
11180
|
/**
|
|
11150
11181
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11151
11182
|
*
|
|
@@ -11460,6 +11491,13 @@ var table = {
|
|
|
11460
11491
|
apply: () => {
|
|
11461
11492
|
throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
|
|
11462
11493
|
}
|
|
11494
|
+
},
|
|
11495
|
+
schmitt: {
|
|
11496
|
+
minArgs: 3,
|
|
11497
|
+
maxArgs: 5,
|
|
11498
|
+
apply: () => {
|
|
11499
|
+
throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
|
|
11500
|
+
}
|
|
11463
11501
|
}
|
|
11464
11502
|
};
|
|
11465
11503
|
Object.freeze(Object.assign(Object.create(null), table));
|
|
@@ -11901,6 +11939,99 @@ function validateExpressionSource(src) {
|
|
|
11901
11939
|
return null;
|
|
11902
11940
|
}
|
|
11903
11941
|
/**
|
|
11942
|
+
* One power reading becomes a washer, dryer or dishwasher (D944 helper).
|
|
11943
|
+
*
|
|
11944
|
+
* Standby of the control board is under 5 W; a motor or a heater is far
|
|
11945
|
+
* above that, so 5 W is the line. A door lock or a short pump spike is not
|
|
11946
|
+
* a cycle, so running starts only after 30 s above the line.
|
|
11947
|
+
*
|
|
11948
|
+
* A wash, dry or dishwasher pause (soak, heat-wait, heater off between
|
|
11949
|
+
* bursts) lasts several minutes. Ending the cycle on the first dip would
|
|
11950
|
+
* report "finished" in the middle of the program, so the stop dwell is
|
|
11951
|
+
* 10 minutes on every type. The number is stored per type so one machine
|
|
11952
|
+
* can move later without moving the others.
|
|
11953
|
+
*/
|
|
11954
|
+
var APPLIANCE_TEMPLATE_ID = "appliance";
|
|
11955
|
+
var APPLIANCE_KINDS = [
|
|
11956
|
+
"washer",
|
|
11957
|
+
"dryer",
|
|
11958
|
+
"dishwasher"
|
|
11959
|
+
];
|
|
11960
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
11961
|
+
/**
|
|
11962
|
+
* Statistics and integrals over a history read (D944).
|
|
11963
|
+
*
|
|
11964
|
+
* Pure: the same points always give the same answer, so a restart recomputes
|
|
11965
|
+
* rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
|
|
11966
|
+
* never the mean of the samples. A hole with no held value is not integrated
|
|
11967
|
+
* as zero. A truncated read is not published.
|
|
11968
|
+
*/
|
|
11969
|
+
var STATISTIC_STATS = [
|
|
11970
|
+
"min",
|
|
11971
|
+
"max",
|
|
11972
|
+
"mean",
|
|
11973
|
+
"sum",
|
|
11974
|
+
"count",
|
|
11975
|
+
"change"
|
|
11976
|
+
];
|
|
11977
|
+
/**
|
|
11978
|
+
* The range a statistics or integral helper asks history for (D944).
|
|
11979
|
+
*
|
|
11980
|
+
* Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
|
|
11981
|
+
* or 25 hours across a DST change. A tier is eligible only when its retention
|
|
11982
|
+
* is strictly longer than the longest that range can be. Forever qualifies.
|
|
11983
|
+
* Equal to a tier's retention is a refusal, not a short answer.
|
|
11984
|
+
*/
|
|
11985
|
+
var MINUTE_MS = 6e4;
|
|
11986
|
+
var HOUR_MS = 60 * MINUTE_MS;
|
|
11987
|
+
var DAY_MS = 24 * HOUR_MS;
|
|
11988
|
+
var ROLLING_SPANS = [
|
|
11989
|
+
"1h",
|
|
11990
|
+
"24h",
|
|
11991
|
+
"2d",
|
|
11992
|
+
"7d",
|
|
11993
|
+
"30d",
|
|
11994
|
+
"custom"
|
|
11995
|
+
];
|
|
11996
|
+
var CALENDAR_PERIODS = [
|
|
11997
|
+
"today",
|
|
11998
|
+
"yesterday",
|
|
11999
|
+
"this-week",
|
|
12000
|
+
"this-month"
|
|
12001
|
+
];
|
|
12002
|
+
var RESET_PERIODS = [
|
|
12003
|
+
"never",
|
|
12004
|
+
"daily",
|
|
12005
|
+
"weekly",
|
|
12006
|
+
"monthly"
|
|
12007
|
+
];
|
|
12008
|
+
5 * MINUTE_MS;
|
|
12009
|
+
24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
|
|
12010
|
+
7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
|
|
12011
|
+
var rollingWindowObject = object({
|
|
12012
|
+
kind: literal("rolling"),
|
|
12013
|
+
span: _enum(ROLLING_SPANS),
|
|
12014
|
+
customMinutes: number().int().min(5).optional()
|
|
12015
|
+
}).check((payload) => {
|
|
12016
|
+
if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
|
|
12017
|
+
code: "custom",
|
|
12018
|
+
message: "a custom range needs a duration in minutes",
|
|
12019
|
+
path: ["customMinutes"],
|
|
12020
|
+
input: payload.value
|
|
12021
|
+
});
|
|
12022
|
+
if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
|
|
12023
|
+
code: "custom",
|
|
12024
|
+
message: "only a custom range carries a duration",
|
|
12025
|
+
path: ["customMinutes"],
|
|
12026
|
+
input: payload.value
|
|
12027
|
+
});
|
|
12028
|
+
});
|
|
12029
|
+
var RollingWindowSchema = rollingWindowObject;
|
|
12030
|
+
var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
|
|
12031
|
+
kind: literal("calendar"),
|
|
12032
|
+
period: _enum(CALENDAR_PERIODS)
|
|
12033
|
+
})]);
|
|
12034
|
+
/**
|
|
11904
12035
|
* A composition source identity: `(addonId, stableId)`, never a numeric device
|
|
11905
12036
|
* id. Split out so helper schemas can name it without a cycle through
|
|
11906
12037
|
* `composition.ts` (D659).
|
|
@@ -11926,7 +12057,13 @@ var GROUP_TEMPLATE_IDS = [
|
|
|
11926
12057
|
"occupancy",
|
|
11927
12058
|
"binary",
|
|
11928
12059
|
"switch",
|
|
11929
|
-
"light"
|
|
12060
|
+
"light",
|
|
12061
|
+
"fan",
|
|
12062
|
+
"cover",
|
|
12063
|
+
"valve",
|
|
12064
|
+
"lock",
|
|
12065
|
+
"media-player",
|
|
12066
|
+
"climate"
|
|
11930
12067
|
];
|
|
11931
12068
|
var NUMERIC_AGGREGATES = [
|
|
11932
12069
|
"mean",
|
|
@@ -11956,14 +12093,73 @@ var HelperMemberSchema = object({
|
|
|
11956
12093
|
cap: string$2().min(1),
|
|
11957
12094
|
fieldPath: string$2().min(1)
|
|
11958
12095
|
});
|
|
11959
|
-
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
12096
|
+
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
12097
|
+
object({
|
|
12098
|
+
kind: literal("group"),
|
|
12099
|
+
templateId: GroupTemplateIdSchema,
|
|
12100
|
+
aggregate: AggregateReducerSchema,
|
|
12101
|
+
members: array(HelperMemberSchema).min(1).max(64),
|
|
12102
|
+
/** Actuator groups only. Readings leave this unset. */
|
|
12103
|
+
active: ActiveCriterionSchema.optional()
|
|
12104
|
+
}),
|
|
12105
|
+
object({
|
|
12106
|
+
kind: literal("appliance"),
|
|
12107
|
+
templateId: literal(APPLIANCE_TEMPLATE_ID),
|
|
12108
|
+
appliance: _enum(APPLIANCE_KINDS),
|
|
12109
|
+
thresholdWatts: number().positive(),
|
|
12110
|
+
startDwellMs: number().int().nonnegative(),
|
|
12111
|
+
stopDwellMs: number().int().nonnegative(),
|
|
12112
|
+
member: HelperMemberSchema
|
|
12113
|
+
}),
|
|
12114
|
+
object({
|
|
12115
|
+
kind: literal("statistic"),
|
|
12116
|
+
stat: _enum(STATISTIC_STATS),
|
|
12117
|
+
window: StatisticWindowSchema,
|
|
12118
|
+
source: HelperMemberSchema
|
|
12119
|
+
}),
|
|
12120
|
+
object({
|
|
12121
|
+
kind: literal("integral"),
|
|
12122
|
+
reset: _enum(RESET_PERIODS),
|
|
12123
|
+
output: _enum(["energy", "meter"]),
|
|
12124
|
+
source: HelperMemberSchema
|
|
12125
|
+
}),
|
|
12126
|
+
object({
|
|
12127
|
+
kind: literal("threshold"),
|
|
12128
|
+
source: HelperMemberSchema,
|
|
12129
|
+
high: number(),
|
|
12130
|
+
low: number(),
|
|
12131
|
+
onForMs: number().int().nonnegative(),
|
|
12132
|
+
offForMs: number().int().nonnegative()
|
|
12133
|
+
}),
|
|
12134
|
+
object({
|
|
12135
|
+
kind: literal("arithmetic"),
|
|
12136
|
+
op: _enum([
|
|
12137
|
+
"sum",
|
|
12138
|
+
"difference",
|
|
12139
|
+
"ratio"
|
|
12140
|
+
]),
|
|
12141
|
+
inputs: array(HelperMemberSchema).min(2).max(64)
|
|
12142
|
+
}),
|
|
12143
|
+
object({
|
|
12144
|
+
kind: literal("expression"),
|
|
12145
|
+
expr: string$2().min(1),
|
|
12146
|
+
inputs: array(object({
|
|
12147
|
+
name: string$2().min(1),
|
|
12148
|
+
member: HelperMemberSchema
|
|
12149
|
+
})).min(1).max(64),
|
|
12150
|
+
output: _enum(["number", "boolean"])
|
|
12151
|
+
}),
|
|
12152
|
+
object({
|
|
12153
|
+
kind: literal("derivative"),
|
|
12154
|
+
window: RollingWindowSchema,
|
|
12155
|
+
per: _enum([
|
|
12156
|
+
"second",
|
|
12157
|
+
"minute",
|
|
12158
|
+
"hour"
|
|
12159
|
+
]),
|
|
12160
|
+
source: HelperMemberSchema
|
|
12161
|
+
})
|
|
12162
|
+
]);
|
|
11967
12163
|
/** A composed device's stableId is this prefix plus the block id. */
|
|
11968
12164
|
var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
|
|
11969
12165
|
/** Longest snippet a `code` source may carry (slice 4). */
|
|
@@ -12024,6 +12220,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
12024
12220
|
"all"
|
|
12025
12221
|
]),
|
|
12026
12222
|
reads: array(CompositionFieldReadSchema).min(1).max(64),
|
|
12223
|
+
/**
|
|
12224
|
+
* Set when the reads are an enum state, not a yes/no: the listed states
|
|
12225
|
+
* count as active (a cover is `open`). A state outside the list is inactive;
|
|
12226
|
+
* a read that is not a state at all is left out. Only `any` / `all` use it.
|
|
12227
|
+
*/
|
|
12228
|
+
activeWhen: array(string$2().min(1)).min(1).optional(),
|
|
12027
12229
|
/** `value` is the reading; the others are the member counts. */
|
|
12028
12230
|
publish: _enum([
|
|
12029
12231
|
"value",
|
|
@@ -12037,6 +12239,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
12037
12239
|
"omit",
|
|
12038
12240
|
"offline"
|
|
12039
12241
|
])
|
|
12242
|
+
}),
|
|
12243
|
+
object({
|
|
12244
|
+
kind: literal("series"),
|
|
12245
|
+
read: CompositionFieldReadSchema,
|
|
12246
|
+
/** `value` is the statistic or the integral. The others are the gap counts. */
|
|
12247
|
+
publish: _enum([
|
|
12248
|
+
"value",
|
|
12249
|
+
"included",
|
|
12250
|
+
"excluded",
|
|
12251
|
+
"total"
|
|
12252
|
+
]),
|
|
12253
|
+
/**
|
|
12254
|
+
* What an unanswered `value` does. Counts ignore this. `offline` leaves the
|
|
12255
|
+
* device unseeded until history answers, so a restart cannot publish 0.
|
|
12256
|
+
*/
|
|
12257
|
+
empty: _enum([
|
|
12258
|
+
"null",
|
|
12259
|
+
"omit",
|
|
12260
|
+
"offline"
|
|
12261
|
+
])
|
|
12040
12262
|
})
|
|
12041
12263
|
]);
|
|
12042
12264
|
var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
|
|
@@ -15474,6 +15696,14 @@ var AutomationConditionLeafSchema = object({
|
|
|
15474
15696
|
array(union([string$2(), number()]))
|
|
15475
15697
|
])
|
|
15476
15698
|
});
|
|
15699
|
+
var AutomationConditionStationaryPresentSchema = object({
|
|
15700
|
+
kind: literal("stationary-present"),
|
|
15701
|
+
deviceId: number().int().nonnegative(),
|
|
15702
|
+
/** Class names; a macro class matches every raw class that maps to it. */
|
|
15703
|
+
classes: array(string$2().min(1)).min(1).optional(),
|
|
15704
|
+
/** The parked object must be in at least one of these zones. */
|
|
15705
|
+
zones: array(string$2().min(1)).min(1).optional()
|
|
15706
|
+
});
|
|
15477
15707
|
/**
|
|
15478
15708
|
* The expression leaf, declared as a plain object rather than an intersection
|
|
15479
15709
|
* with {@link ExpressionSourceSchema}: a discriminated union has to be able to
|
|
@@ -15507,7 +15737,8 @@ var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
|
|
|
15507
15737
|
child: AutomationConditionSchema
|
|
15508
15738
|
}),
|
|
15509
15739
|
AutomationConditionLeafSchema,
|
|
15510
|
-
AutomationConditionExpressionSchema
|
|
15740
|
+
AutomationConditionExpressionSchema,
|
|
15741
|
+
AutomationConditionStationaryPresentSchema
|
|
15511
15742
|
]));
|
|
15512
15743
|
/**
|
|
15513
15744
|
* What starts a run.
|
|
@@ -15548,7 +15779,34 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
15548
15779
|
kind: literal("schedule"),
|
|
15549
15780
|
cron: string$2().min(1).max(120)
|
|
15550
15781
|
}),
|
|
15551
|
-
object({ kind: literal("manual") })
|
|
15782
|
+
object({ kind: literal("manual") }),
|
|
15783
|
+
object({
|
|
15784
|
+
kind: literal("detection"),
|
|
15785
|
+
/** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
|
|
15786
|
+
* matches every raw class that maps to it; any other name matches
|
|
15787
|
+
* exactly. */
|
|
15788
|
+
classes: array(string$2().min(1)).min(1),
|
|
15789
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
15790
|
+
/** The track must have visited at least one of these zones. */
|
|
15791
|
+
zones: array(string$2().min(1)).min(1).optional(),
|
|
15792
|
+
/** Best-so-far confidence the track must reach. */
|
|
15793
|
+
minScore: number().min(0).max(1).optional()
|
|
15794
|
+
}),
|
|
15795
|
+
object({
|
|
15796
|
+
kind: literal("stationary"),
|
|
15797
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
15798
|
+
/** Class names; a macro class matches like the detection trigger. */
|
|
15799
|
+
classes: array(string$2().min(1)).min(1).optional(),
|
|
15800
|
+
/** The parked object must be in at least one of these zones. */
|
|
15801
|
+
zones: array(string$2().min(1)).min(1).optional(),
|
|
15802
|
+
/** `appeared`: it became parked. `departed`: the registry retired it.
|
|
15803
|
+
* The vocabulary of `PipelineAnalyticsStationaryChanged`. */
|
|
15804
|
+
phase: _enum(["appeared", "departed"])
|
|
15805
|
+
}),
|
|
15806
|
+
object({
|
|
15807
|
+
kind: literal("interval"),
|
|
15808
|
+
minutes: number().int().min(1).max(1440)
|
|
15809
|
+
})
|
|
15552
15810
|
]);
|
|
15553
15811
|
/**
|
|
15554
15812
|
* One action step.
|
|
@@ -15568,28 +15826,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
15568
15826
|
* is called. The runner still receives a number and is untouched; the
|
|
15569
15827
|
* resolution is the recipe's job, not the runner's.
|
|
15570
15828
|
*/
|
|
15829
|
+
var AutomationWaitStepSchema = object({
|
|
15830
|
+
kind: literal("wait"),
|
|
15831
|
+
seconds: number().min(0).max(300)
|
|
15832
|
+
});
|
|
15833
|
+
var AutomationCapStepSchema = object({
|
|
15834
|
+
kind: literal("cap"),
|
|
15835
|
+
/**
|
|
15836
|
+
* One device, several same-family devices (locks / climate family / …),
|
|
15837
|
+
* or `{ $var }` for the device that triggered the run. An array is
|
|
15838
|
+
* fan-out at codegen — each id gets the same cap.method call.
|
|
15839
|
+
*/
|
|
15840
|
+
deviceId: union([
|
|
15841
|
+
number().int(),
|
|
15842
|
+
array(number().int()).min(1),
|
|
15843
|
+
object({ $var: string$2().min(1) })
|
|
15844
|
+
]),
|
|
15845
|
+
cap: string$2().min(1),
|
|
15846
|
+
method: string$2().min(1),
|
|
15847
|
+
/** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
|
|
15848
|
+
* SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
|
|
15849
|
+
* the expression leaf. */
|
|
15850
|
+
args: record(string$2(), unknown()).optional()
|
|
15851
|
+
});
|
|
15852
|
+
/**
|
|
15853
|
+
* The steps an internal Action may contain: cap and wait, and nothing that can
|
|
15854
|
+
* call back into an Action — so an Action can never reach itself (no cycles).
|
|
15855
|
+
*/
|
|
15856
|
+
var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
|
|
15571
15857
|
var AutomationActionSchema = discriminatedUnion("kind", [
|
|
15858
|
+
AutomationWaitStepSchema,
|
|
15859
|
+
AutomationCapStepSchema,
|
|
15572
15860
|
object({
|
|
15573
|
-
kind: literal("
|
|
15574
|
-
|
|
15575
|
-
|
|
15576
|
-
|
|
15577
|
-
|
|
15578
|
-
|
|
15579
|
-
|
|
15580
|
-
|
|
15581
|
-
|
|
15582
|
-
*/
|
|
15583
|
-
deviceId: union([
|
|
15584
|
-
number().int(),
|
|
15585
|
-
array(number().int()).min(1),
|
|
15586
|
-
object({ $var: string$2().min(1) })
|
|
15587
|
-
]),
|
|
15588
|
-
cap: string$2().min(1),
|
|
15589
|
-
method: string$2().min(1),
|
|
15590
|
-
/** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
|
|
15591
|
-
* evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
|
|
15592
|
-
args: record(string$2(), unknown()).optional()
|
|
15861
|
+
kind: literal("action"),
|
|
15862
|
+
/** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
|
|
15863
|
+
actionId: string$2().min(1),
|
|
15864
|
+
/** Values for the action's declared variables; the others take their default. */
|
|
15865
|
+
variables: record(string$2(), union([
|
|
15866
|
+
string$2(),
|
|
15867
|
+
number(),
|
|
15868
|
+
boolean()
|
|
15869
|
+
])).optional()
|
|
15593
15870
|
}),
|
|
15594
15871
|
object({
|
|
15595
15872
|
kind: literal("code"),
|
|
@@ -15598,10 +15875,357 @@ var AutomationActionSchema = discriminatedUnion("kind", [
|
|
|
15598
15875
|
code: string$2().min(1).max(2e4)
|
|
15599
15876
|
})
|
|
15600
15877
|
]);
|
|
15878
|
+
/**
|
|
15879
|
+
* How a trigger that arrives while the automation is busy (or cooling down) is
|
|
15880
|
+
* handled. One of the automation's execution modes, never a property of its
|
|
15881
|
+
* actions. Absent means `single`.
|
|
15882
|
+
*
|
|
15883
|
+
* - `single` — drop the trigger while a run is in progress (traced + logged).
|
|
15884
|
+
* - `restart` — abort the run in progress (at its next step boundary), start fresh.
|
|
15885
|
+
* - `queued` — queue up to `max` triggers, drop the rest (traced).
|
|
15886
|
+
* - `parallel` — up to `max` runs at once, drop the rest (traced).
|
|
15887
|
+
* - `cooldown` — `single`, and no new run until `minutes` after the last run
|
|
15888
|
+
* STARTED; persisted across a restart. A manual trigger bypasses only this
|
|
15889
|
+
* wait, never the single/queued limits.
|
|
15890
|
+
*/
|
|
15891
|
+
var AutomationRunModeSchema = discriminatedUnion("mode", [
|
|
15892
|
+
object({ mode: literal("single") }),
|
|
15893
|
+
object({ mode: literal("restart") }),
|
|
15894
|
+
object({
|
|
15895
|
+
mode: literal("queued"),
|
|
15896
|
+
max: number().int().min(1).max(10)
|
|
15897
|
+
}),
|
|
15898
|
+
object({
|
|
15899
|
+
mode: literal("parallel"),
|
|
15900
|
+
max: number().int().min(1).max(10)
|
|
15901
|
+
}),
|
|
15902
|
+
object({
|
|
15903
|
+
mode: literal("cooldown"),
|
|
15904
|
+
minutes: number().int().min(1).max(1440)
|
|
15905
|
+
})
|
|
15906
|
+
]);
|
|
15601
15907
|
var AutomationRecipeSchema = object({
|
|
15602
15908
|
triggers: array(AutomationTriggerSchema),
|
|
15603
15909
|
conditions: AutomationConditionSchema.optional(),
|
|
15604
|
-
actions: array(AutomationActionSchema)
|
|
15910
|
+
actions: array(AutomationActionSchema),
|
|
15911
|
+
runMode: AutomationRunModeSchema.optional(),
|
|
15912
|
+
/** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
|
|
15913
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
15914
|
+
});
|
|
15915
|
+
new Set([
|
|
15916
|
+
{
|
|
15917
|
+
id: "person",
|
|
15918
|
+
name: "Person"
|
|
15919
|
+
},
|
|
15920
|
+
{
|
|
15921
|
+
id: "vehicle",
|
|
15922
|
+
name: "Vehicle"
|
|
15923
|
+
},
|
|
15924
|
+
{
|
|
15925
|
+
id: "animal",
|
|
15926
|
+
name: "Animal"
|
|
15927
|
+
},
|
|
15928
|
+
{
|
|
15929
|
+
id: "package",
|
|
15930
|
+
name: "Package"
|
|
15931
|
+
}
|
|
15932
|
+
].map((l) => l.id));
|
|
15933
|
+
var COCO_TO_MACRO = {
|
|
15934
|
+
mapping: {
|
|
15935
|
+
person: "person",
|
|
15936
|
+
bicycle: "vehicle",
|
|
15937
|
+
car: "vehicle",
|
|
15938
|
+
motorcycle: "vehicle",
|
|
15939
|
+
airplane: "vehicle",
|
|
15940
|
+
bus: "vehicle",
|
|
15941
|
+
train: "vehicle",
|
|
15942
|
+
truck: "vehicle",
|
|
15943
|
+
boat: "vehicle",
|
|
15944
|
+
bird: "animal",
|
|
15945
|
+
cat: "animal",
|
|
15946
|
+
dog: "animal",
|
|
15947
|
+
horse: "animal",
|
|
15948
|
+
sheep: "animal",
|
|
15949
|
+
cow: "animal",
|
|
15950
|
+
elephant: "animal",
|
|
15951
|
+
bear: "animal",
|
|
15952
|
+
zebra: "animal",
|
|
15953
|
+
giraffe: "animal",
|
|
15954
|
+
suitcase: "package",
|
|
15955
|
+
backpack: "package",
|
|
15956
|
+
handbag: "package"
|
|
15957
|
+
},
|
|
15958
|
+
preserveOriginal: false
|
|
15959
|
+
};
|
|
15960
|
+
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
15961
|
+
/**
|
|
15962
|
+
* Run traces — what an automation (or, later, an Action run) DID, node by node,
|
|
15963
|
+
* so the editor can paint every block green or red with its reason.
|
|
15964
|
+
*
|
|
15965
|
+
* A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
|
|
15966
|
+
* every condition node (containers and leaves, including the ones a
|
|
15967
|
+
* short-circuit never reached), the run-mode verdict and every action step —
|
|
15968
|
+
* each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
|
|
15969
|
+
* for an Action run). Skipped
|
|
15970
|
+
* evaluations are traced too, and identical consecutive skips collapse into a
|
|
15971
|
+
* repeat count so a trigger that fails every 5 minutes cannot flush real runs.
|
|
15972
|
+
*
|
|
15973
|
+
* {@link AutomationStepTraceSchema} is deliberately free of automation
|
|
15974
|
+
* vocabulary so an Action run records the same step shape.
|
|
15975
|
+
*/
|
|
15976
|
+
/** A value a trace may show: JSON primitives and short lists, nothing deeper. */
|
|
15977
|
+
var TraceValueSchema = union([
|
|
15978
|
+
string$2(),
|
|
15979
|
+
number(),
|
|
15980
|
+
boolean(),
|
|
15981
|
+
_null(),
|
|
15982
|
+
array(union([string$2(), number()]))
|
|
15983
|
+
]);
|
|
15984
|
+
var AutomationRunOutcomeSchema = _enum([
|
|
15985
|
+
"ran",
|
|
15986
|
+
"skipped-trigger",
|
|
15987
|
+
"skipped-conditions",
|
|
15988
|
+
"skipped-cooldown",
|
|
15989
|
+
"skipped-overlap",
|
|
15990
|
+
"failed",
|
|
15991
|
+
"aborted"
|
|
15992
|
+
]);
|
|
15993
|
+
/** `not-evaluated` = a short-circuit never reached the node. */
|
|
15994
|
+
var TraceNodeOutcomeSchema = _enum([
|
|
15995
|
+
"pass",
|
|
15996
|
+
"fail",
|
|
15997
|
+
"not-evaluated"
|
|
15998
|
+
]);
|
|
15999
|
+
var AutomationTriggerTraceSchema = object({
|
|
16000
|
+
kind: string$2().min(1),
|
|
16001
|
+
outcome: _enum(["pass", "fail"]),
|
|
16002
|
+
/** Why an event was rejected ("class person not in [dog]") or what fired. */
|
|
16003
|
+
reason: string$2(),
|
|
16004
|
+
/** deviceId, className, trackId, score … — only what the trigger exposes. */
|
|
16005
|
+
vars: record(string$2(), TraceValueSchema)
|
|
16006
|
+
});
|
|
16007
|
+
var AutomationConditionTraceNodeSchema = object({
|
|
16008
|
+
/** `conditions` for the root, `conditions.<i>` for its i-th child, … */
|
|
16009
|
+
path: string$2().min(1),
|
|
16010
|
+
kind: _enum([
|
|
16011
|
+
"all",
|
|
16012
|
+
"any",
|
|
16013
|
+
"not",
|
|
16014
|
+
"condition",
|
|
16015
|
+
"expression",
|
|
16016
|
+
"stationary-present"
|
|
16017
|
+
]),
|
|
16018
|
+
outcome: TraceNodeOutcomeSchema,
|
|
16019
|
+
reason: string$2(),
|
|
16020
|
+
deviceId: number().int().optional(),
|
|
16021
|
+
cap: string$2().optional(),
|
|
16022
|
+
fieldPath: string$2().optional(),
|
|
16023
|
+
operator: string$2().optional(),
|
|
16024
|
+
expected: TraceValueSchema.optional(),
|
|
16025
|
+
/** What the mirror held; `null` when it held nothing (unknown). */
|
|
16026
|
+
observed: TraceValueSchema.optional()
|
|
16027
|
+
});
|
|
16028
|
+
var AutomationRunVerdictSchema = object({
|
|
16029
|
+
mode: AutomationRunModeSchema,
|
|
16030
|
+
decision: _enum([
|
|
16031
|
+
"run",
|
|
16032
|
+
"queued",
|
|
16033
|
+
"restart",
|
|
16034
|
+
"drop",
|
|
16035
|
+
"cooldown",
|
|
16036
|
+
"bypass"
|
|
16037
|
+
]),
|
|
16038
|
+
reason: string$2()
|
|
16039
|
+
});
|
|
16040
|
+
/** One executed (or not-run) step — shared by automations and Action runs. */
|
|
16041
|
+
var AutomationStepTraceSchema = object({
|
|
16042
|
+
/** `actions.<i>` */
|
|
16043
|
+
path: string$2().min(1),
|
|
16044
|
+
kind: string$2().min(1),
|
|
16045
|
+
cap: string$2().optional(),
|
|
16046
|
+
method: string$2().optional(),
|
|
16047
|
+
deviceId: number().int().optional(),
|
|
16048
|
+
startedAt: number().optional(),
|
|
16049
|
+
endedAt: number().optional(),
|
|
16050
|
+
outcome: _enum([
|
|
16051
|
+
"ok",
|
|
16052
|
+
"error",
|
|
16053
|
+
"not-run"
|
|
16054
|
+
]),
|
|
16055
|
+
message: string$2().optional()
|
|
16056
|
+
});
|
|
16057
|
+
var AutomationRunTraceSchema = object({
|
|
16058
|
+
runId: string$2().min(1),
|
|
16059
|
+
startedAt: number(),
|
|
16060
|
+
endedAt: number(),
|
|
16061
|
+
trigger: AutomationTriggerTraceSchema,
|
|
16062
|
+
conditions: array(AutomationConditionTraceNodeSchema),
|
|
16063
|
+
verdict: AutomationRunVerdictSchema.optional(),
|
|
16064
|
+
steps: array(AutomationStepTraceSchema),
|
|
16065
|
+
outcome: AutomationRunOutcomeSchema,
|
|
16066
|
+
/** How many identical consecutive skips this trace stands for (>= 1). */
|
|
16067
|
+
repeat: number().int().min(1),
|
|
16068
|
+
/** When the latest of those repeats happened. */
|
|
16069
|
+
lastAt: number()
|
|
16070
|
+
});
|
|
16071
|
+
/**
|
|
16072
|
+
* What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
|
|
16073
|
+
* `action:<actionId>`): the cooldown stamp and the trace ring.
|
|
16074
|
+
*/
|
|
16075
|
+
var AutomationStateDocSchema = object({
|
|
16076
|
+
version: literal(1),
|
|
16077
|
+
lastRunStartedAt: number().nullable(),
|
|
16078
|
+
traces: array(AutomationRunTraceSchema)
|
|
16079
|
+
});
|
|
16080
|
+
/**
|
|
16081
|
+
* Actions — named, runnable things an operator (or an automation) can start,
|
|
16082
|
+
* declared as LIST ENTRIES, never as devices.
|
|
16083
|
+
*
|
|
16084
|
+
* An entry is either **internal** (a short list of cap/wait steps kept in the
|
|
16085
|
+
* hub's `actions` record kind) or declared by an addon through the
|
|
16086
|
+
* `action-source` collection (a Home Assistant script, a scene …). The one
|
|
16087
|
+
* surface the UI and automations talk to is the `actions` cap; it keeps no
|
|
16088
|
+
* catalog of its own (D3) — every `list` asks the providers live.
|
|
16089
|
+
*
|
|
16090
|
+
* This file is the data half: the entry schema, the variables an entry may
|
|
16091
|
+
* declare, and the pure resolution of the values a run was given. It holds no
|
|
16092
|
+
* runtime.
|
|
16093
|
+
*/
|
|
16094
|
+
/** What a variable's value is. `select` takes one of `options`. */
|
|
16095
|
+
var ActionVariableTypeSchema = _enum([
|
|
16096
|
+
"string",
|
|
16097
|
+
"number",
|
|
16098
|
+
"boolean",
|
|
16099
|
+
"select"
|
|
16100
|
+
]);
|
|
16101
|
+
var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
16102
|
+
var ActionVariableValueSchema = union([
|
|
16103
|
+
string$2(),
|
|
16104
|
+
number(),
|
|
16105
|
+
boolean()
|
|
16106
|
+
]);
|
|
16107
|
+
/**
|
|
16108
|
+
* One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
|
|
16109
|
+
* value) or `{{vars.name}}` (inside a string) in their args.
|
|
16110
|
+
*/
|
|
16111
|
+
var ActionVariableSchema = object({
|
|
16112
|
+
name: string$2().regex(ACTION_VARIABLE_NAME_RE),
|
|
16113
|
+
label: string$2().min(1).max(120).optional(),
|
|
16114
|
+
type: ActionVariableTypeSchema,
|
|
16115
|
+
required: boolean().optional(),
|
|
16116
|
+
default: ActionVariableValueSchema.optional(),
|
|
16117
|
+
/** The allowed values of a `select`. */
|
|
16118
|
+
options: array(string$2().min(1)).min(1).optional()
|
|
16119
|
+
}).superRefine((v, ctx) => {
|
|
16120
|
+
if (v.type === "select" && v.options === void 0) ctx.addIssue({
|
|
16121
|
+
code: "custom",
|
|
16122
|
+
message: "a select variable needs options",
|
|
16123
|
+
path: ["options"]
|
|
16124
|
+
});
|
|
16125
|
+
if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
|
|
16126
|
+
code: "custom",
|
|
16127
|
+
message: variableValueError(v, v.default) ?? "invalid default",
|
|
16128
|
+
path: ["default"]
|
|
16129
|
+
});
|
|
16130
|
+
});
|
|
16131
|
+
/** Why `value` is not a legal value for the variable, or null when it is. */
|
|
16132
|
+
function variableValueError(variable, value) {
|
|
16133
|
+
switch (variable.type) {
|
|
16134
|
+
case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
|
|
16135
|
+
case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
|
|
16136
|
+
case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
|
|
16137
|
+
case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
|
|
16138
|
+
}
|
|
16139
|
+
}
|
|
16140
|
+
var ActionSourceKindSchema = _enum(["internal", "addon"]);
|
|
16141
|
+
function isInternalActionId(actionId) {
|
|
16142
|
+
return actionId.startsWith("internal:") && actionId.length > 9;
|
|
16143
|
+
}
|
|
16144
|
+
/** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
|
|
16145
|
+
function addonIdOfActionId(actionId) {
|
|
16146
|
+
if (isInternalActionId(actionId)) return null;
|
|
16147
|
+
const at = actionId.indexOf(":");
|
|
16148
|
+
return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
|
|
16149
|
+
}
|
|
16150
|
+
var ActionStatusSchema = object({
|
|
16151
|
+
isRunning: boolean(),
|
|
16152
|
+
/** Ms epoch the latest run STARTED; null when it never ran. */
|
|
16153
|
+
lastRunAt: number().nullable(),
|
|
16154
|
+
/** The latest run's failure; null on success or when it never ran. */
|
|
16155
|
+
lastError: string$2().nullable()
|
|
16156
|
+
});
|
|
16157
|
+
var ActionEntrySchema = object({
|
|
16158
|
+
/** `internal:<uuid>` or `<addonId>:<localId>`. */
|
|
16159
|
+
id: string$2().min(3),
|
|
16160
|
+
name: string$2().min(1).max(120),
|
|
16161
|
+
/** The addon that declared it; `core-blocks` for an internal one. */
|
|
16162
|
+
addonId: string$2().min(1),
|
|
16163
|
+
source: ActionSourceKindSchema,
|
|
16164
|
+
/** An addon-declared entry is edited where it was declared. */
|
|
16165
|
+
readOnly: boolean(),
|
|
16166
|
+
/** Internal only: cap and wait steps. No action-in-action, so no cycles. */
|
|
16167
|
+
steps: array(ActionStepSchema).min(1).max(50).optional(),
|
|
16168
|
+
variables: array(ActionVariableSchema).max(20).optional(),
|
|
16169
|
+
status: ActionStatusSchema
|
|
16170
|
+
}).superRefine((entry, ctx) => {
|
|
16171
|
+
if (entry.source === "internal") {
|
|
16172
|
+
if (!isInternalActionId(entry.id)) ctx.addIssue({
|
|
16173
|
+
code: "custom",
|
|
16174
|
+
message: "an internal action id is internal:<uuid>",
|
|
16175
|
+
path: ["id"]
|
|
16176
|
+
});
|
|
16177
|
+
return;
|
|
16178
|
+
}
|
|
16179
|
+
if (entry.steps !== void 0) ctx.addIssue({
|
|
16180
|
+
code: "custom",
|
|
16181
|
+
message: "only an internal action carries steps",
|
|
16182
|
+
path: ["steps"]
|
|
16183
|
+
});
|
|
16184
|
+
if (!entry.readOnly) ctx.addIssue({
|
|
16185
|
+
code: "custom",
|
|
16186
|
+
message: "an addon action is read-only here",
|
|
16187
|
+
path: ["readOnly"]
|
|
16188
|
+
});
|
|
16189
|
+
if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
|
|
16190
|
+
code: "custom",
|
|
16191
|
+
message: "an addon action id is <addonId>:<localId>",
|
|
16192
|
+
path: ["id"]
|
|
16193
|
+
});
|
|
16194
|
+
});
|
|
16195
|
+
/** What an operator writes for an internal action. */
|
|
16196
|
+
var ActionInputSchema = object({
|
|
16197
|
+
name: string$2().min(1).max(120),
|
|
16198
|
+
steps: array(ActionStepSchema).min(1).max(50),
|
|
16199
|
+
variables: array(ActionVariableSchema).max(20).optional()
|
|
16200
|
+
});
|
|
16201
|
+
/** Who started a run. Absent = the operator pressed it. */
|
|
16202
|
+
var ActionRunOriginSchema = object({
|
|
16203
|
+
/** The automation block whose step started it. */
|
|
16204
|
+
blockId: string$2().min(1).optional() });
|
|
16205
|
+
/** `started` = the caller did not ask to wait. */
|
|
16206
|
+
var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
|
|
16207
|
+
var ActionRunResultSchema = object({
|
|
16208
|
+
runId: string$2().min(1),
|
|
16209
|
+
outcome: ActionRunOutcomeSchema,
|
|
16210
|
+
/** Why it did not run, or what failed. */
|
|
16211
|
+
message: string$2().optional()
|
|
16212
|
+
});
|
|
16213
|
+
_enum([
|
|
16214
|
+
"not-found",
|
|
16215
|
+
"read-only",
|
|
16216
|
+
"invalid-variables",
|
|
16217
|
+
"invalid-steps",
|
|
16218
|
+
"not-linked",
|
|
16219
|
+
"busy"
|
|
16220
|
+
]);
|
|
16221
|
+
object({
|
|
16222
|
+
actionId: string$2(),
|
|
16223
|
+
runId: string$2(),
|
|
16224
|
+
isRunning: boolean(),
|
|
16225
|
+
/** Set when the run ended. */
|
|
16226
|
+
outcome: AutomationRunOutcomeSchema.optional(),
|
|
16227
|
+
lastRunAt: number().nullable(),
|
|
16228
|
+
lastError: string$2().nullable()
|
|
15605
16229
|
});
|
|
15606
16230
|
/**
|
|
15607
16231
|
* `addon-pages` — system-scoped singleton aggregator cap. Public-facing
|
|
@@ -17196,7 +17820,22 @@ var BrokerProviderInfoSchema = object({
|
|
|
17196
17820
|
*/
|
|
17197
17821
|
interactiveConnectPath: string$2().min(1).optional(),
|
|
17198
17822
|
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
17199
|
-
interactiveDisconnectPath: string$2().min(1).optional()
|
|
17823
|
+
interactiveDisconnectPath: string$2().min(1).optional(),
|
|
17824
|
+
/**
|
|
17825
|
+
* The kind signs in through a server-held interactive session instead of
|
|
17826
|
+
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
17827
|
+
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
17828
|
+
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
17829
|
+
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
17830
|
+
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
17831
|
+
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
17832
|
+
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
17833
|
+
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
17834
|
+
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
17835
|
+
* offers it as the default path; the fields the session fills are
|
|
17836
|
+
* marked `filledBySignIn` in the kind's settings schema.
|
|
17837
|
+
*/
|
|
17838
|
+
signIn: object({ basePath: string$2().min(1) }).optional()
|
|
17200
17839
|
}))
|
|
17201
17840
|
});
|
|
17202
17841
|
var ListInputSchema = object({
|
|
@@ -18322,7 +18961,7 @@ var CompositionEventPlanSchema = object({
|
|
|
18322
18961
|
stateField: string$2(),
|
|
18323
18962
|
fieldKey: string$2()
|
|
18324
18963
|
});
|
|
18325
|
-
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.
|
|
18964
|
+
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
|
|
18326
18965
|
var CompositionProblemSchema = object({
|
|
18327
18966
|
code: _enum([
|
|
18328
18967
|
"reserved-variant",
|
|
@@ -18350,7 +18989,8 @@ var CompositionProblemSchema = object({
|
|
|
18350
18989
|
"native-capability-replace",
|
|
18351
18990
|
"mixed-quantity",
|
|
18352
18991
|
"helper-mismatch",
|
|
18353
|
-
"member-constraint"
|
|
18992
|
+
"member-constraint",
|
|
18993
|
+
"retention"
|
|
18354
18994
|
]),
|
|
18355
18995
|
path: string$2(),
|
|
18356
18996
|
message: string$2()
|
|
@@ -18584,13 +19224,17 @@ Object.freeze({
|
|
|
18584
19224
|
latchedAt: {
|
|
18585
19225
|
kind: "fixed",
|
|
18586
19226
|
result: ["number", "null"]
|
|
19227
|
+
},
|
|
19228
|
+
schmitt: {
|
|
19229
|
+
kind: "fixed",
|
|
19230
|
+
result: ["boolean", "null"]
|
|
18587
19231
|
}
|
|
18588
19232
|
});
|
|
18589
19233
|
/**
|
|
18590
19234
|
* One catalog row per base reading a group helper can build (D944 §3).
|
|
18591
19235
|
* Data, not a form default written into the block.
|
|
18592
19236
|
*/
|
|
18593
|
-
var NOW = {
|
|
19237
|
+
var NOW$2 = {
|
|
18594
19238
|
fieldPath: "lastFetchedAt",
|
|
18595
19239
|
expr: "now"
|
|
18596
19240
|
};
|
|
@@ -18604,6 +19248,8 @@ function numeric(input) {
|
|
|
18604
19248
|
family: "numeric",
|
|
18605
19249
|
deviceType: DeviceType.Sensor,
|
|
18606
19250
|
memberDeviceType: DeviceType.Sensor,
|
|
19251
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
19252
|
+
activeWhen: void 0,
|
|
18607
19253
|
commandCaps: [],
|
|
18608
19254
|
baseUnit: baseUnitOf(input.quantity),
|
|
18609
19255
|
aggregates: NUMERIC_AGGREGATES,
|
|
@@ -18619,6 +19265,8 @@ function binary(input) {
|
|
|
18619
19265
|
family: "binary",
|
|
18620
19266
|
deviceType: DeviceType.Sensor,
|
|
18621
19267
|
memberDeviceType: DeviceType.Sensor,
|
|
19268
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
19269
|
+
activeWhen: void 0,
|
|
18622
19270
|
commandCaps: [],
|
|
18623
19271
|
quantity: "none",
|
|
18624
19272
|
baseUnit: void 0,
|
|
@@ -18632,73 +19280,73 @@ var GROUP_TEMPLATES = [
|
|
|
18632
19280
|
summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
|
|
18633
19281
|
role: DeviceRole.TemperatureSensor,
|
|
18634
19282
|
outputCap: "temperature-sensor",
|
|
18635
|
-
outputField: "
|
|
19283
|
+
outputField: "temperature",
|
|
18636
19284
|
quantity: "temperature",
|
|
18637
19285
|
defaultAggregate: "mean",
|
|
18638
19286
|
empty: "null",
|
|
18639
|
-
companions: [NOW]
|
|
19287
|
+
companions: [NOW$2]
|
|
18640
19288
|
}),
|
|
18641
19289
|
numeric({
|
|
18642
19290
|
id: "humidity",
|
|
18643
19291
|
summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18644
19292
|
role: DeviceRole.HumiditySensor,
|
|
18645
19293
|
outputCap: "humidity-sensor",
|
|
18646
|
-
outputField: "
|
|
19294
|
+
outputField: "humidity",
|
|
18647
19295
|
quantity: "humidity",
|
|
18648
19296
|
defaultAggregate: "mean",
|
|
18649
19297
|
empty: "null",
|
|
18650
|
-
companions: [NOW]
|
|
19298
|
+
companions: [NOW$2]
|
|
18651
19299
|
}),
|
|
18652
19300
|
numeric({
|
|
18653
19301
|
id: "pressure",
|
|
18654
19302
|
summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
|
|
18655
19303
|
role: DeviceRole.PressureSensor,
|
|
18656
19304
|
outputCap: "pressure-sensor",
|
|
18657
|
-
outputField: "
|
|
19305
|
+
outputField: "pressure",
|
|
18658
19306
|
quantity: "pressure",
|
|
18659
19307
|
defaultAggregate: "mean",
|
|
18660
19308
|
empty: "null",
|
|
18661
|
-
companions: [NOW]
|
|
19309
|
+
companions: [NOW$2]
|
|
18662
19310
|
}),
|
|
18663
19311
|
numeric({
|
|
18664
19312
|
id: "illuminance",
|
|
18665
19313
|
summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
|
|
18666
19314
|
role: DeviceRole.AmbientLightSensor,
|
|
18667
19315
|
outputCap: "ambient-light-sensor",
|
|
18668
|
-
outputField: "
|
|
19316
|
+
outputField: "illuminance",
|
|
18669
19317
|
quantity: "illuminance",
|
|
18670
19318
|
defaultAggregate: "mean",
|
|
18671
19319
|
empty: "null",
|
|
18672
|
-
companions: [NOW]
|
|
19320
|
+
companions: [NOW$2]
|
|
18673
19321
|
}),
|
|
18674
19322
|
numeric({
|
|
18675
19323
|
id: "power",
|
|
18676
19324
|
summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
|
|
18677
19325
|
role: DeviceRole.PowerSensor,
|
|
18678
19326
|
outputCap: "power-meter",
|
|
18679
|
-
outputField: "
|
|
19327
|
+
outputField: "power",
|
|
18680
19328
|
quantity: "power",
|
|
18681
19329
|
defaultAggregate: "sum",
|
|
18682
19330
|
empty: "omit",
|
|
18683
|
-
companions: [NOW]
|
|
19331
|
+
companions: [NOW$2]
|
|
18684
19332
|
}),
|
|
18685
19333
|
numeric({
|
|
18686
19334
|
id: "energy",
|
|
18687
19335
|
summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
|
|
18688
19336
|
role: DeviceRole.EnergySensor,
|
|
18689
19337
|
outputCap: "power-meter",
|
|
18690
|
-
outputField: "
|
|
19338
|
+
outputField: "energy",
|
|
18691
19339
|
quantity: "energy",
|
|
18692
19340
|
defaultAggregate: "sum",
|
|
18693
19341
|
empty: "omit",
|
|
18694
|
-
companions: [NOW]
|
|
19342
|
+
companions: [NOW$2]
|
|
18695
19343
|
}),
|
|
18696
19344
|
numeric({
|
|
18697
19345
|
id: "battery",
|
|
18698
19346
|
summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18699
19347
|
role: DeviceRole.BatterySensor,
|
|
18700
19348
|
outputCap: "battery",
|
|
18701
|
-
outputField: "
|
|
19349
|
+
outputField: "level",
|
|
18702
19350
|
quantity: "percentage",
|
|
18703
19351
|
defaultAggregate: "mean",
|
|
18704
19352
|
empty: "null",
|
|
@@ -18726,7 +19374,7 @@ var GROUP_TEMPLATES = [
|
|
|
18726
19374
|
quantity: "device",
|
|
18727
19375
|
defaultAggregate: "mean",
|
|
18728
19376
|
empty: "null",
|
|
18729
|
-
companions: [NOW]
|
|
19377
|
+
companions: [NOW$2]
|
|
18730
19378
|
}),
|
|
18731
19379
|
binary({
|
|
18732
19380
|
id: "motion",
|
|
@@ -18820,6 +19468,11 @@ var GROUP_TEMPLATES = [
|
|
|
18820
19468
|
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.",
|
|
18821
19469
|
deviceType: DeviceType.Switch,
|
|
18822
19470
|
outputCap: "switch",
|
|
19471
|
+
outputField: "on",
|
|
19472
|
+
member: {
|
|
19473
|
+
cap: "switch",
|
|
19474
|
+
fieldPath: "on"
|
|
19475
|
+
},
|
|
18823
19476
|
commandCaps: ["switch"]
|
|
18824
19477
|
}),
|
|
18825
19478
|
actuator({
|
|
@@ -18827,34 +19480,136 @@ var GROUP_TEMPLATES = [
|
|
|
18827
19480
|
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.",
|
|
18828
19481
|
deviceType: DeviceType.Light,
|
|
18829
19482
|
outputCap: "switch",
|
|
19483
|
+
outputField: "on",
|
|
19484
|
+
member: {
|
|
19485
|
+
cap: "switch",
|
|
19486
|
+
fieldPath: "on"
|
|
19487
|
+
},
|
|
18830
19488
|
commandCaps: [
|
|
18831
19489
|
"switch",
|
|
18832
19490
|
"brightness",
|
|
18833
19491
|
"color"
|
|
18834
19492
|
]
|
|
19493
|
+
}),
|
|
19494
|
+
actuator({
|
|
19495
|
+
id: "fan",
|
|
19496
|
+
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.",
|
|
19497
|
+
deviceType: DeviceType.Fan,
|
|
19498
|
+
outputCap: "switch",
|
|
19499
|
+
outputField: "on",
|
|
19500
|
+
member: {
|
|
19501
|
+
cap: "switch",
|
|
19502
|
+
fieldPath: "on"
|
|
19503
|
+
},
|
|
19504
|
+
commandCaps: ["switch", "fan-control"]
|
|
19505
|
+
}),
|
|
19506
|
+
actuator({
|
|
19507
|
+
id: "cover",
|
|
19508
|
+
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.",
|
|
19509
|
+
deviceType: DeviceType.Cover,
|
|
19510
|
+
outputCap: "group-active",
|
|
19511
|
+
outputField: "active",
|
|
19512
|
+
member: {
|
|
19513
|
+
cap: "cover",
|
|
19514
|
+
fieldPath: "state"
|
|
19515
|
+
},
|
|
19516
|
+
activeWhen: [
|
|
19517
|
+
"open",
|
|
19518
|
+
"opening",
|
|
19519
|
+
"stopped"
|
|
19520
|
+
],
|
|
19521
|
+
commandCaps: ["cover"]
|
|
19522
|
+
}),
|
|
19523
|
+
actuator({
|
|
19524
|
+
id: "valve",
|
|
19525
|
+
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.",
|
|
19526
|
+
deviceType: DeviceType.Valve,
|
|
19527
|
+
outputCap: "group-active",
|
|
19528
|
+
outputField: "active",
|
|
19529
|
+
member: {
|
|
19530
|
+
cap: "valve",
|
|
19531
|
+
fieldPath: "state"
|
|
19532
|
+
},
|
|
19533
|
+
activeWhen: [
|
|
19534
|
+
"open",
|
|
19535
|
+
"opening",
|
|
19536
|
+
"stopped"
|
|
19537
|
+
],
|
|
19538
|
+
commandCaps: ["valve"]
|
|
19539
|
+
}),
|
|
19540
|
+
actuator({
|
|
19541
|
+
id: "lock",
|
|
19542
|
+
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.",
|
|
19543
|
+
deviceType: DeviceType.Lock,
|
|
19544
|
+
outputCap: "group-active",
|
|
19545
|
+
outputField: "active",
|
|
19546
|
+
member: {
|
|
19547
|
+
cap: "lock-control",
|
|
19548
|
+
fieldPath: "state"
|
|
19549
|
+
},
|
|
19550
|
+
activeWhen: ["locked"],
|
|
19551
|
+
commandCaps: ["lock-control"]
|
|
19552
|
+
}),
|
|
19553
|
+
actuator({
|
|
19554
|
+
id: "media-player",
|
|
19555
|
+
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.",
|
|
19556
|
+
deviceType: DeviceType.MediaPlayer,
|
|
19557
|
+
outputCap: "group-active",
|
|
19558
|
+
outputField: "active",
|
|
19559
|
+
member: {
|
|
19560
|
+
cap: "media-player",
|
|
19561
|
+
fieldPath: "state"
|
|
19562
|
+
},
|
|
19563
|
+
activeWhen: ["playing", "buffering"],
|
|
19564
|
+
commandCaps: ["media-player"]
|
|
19565
|
+
}),
|
|
19566
|
+
actuator({
|
|
19567
|
+
id: "climate",
|
|
19568
|
+
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.",
|
|
19569
|
+
deviceType: DeviceType.Thermostat,
|
|
19570
|
+
memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
|
|
19571
|
+
outputCap: "group-active",
|
|
19572
|
+
outputField: "active",
|
|
19573
|
+
member: {
|
|
19574
|
+
cap: "climate-control",
|
|
19575
|
+
fieldPath: "mode"
|
|
19576
|
+
},
|
|
19577
|
+
activeWhen: [
|
|
19578
|
+
"heat",
|
|
19579
|
+
"cool",
|
|
19580
|
+
"auto",
|
|
19581
|
+
"heat_cool",
|
|
19582
|
+
"fan_only",
|
|
19583
|
+
"dry"
|
|
19584
|
+
],
|
|
19585
|
+
commandCaps: ["climate-control"]
|
|
18835
19586
|
})
|
|
18836
19587
|
];
|
|
18837
19588
|
function actuator(input) {
|
|
18838
19589
|
return {
|
|
18839
|
-
|
|
19590
|
+
id: input.id,
|
|
19591
|
+
summary: input.summary,
|
|
19592
|
+
deviceType: input.deviceType,
|
|
19593
|
+
outputCap: input.outputCap,
|
|
19594
|
+
outputField: input.outputField,
|
|
19595
|
+
commandCaps: input.commandCaps,
|
|
18840
19596
|
family: "actuator",
|
|
18841
19597
|
role: void 0,
|
|
18842
19598
|
memberDeviceType: input.deviceType,
|
|
18843
|
-
|
|
19599
|
+
memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
|
|
19600
|
+
activeWhen: input.activeWhen,
|
|
18844
19601
|
quantity: "none",
|
|
18845
19602
|
baseUnit: void 0,
|
|
18846
19603
|
aggregates: BINARY_AGGREGATES,
|
|
18847
19604
|
defaultAggregate: "any",
|
|
18848
19605
|
empty: "offline",
|
|
18849
|
-
accept: [
|
|
18850
|
-
cap: "switch",
|
|
18851
|
-
fieldPath: "on"
|
|
18852
|
-
}],
|
|
19606
|
+
accept: [input.member],
|
|
18853
19607
|
companions: [CHANGED]
|
|
18854
19608
|
};
|
|
18855
19609
|
}
|
|
18856
19610
|
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
18857
19611
|
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
19612
|
+
DeviceType.Camera;
|
|
18858
19613
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
18859
19614
|
/**
|
|
18860
19615
|
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
@@ -19169,7 +19924,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
19169
19924
|
kind: "mutation",
|
|
19170
19925
|
auth: "admin",
|
|
19171
19926
|
access: "view"
|
|
19172
|
-
})
|
|
19927
|
+
}), method(object({
|
|
19928
|
+
blockId: string$2().min(1),
|
|
19929
|
+
trace: AutomationRunTraceSchema.optional(),
|
|
19930
|
+
runStartedAt: number().optional(),
|
|
19931
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
19932
|
+
}), _void(), {
|
|
19933
|
+
kind: "mutation",
|
|
19934
|
+
auth: "admin"
|
|
19935
|
+
}), method(object({ blockId: string$2().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string$2().min(1) }), object({
|
|
19936
|
+
traces: array(AutomationRunTraceSchema),
|
|
19937
|
+
lastError: string$2().nullable()
|
|
19938
|
+
}), { auth: "admin" });
|
|
19173
19939
|
/**
|
|
19174
19940
|
* `custom-model-registry` — collection cap exposing operator-registered
|
|
19175
19941
|
* custom detection models. Each provider (today: `addon-model-studio`)
|
|
@@ -44078,7 +44844,7 @@ var NetworkAccessStatusSchema = object({
|
|
|
44078
44844
|
});
|
|
44079
44845
|
/**
|
|
44080
44846
|
* Optional, richer endpoint shape returned by providers that expose
|
|
44081
|
-
* MORE than one ingress concurrently (Tailscale
|
|
44847
|
+
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
44082
44848
|
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
44083
44849
|
* the originating provider config (mode + sourcePort) so the
|
|
44084
44850
|
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
@@ -44471,51 +45237,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
44471
45237
|
kind: "mutation",
|
|
44472
45238
|
auth: "admin"
|
|
44473
45239
|
});
|
|
44474
|
-
new Set([
|
|
44475
|
-
{
|
|
44476
|
-
id: "person",
|
|
44477
|
-
name: "Person"
|
|
44478
|
-
},
|
|
44479
|
-
{
|
|
44480
|
-
id: "vehicle",
|
|
44481
|
-
name: "Vehicle"
|
|
44482
|
-
},
|
|
44483
|
-
{
|
|
44484
|
-
id: "animal",
|
|
44485
|
-
name: "Animal"
|
|
44486
|
-
},
|
|
44487
|
-
{
|
|
44488
|
-
id: "package",
|
|
44489
|
-
name: "Package"
|
|
44490
|
-
}
|
|
44491
|
-
].map((l) => l.id));
|
|
44492
|
-
var COCO_TO_MACRO = {
|
|
44493
|
-
mapping: {
|
|
44494
|
-
person: "person",
|
|
44495
|
-
bicycle: "vehicle",
|
|
44496
|
-
car: "vehicle",
|
|
44497
|
-
motorcycle: "vehicle",
|
|
44498
|
-
airplane: "vehicle",
|
|
44499
|
-
bus: "vehicle",
|
|
44500
|
-
train: "vehicle",
|
|
44501
|
-
truck: "vehicle",
|
|
44502
|
-
boat: "vehicle",
|
|
44503
|
-
bird: "animal",
|
|
44504
|
-
cat: "animal",
|
|
44505
|
-
dog: "animal",
|
|
44506
|
-
horse: "animal",
|
|
44507
|
-
sheep: "animal",
|
|
44508
|
-
cow: "animal",
|
|
44509
|
-
elephant: "animal",
|
|
44510
|
-
bear: "animal",
|
|
44511
|
-
zebra: "animal",
|
|
44512
|
-
giraffe: "animal",
|
|
44513
|
-
suitcase: "package",
|
|
44514
|
-
backpack: "package",
|
|
44515
|
-
handbag: "package"
|
|
44516
|
-
},
|
|
44517
|
-
preserveOriginal: false
|
|
44518
|
-
};
|
|
44519
45240
|
var AUDIO_MACRO_LABELS = [
|
|
44520
45241
|
{
|
|
44521
45242
|
id: "speech",
|
|
@@ -46765,7 +47486,20 @@ var NcRuleInputSchema = object({
|
|
|
46765
47486
|
* save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
|
|
46766
47487
|
* path does not run Zod defaults.
|
|
46767
47488
|
*/
|
|
46768
|
-
groupingSec: number().int().min(5).max(60).optional()
|
|
47489
|
+
groupingSec: number().int().min(5).max(60).optional(),
|
|
47490
|
+
/**
|
|
47491
|
+
* ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
|
|
47492
|
+
* carries "Correct" / "Wrong" buttons (and, where the channel has room, a
|
|
47493
|
+
* "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
|
|
47494
|
+
* same mark the Library writes, and records whose notification it was and
|
|
47495
|
+
* when. A channel with no buttons gets the same two choices as links.
|
|
47496
|
+
*
|
|
47497
|
+
* `.optional()`, never `.default()` (a Zod default does not run on the addon
|
|
47498
|
+
* cap path): absent and `false` both mean off, which is what every rule
|
|
47499
|
+
* authored before this field meant. An editor turns it off by writing
|
|
47500
|
+
* `false`, so it needs no `clear` key.
|
|
47501
|
+
*/
|
|
47502
|
+
askVerdict: boolean().optional()
|
|
46769
47503
|
});
|
|
46770
47504
|
/**
|
|
46771
47505
|
* Partial patch for `updateRule` — any subset of the input fields, plus the
|
|
@@ -57069,9 +57803,9 @@ method(object({ nodeId: string$2().optional() }).optional(), array(AddonListItem
|
|
|
57069
57803
|
*/
|
|
57070
57804
|
var AirQualitySensorStatusSchema = object({
|
|
57071
57805
|
/** Carbon dioxide concentration in ppm. */
|
|
57072
|
-
|
|
57806
|
+
co2: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
57073
57807
|
/** Total volatile organic compounds in ppb. */
|
|
57074
|
-
|
|
57808
|
+
voc: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
57075
57809
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
57076
57810
|
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
57077
57811
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
@@ -57104,12 +57838,12 @@ var airQualitySensorCapability = {
|
|
|
57104
57838
|
* History (D850): recorded series.
|
|
57105
57839
|
*/
|
|
57106
57840
|
history: { fields: {
|
|
57107
|
-
|
|
57841
|
+
co2: {
|
|
57108
57842
|
kind: "gauge",
|
|
57109
57843
|
unit: "ppm",
|
|
57110
57844
|
deadband: 5
|
|
57111
57845
|
},
|
|
57112
|
-
|
|
57846
|
+
voc: {
|
|
57113
57847
|
kind: "gauge",
|
|
57114
57848
|
unit: "ppb",
|
|
57115
57849
|
deadband: 5
|
|
@@ -57140,7 +57874,7 @@ var airQualitySensorCapability = {
|
|
|
57140
57874
|
*/
|
|
57141
57875
|
var AmbientLightSensorStatusSchema = object({
|
|
57142
57876
|
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
57143
|
-
|
|
57877
|
+
illuminance: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
57144
57878
|
precision: 0,
|
|
57145
57879
|
label: "Illuminance"
|
|
57146
57880
|
})),
|
|
@@ -57169,7 +57903,7 @@ var ambientLightSensorCapability = {
|
|
|
57169
57903
|
/**
|
|
57170
57904
|
* History (D850): recorded series. Wide range, so a RELATIVE deadband (10 %).
|
|
57171
57905
|
*/
|
|
57172
|
-
history: { fields: {
|
|
57906
|
+
history: { fields: { illuminance: {
|
|
57173
57907
|
kind: "gauge",
|
|
57174
57908
|
unit: "lx",
|
|
57175
57909
|
deadbandPct: 10,
|
|
@@ -57308,6 +58042,84 @@ var audioMetricsCapability = {
|
|
|
57308
58042
|
*/
|
|
57309
58043
|
history: "none"
|
|
57310
58044
|
};
|
|
58045
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
58046
|
+
actionId: string$2().min(1),
|
|
58047
|
+
variables: record(string$2(), ActionVariableValueSchema).optional()
|
|
58048
|
+
}), _void(), {
|
|
58049
|
+
kind: "mutation",
|
|
58050
|
+
auth: "admin"
|
|
58051
|
+
}), method(object({ actionId: string$2().min(1) }), _void(), {
|
|
58052
|
+
kind: "mutation",
|
|
58053
|
+
auth: "admin"
|
|
58054
|
+
});
|
|
58055
|
+
/**
|
|
58056
|
+
* actions — the ONE surface for runnable actions, for the UI and for
|
|
58057
|
+
* automations. Provided by the core-blocks builtin on the hub.
|
|
58058
|
+
*
|
|
58059
|
+
* An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
|
|
58060
|
+
* steps, kept in the hub's `actions` record kind) or declared by an addon over
|
|
58061
|
+
* `action-source`. `list` asks every `action-source` provider LIVE and overlays
|
|
58062
|
+
* the run status from the hub's `runs` ledger; there is no catalog here to
|
|
58063
|
+
* drift (D3).
|
|
58064
|
+
*
|
|
58065
|
+
* **Permissions follow the camera, as linked devices do.** An action is linked
|
|
58066
|
+
* to cameras by the operator (`setLinks`, D356: nothing links itself). A
|
|
58067
|
+
* non-admin may `get` / `run` an action only through a camera they may see that
|
|
58068
|
+
* links it: they name the camera (`deviceId`, a top-level field so the scope
|
|
58069
|
+
* gate sees it) and the provider refuses an action that camera does not link.
|
|
58070
|
+
* An admin, and an in-process caller (an automation block), may run any action.
|
|
58071
|
+
* Every refusal is typed (`[not-linked] …`) and logged.
|
|
58072
|
+
*
|
|
58073
|
+
* A second `run` of an action that is already running is REFUSED — traced and
|
|
58074
|
+
* logged — never queued.
|
|
58075
|
+
*/
|
|
58076
|
+
var ActionIdInput = object({ actionId: string$2().min(1) });
|
|
58077
|
+
var ActionLinksSchema = object({
|
|
58078
|
+
deviceId: number().int().nonnegative(),
|
|
58079
|
+
actionIds: array(string$2().min(1)).max(100)
|
|
58080
|
+
});
|
|
58081
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
58082
|
+
actionId: string$2().min(1),
|
|
58083
|
+
deviceId: number().int().nonnegative().optional()
|
|
58084
|
+
}), object({ action: ActionEntrySchema.nullable() }), {
|
|
58085
|
+
role: "viewer",
|
|
58086
|
+
auth: "protected",
|
|
58087
|
+
kind: "query",
|
|
58088
|
+
caller: "required"
|
|
58089
|
+
}), method(object({
|
|
58090
|
+
actionId: string$2().min(1),
|
|
58091
|
+
variables: record(string$2(), ActionVariableValueSchema).optional(),
|
|
58092
|
+
wait: boolean().optional(),
|
|
58093
|
+
origin: ActionRunOriginSchema.optional(),
|
|
58094
|
+
deviceId: number().int().nonnegative().optional()
|
|
58095
|
+
}), ActionRunResultSchema, {
|
|
58096
|
+
role: "viewer",
|
|
58097
|
+
auth: "protected",
|
|
58098
|
+
kind: "mutation",
|
|
58099
|
+
caller: "required"
|
|
58100
|
+
}), method(ActionIdInput, _void(), {
|
|
58101
|
+
kind: "mutation",
|
|
58102
|
+
auth: "admin"
|
|
58103
|
+
}), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
|
|
58104
|
+
kind: "mutation",
|
|
58105
|
+
auth: "admin"
|
|
58106
|
+
}), method(object({
|
|
58107
|
+
actionId: string$2().min(1),
|
|
58108
|
+
action: ActionInputSchema.partial()
|
|
58109
|
+
}), object({ action: ActionEntrySchema }), {
|
|
58110
|
+
kind: "mutation",
|
|
58111
|
+
auth: "admin"
|
|
58112
|
+
}), method(ActionIdInput, _void(), {
|
|
58113
|
+
kind: "mutation",
|
|
58114
|
+
auth: "admin"
|
|
58115
|
+
}), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
|
|
58116
|
+
role: "viewer",
|
|
58117
|
+
auth: "protected",
|
|
58118
|
+
kind: "query"
|
|
58119
|
+
}), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
|
|
58120
|
+
kind: "mutation",
|
|
58121
|
+
auth: "admin"
|
|
58122
|
+
});
|
|
57311
58123
|
/**
|
|
57312
58124
|
* Automation-control cap. Models HA `automation.*` entities on
|
|
57313
58125
|
* `DeviceType.Automation`. An automation is a trigger+condition+
|
|
@@ -57404,7 +58216,7 @@ var BatteryStatusSchema = object({
|
|
|
57404
58216
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
57405
58217
|
* declines to band a non-finite reading, which is the correct shape.
|
|
57406
58218
|
*/
|
|
57407
|
-
|
|
58219
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
57408
58220
|
/**
|
|
57409
58221
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
57410
58222
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -57414,7 +58226,7 @@ var BatteryStatusSchema = object({
|
|
|
57414
58226
|
* `'unknown'` is the absence of an answer: the provider has not read the
|
|
57415
58227
|
* power fields yet, or the firmware reported neither. Before it existed,
|
|
57416
58228
|
* two unreadable fields produced `'none'`, and a camera nobody had reached
|
|
57417
|
-
* was drawn "on battery alone" — the same D315 defect `
|
|
58229
|
+
* was drawn "on battery alone" — the same D315 defect `level` closed
|
|
57418
58230
|
* by going nullable. Consumers SKIP it (no charger state published, no
|
|
57419
58231
|
* glyph, no HomeKit ChargingState) rather than coerce it either way.
|
|
57420
58232
|
*/
|
|
@@ -57438,7 +58250,7 @@ var BatteryStatusSchema = object({
|
|
|
57438
58250
|
* that only report charge. A second reading on this cap, not a second
|
|
57439
58251
|
* `numeric-sensor` on the same device.
|
|
57440
58252
|
*/
|
|
57441
|
-
|
|
58253
|
+
voltage: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
57442
58254
|
/**
|
|
57443
58255
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
57444
58256
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -57472,7 +58284,7 @@ var BatteryStatusSchema = object({
|
|
|
57472
58284
|
/**
|
|
57473
58285
|
* True when the source is a BINARY low-battery indicator (HA
|
|
57474
58286
|
* `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
|
|
57475
|
-
* charge level — `
|
|
58287
|
+
* charge level — `level` is then a coarse stand-in (100 = normal,
|
|
57476
58288
|
* sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
|
|
57477
58289
|
* misleading exact percentage. Absent/false → genuine 0–100 % reading.
|
|
57478
58290
|
*/
|
|
@@ -57485,21 +58297,22 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
57485
58297
|
DeviceType.Switch,
|
|
57486
58298
|
DeviceType.PetFeeder,
|
|
57487
58299
|
DeviceType.Container,
|
|
57488
|
-
DeviceType.Thermostat
|
|
58300
|
+
DeviceType.Thermostat,
|
|
58301
|
+
DeviceType.EventEmitter
|
|
57489
58302
|
];
|
|
57490
58303
|
/** Generic Customize target for the percentage reading (D936). */
|
|
57491
58304
|
var batteryCustomize = {
|
|
57492
58305
|
version: 1,
|
|
57493
58306
|
targets: [{
|
|
57494
|
-
id: "
|
|
58307
|
+
id: "level",
|
|
57495
58308
|
cardinality: "scalar",
|
|
57496
58309
|
vocabulary: {
|
|
57497
58310
|
cap: "battery",
|
|
57498
|
-
stateField: "
|
|
58311
|
+
stateField: "level"
|
|
57499
58312
|
},
|
|
57500
58313
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
57501
58314
|
apply: "add",
|
|
57502
|
-
path: "
|
|
58315
|
+
path: "level",
|
|
57503
58316
|
sources: [{
|
|
57504
58317
|
kind: "field",
|
|
57505
58318
|
valueKinds: ["number", "string"],
|
|
@@ -57593,7 +58406,7 @@ onStatusChanged: { data: object({
|
|
|
57593
58406
|
schema: BatteryStatusSchema,
|
|
57594
58407
|
kind: "push",
|
|
57595
58408
|
empty: {
|
|
57596
|
-
|
|
58409
|
+
level: null,
|
|
57597
58410
|
charging: "unknown",
|
|
57598
58411
|
sleeping: false,
|
|
57599
58412
|
lastUpdated: 0
|
|
@@ -57620,11 +58433,11 @@ onStatusChanged: { data: object({
|
|
|
57620
58433
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
57621
58434
|
*/
|
|
57622
58435
|
history: { fields: {
|
|
57623
|
-
|
|
58436
|
+
level: {
|
|
57624
58437
|
kind: "gauge",
|
|
57625
58438
|
unit: "%"
|
|
57626
58439
|
},
|
|
57627
|
-
|
|
58440
|
+
voltage: {
|
|
57628
58441
|
kind: "gauge",
|
|
57629
58442
|
unit: "V"
|
|
57630
58443
|
}
|
|
@@ -57684,14 +58497,14 @@ var binaryCapability = {
|
|
|
57684
58497
|
* controls to this single-method surface.
|
|
57685
58498
|
*
|
|
57686
58499
|
* The cap is intentionally minimal: a single `setBrightness({deviceId,
|
|
57687
|
-
*
|
|
58500
|
+
* level})` mutation plus the auto-injected `getStatus`. Drivers
|
|
57688
58501
|
* that expose richer controls (color temperature, scenes, schedules)
|
|
57689
58502
|
* should surface those via the device's `getSettingsUISchema()`
|
|
57690
58503
|
* instead of bloating this cap.
|
|
57691
58504
|
*/
|
|
57692
58505
|
var BrightnessStatusSchema = object({
|
|
57693
58506
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
57694
|
-
|
|
58507
|
+
level: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
57695
58508
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
57696
58509
|
lastChangedAt: number()
|
|
57697
58510
|
});
|
|
@@ -57703,7 +58516,7 @@ var brightnessCapability = {
|
|
|
57703
58516
|
deviceTypes: [DeviceType.Light],
|
|
57704
58517
|
methods: { setBrightness: method(object({
|
|
57705
58518
|
deviceId: number().int().nonnegative(),
|
|
57706
|
-
|
|
58519
|
+
level: number().min(0).max(100)
|
|
57707
58520
|
}), _void(), {
|
|
57708
58521
|
kind: "mutation",
|
|
57709
58522
|
auth: "protected"
|
|
@@ -57716,7 +58529,7 @@ var brightnessCapability = {
|
|
|
57716
58529
|
*/
|
|
57717
58530
|
onBrightnessChanged: { data: object({
|
|
57718
58531
|
deviceId: number(),
|
|
57719
|
-
|
|
58532
|
+
level: number().min(0).max(100),
|
|
57720
58533
|
lastChangedAt: number()
|
|
57721
58534
|
}) } },
|
|
57722
58535
|
status: {
|
|
@@ -57739,7 +58552,7 @@ onBrightnessChanged: { data: object({
|
|
|
57739
58552
|
/**
|
|
57740
58553
|
* History (D850): recorded series. Actuator tier: level keeps every change.
|
|
57741
58554
|
*/
|
|
57742
|
-
history: { fields: {
|
|
58555
|
+
history: { fields: { level: {
|
|
57743
58556
|
kind: "gauge",
|
|
57744
58557
|
unit: "%",
|
|
57745
58558
|
minIntervalMs: 0
|
|
@@ -58353,11 +59166,11 @@ var ColorInputSchema = discriminatedUnion("mode", [
|
|
|
58353
59166
|
}),
|
|
58354
59167
|
object({
|
|
58355
59168
|
mode: literal("mired"),
|
|
58356
|
-
|
|
59169
|
+
colorTemperature: number().int().min(50).max(1e3)
|
|
58357
59170
|
})
|
|
58358
59171
|
]);
|
|
58359
59172
|
var ColorStatusSchema = object({
|
|
58360
|
-
/** Active color mode — which of `rgb` / `hsv` / `
|
|
59173
|
+
/** Active color mode — which of `rgb` / `hsv` / `colorTemperature` reflects
|
|
58361
59174
|
* the bulb's current state. */
|
|
58362
59175
|
mode: _enum([
|
|
58363
59176
|
"rgb",
|
|
@@ -58369,7 +59182,7 @@ var ColorStatusSchema = object({
|
|
|
58369
59182
|
/** Populated when `mode === 'hsv'`. */
|
|
58370
59183
|
hsv: HsvTripletSchema.optional(),
|
|
58371
59184
|
/** Populated when `mode === 'mired'`. */
|
|
58372
|
-
|
|
59185
|
+
colorTemperature: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
58373
59186
|
/** Ms epoch of the last operator-driven change. */
|
|
58374
59187
|
lastChangedAt: number()
|
|
58375
59188
|
});
|
|
@@ -58401,7 +59214,7 @@ onColorChanged: { data: object({
|
|
|
58401
59214
|
]),
|
|
58402
59215
|
rgb: RgbTripletSchema.optional(),
|
|
58403
59216
|
hsv: HsvTripletSchema.optional(),
|
|
58404
|
-
|
|
59217
|
+
colorTemperature: number().int().optional(),
|
|
58405
59218
|
lastChangedAt: number()
|
|
58406
59219
|
}) } },
|
|
58407
59220
|
status: {
|
|
@@ -59278,11 +60091,11 @@ var DishwasherStatusSchema = object({
|
|
|
59278
60091
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
59279
60092
|
availablePrograms: array(string$2()),
|
|
59280
60093
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
59281
|
-
|
|
60094
|
+
remaining: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
59282
60095
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
59283
|
-
|
|
60096
|
+
delayedStart: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
59284
60097
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
59285
|
-
|
|
60098
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
59286
60099
|
/** True when the door is reported open; null when the door state is not known. */
|
|
59287
60100
|
doorOpen: boolean().nullable(),
|
|
59288
60101
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -59501,14 +60314,14 @@ var dishwasherCapability = {
|
|
|
59501
60314
|
runtimeState: DishwasherStatusSchema,
|
|
59502
60315
|
durability: "session",
|
|
59503
60316
|
history: { fields: {
|
|
59504
|
-
|
|
60317
|
+
progress: {
|
|
59505
60318
|
kind: "gauge",
|
|
59506
60319
|
unit: "%",
|
|
59507
60320
|
deadband: 2
|
|
59508
60321
|
},
|
|
59509
60322
|
state: { kind: "enum" },
|
|
59510
60323
|
operationState: { kind: "enum" },
|
|
59511
|
-
|
|
60324
|
+
remaining: {
|
|
59512
60325
|
kind: "gauge",
|
|
59513
60326
|
unit: "s",
|
|
59514
60327
|
minIntervalMs: 6e4
|
|
@@ -60099,7 +60912,7 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
60099
60912
|
var FanControlStatusSchema = object({
|
|
60100
60913
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
60101
60914
|
* speed surface (single-speed fan). */
|
|
60102
|
-
|
|
60915
|
+
speed: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60103
60916
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
60104
60917
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
60105
60918
|
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
@@ -60126,7 +60939,7 @@ var fanControlCapability = {
|
|
|
60126
60939
|
methods: {
|
|
60127
60940
|
setPercentage: method(object({
|
|
60128
60941
|
deviceId: number().int().nonnegative(),
|
|
60129
|
-
|
|
60942
|
+
speed: number().min(0).max(100)
|
|
60130
60943
|
}), _void(), {
|
|
60131
60944
|
kind: "mutation",
|
|
60132
60945
|
auth: "protected"
|
|
@@ -60159,7 +60972,7 @@ var fanControlCapability = {
|
|
|
60159
60972
|
},
|
|
60160
60973
|
/**
|
|
60161
60974
|
* Runtime-state slice — mirrored by the kernel. UI fan speed
|
|
60162
|
-
* sliders read `
|
|
60975
|
+
* sliders read `speed` for live updates.
|
|
60163
60976
|
*/
|
|
60164
60977
|
runtimeState: FanControlStatusSchema,
|
|
60165
60978
|
/**
|
|
@@ -60173,7 +60986,7 @@ var fanControlCapability = {
|
|
|
60173
60986
|
* History (D850): recorded series. Actuator tier: speed keeps every change.
|
|
60174
60987
|
*/
|
|
60175
60988
|
history: { fields: {
|
|
60176
|
-
|
|
60989
|
+
speed: {
|
|
60177
60990
|
kind: "gauge",
|
|
60178
60991
|
unit: "%",
|
|
60179
60992
|
minIntervalMs: 0
|
|
@@ -60362,7 +61175,17 @@ var groupCommandsCapability = {
|
|
|
60362
61175
|
scope: "device",
|
|
60363
61176
|
deviceNative: true,
|
|
60364
61177
|
mode: "singleton",
|
|
60365
|
-
deviceTypes: [
|
|
61178
|
+
deviceTypes: [
|
|
61179
|
+
DeviceType.Switch,
|
|
61180
|
+
DeviceType.Light,
|
|
61181
|
+
DeviceType.Fan,
|
|
61182
|
+
DeviceType.Cover,
|
|
61183
|
+
DeviceType.Valve,
|
|
61184
|
+
DeviceType.Lock,
|
|
61185
|
+
DeviceType.MediaPlayer,
|
|
61186
|
+
DeviceType.Thermostat,
|
|
61187
|
+
DeviceType.Climate
|
|
61188
|
+
],
|
|
60366
61189
|
methods: {},
|
|
60367
61190
|
status: {
|
|
60368
61191
|
schema: GroupCommandsSchema,
|
|
@@ -60380,6 +61203,49 @@ var groupCommandsCapability = {
|
|
|
60380
61203
|
history: "none"
|
|
60381
61204
|
};
|
|
60382
61205
|
/**
|
|
61206
|
+
* Whether a helper actuator group counts as active (D944): at least one
|
|
61207
|
+
* included member is, or every included member is. What "active" means is the
|
|
61208
|
+
* type's: a cover is open, a lock is locked, a media player is playing, a
|
|
61209
|
+
* climate device is not off. A group of lights, switches and fans reports
|
|
61210
|
+
* `switch.on` instead; this cap is for the types whose own state is not a
|
|
61211
|
+
* yes/no. Unknown members are left out (`group-status` counts them) and an
|
|
61212
|
+
* empty included set takes the device offline, never `active: false`.
|
|
61213
|
+
*/
|
|
61214
|
+
var GroupActiveSchema = object({
|
|
61215
|
+
active: boolean().meta({ label: "Active" }),
|
|
61216
|
+
/** Ms epoch when the slice was last updated. */
|
|
61217
|
+
lastChangedAt: number()
|
|
61218
|
+
});
|
|
61219
|
+
var groupActiveCapability = {
|
|
61220
|
+
name: "group-active",
|
|
61221
|
+
scope: "device",
|
|
61222
|
+
deviceNative: true,
|
|
61223
|
+
mode: "singleton",
|
|
61224
|
+
deviceTypes: [
|
|
61225
|
+
DeviceType.Cover,
|
|
61226
|
+
DeviceType.Valve,
|
|
61227
|
+
DeviceType.Lock,
|
|
61228
|
+
DeviceType.MediaPlayer,
|
|
61229
|
+
DeviceType.Climate,
|
|
61230
|
+
DeviceType.Thermostat
|
|
61231
|
+
],
|
|
61232
|
+
methods: {},
|
|
61233
|
+
status: {
|
|
61234
|
+
schema: GroupActiveSchema,
|
|
61235
|
+
kind: "push"
|
|
61236
|
+
},
|
|
61237
|
+
runtimeState: GroupActiveSchema,
|
|
61238
|
+
/**
|
|
61239
|
+
* Runtime-state durability: **session** — recomputed from the members on the
|
|
61240
|
+
* next source event; a restored value would outlive the members it named.
|
|
61241
|
+
*
|
|
61242
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
61243
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
61244
|
+
*/
|
|
61245
|
+
durability: "session",
|
|
61246
|
+
history: "none"
|
|
61247
|
+
};
|
|
61248
|
+
/**
|
|
60383
61249
|
* How many members of a helper group answered (D944). Counts are integers,
|
|
60384
61250
|
* not measurements: zero included is a real count, never a stand-in reading.
|
|
60385
61251
|
* The reading itself stays on the output capability. History is none — the
|
|
@@ -60410,7 +61276,14 @@ var groupStatusCapability = {
|
|
|
60410
61276
|
deviceTypes: [
|
|
60411
61277
|
DeviceType.Sensor,
|
|
60412
61278
|
DeviceType.Switch,
|
|
60413
|
-
DeviceType.Light
|
|
61279
|
+
DeviceType.Light,
|
|
61280
|
+
DeviceType.Fan,
|
|
61281
|
+
DeviceType.Cover,
|
|
61282
|
+
DeviceType.Valve,
|
|
61283
|
+
DeviceType.Lock,
|
|
61284
|
+
DeviceType.MediaPlayer,
|
|
61285
|
+
DeviceType.Thermostat,
|
|
61286
|
+
DeviceType.Climate
|
|
60414
61287
|
],
|
|
60415
61288
|
methods: {},
|
|
60416
61289
|
status: {
|
|
@@ -60558,7 +61431,7 @@ var humidifierCapability = {
|
|
|
60558
61431
|
*/
|
|
60559
61432
|
var HumiditySensorStatusSchema = object({
|
|
60560
61433
|
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
60561
|
-
|
|
61434
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
60562
61435
|
precision: 0,
|
|
60563
61436
|
label: "Humidity"
|
|
60564
61437
|
})),
|
|
@@ -60587,7 +61460,7 @@ var humiditySensorCapability = {
|
|
|
60587
61460
|
/**
|
|
60588
61461
|
* History (D850): recorded series.
|
|
60589
61462
|
*/
|
|
60590
|
-
history: { fields: {
|
|
61463
|
+
history: { fields: { humidity: {
|
|
60591
61464
|
kind: "gauge",
|
|
60592
61465
|
unit: "%",
|
|
60593
61466
|
deadband: .5
|
|
@@ -61227,7 +62100,7 @@ var LawnMowerControlStatusSchema = object({
|
|
|
61227
62100
|
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61228
62101
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
61229
62102
|
* task is active / progress is unavailable. */
|
|
61230
|
-
|
|
62103
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61231
62104
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
61232
62105
|
* or null when unknown. */
|
|
61233
62106
|
currentCode: number().nullable().meta(readingMeta("none")),
|
|
@@ -61683,7 +62556,14 @@ var lockControlCapability = {
|
|
|
61683
62556
|
}),
|
|
61684
62557
|
unlock: method(object({
|
|
61685
62558
|
deviceId: number().int().nonnegative(),
|
|
61686
|
-
code: string$2().min(1).optional()
|
|
62559
|
+
code: string$2().min(1).optional(),
|
|
62560
|
+
/**
|
|
62561
|
+
* Honoured by a helper lock GROUP only (D944): unlocking several doors
|
|
62562
|
+
* with one call is refused unless the caller says it was confirmed. A
|
|
62563
|
+
* single lock ignores it. The refusal lives in the callee, so no
|
|
62564
|
+
* automation or API caller reaches the doors by skipping a dialog.
|
|
62565
|
+
*/
|
|
62566
|
+
confirm: boolean().optional()
|
|
61687
62567
|
}), _void(), {
|
|
61688
62568
|
kind: "mutation",
|
|
61689
62569
|
auth: "protected"
|
|
@@ -61720,6 +62600,44 @@ var lockControlCapability = {
|
|
|
61720
62600
|
volatileStateFields: ["lastChangedAt"]
|
|
61721
62601
|
};
|
|
61722
62602
|
/**
|
|
62603
|
+
* A monotonic total between resets (D944). Energy uses `power-meter.energy`
|
|
62604
|
+
* so dashboards keep one series; every other instantaneous reading lands here.
|
|
62605
|
+
* `value` is the time integral in the source quantity's accumulated base
|
|
62606
|
+
* (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
|
|
62607
|
+
*/
|
|
62608
|
+
var MeterStatusSchema = object({
|
|
62609
|
+
value: number().nullable().meta(readingMeta("device", { label: "Total" })),
|
|
62610
|
+
lastFetchedAt: number()
|
|
62611
|
+
});
|
|
62612
|
+
var meterCapability = {
|
|
62613
|
+
name: "meter",
|
|
62614
|
+
scope: "device",
|
|
62615
|
+
deviceNative: true,
|
|
62616
|
+
mode: "singleton",
|
|
62617
|
+
deviceTypes: [DeviceType.Sensor],
|
|
62618
|
+
methods: {},
|
|
62619
|
+
status: {
|
|
62620
|
+
schema: MeterStatusSchema,
|
|
62621
|
+
kind: "push"
|
|
62622
|
+
},
|
|
62623
|
+
runtimeState: MeterStatusSchema,
|
|
62624
|
+
/**
|
|
62625
|
+
* Runtime-state durability: **restored** — the total is a cache of the
|
|
62626
|
+
* history integral. A restart shows it only until the history pass
|
|
62627
|
+
* recomputes the open period; it is not an in-memory buffer.
|
|
62628
|
+
*
|
|
62629
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
62630
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
62631
|
+
*/
|
|
62632
|
+
durability: "restored",
|
|
62633
|
+
history: { fields: { value: {
|
|
62634
|
+
kind: "counter",
|
|
62635
|
+
deadband: .001,
|
|
62636
|
+
minIntervalMs: 6e4
|
|
62637
|
+
} } },
|
|
62638
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
62639
|
+
};
|
|
62640
|
+
/**
|
|
61723
62641
|
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
61724
62642
|
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
61725
62643
|
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
@@ -61776,10 +62694,10 @@ var MediaPlayerStatusSchema = object({
|
|
|
61776
62694
|
currentMedia: MediaInfoSchema.nullable(),
|
|
61777
62695
|
/** Current playback position in ms. Null when not seekable or
|
|
61778
62696
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
61779
|
-
|
|
62697
|
+
position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61780
62698
|
/** Total duration of the current media in ms. Null when unknown
|
|
61781
62699
|
* (live stream) or nothing is playing. */
|
|
61782
|
-
|
|
62700
|
+
duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
61783
62701
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
61784
62702
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
61785
62703
|
shuffle: boolean().nullable(),
|
|
@@ -61818,7 +62736,7 @@ var mediaPlayerCapability = {
|
|
|
61818
62736
|
}),
|
|
61819
62737
|
seek: method(object({
|
|
61820
62738
|
deviceId: number().int().nonnegative(),
|
|
61821
|
-
|
|
62739
|
+
position: number().int().nonnegative()
|
|
61822
62740
|
}), _void(), {
|
|
61823
62741
|
kind: "mutation",
|
|
61824
62742
|
auth: "protected"
|
|
@@ -61890,7 +62808,7 @@ var mediaPlayerCapability = {
|
|
|
61890
62808
|
/**
|
|
61891
62809
|
* History (D850): volume (a level an operator or an automation sets; the
|
|
61892
62810
|
* default 10 s floor folds a slider drag into its end value) and mute.
|
|
61893
|
-
* `
|
|
62811
|
+
* `position` is not recorded: it moves continuously while playing.
|
|
61894
62812
|
*/
|
|
61895
62813
|
history: { fields: {
|
|
61896
62814
|
volumeLevel: {
|
|
@@ -62852,9 +63770,9 @@ var NetworkLinkStatusSchema = object({
|
|
|
62852
63770
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
62853
63771
|
* SKIP a null rather than coerce it.
|
|
62854
63772
|
*/
|
|
62855
|
-
|
|
63773
|
+
signal: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62856
63774
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
62857
|
-
|
|
63775
|
+
rssi: number().optional().meta(readingMeta("signal-level")),
|
|
62858
63776
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
62859
63777
|
ssid: string$2().optional(),
|
|
62860
63778
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
@@ -62890,7 +63808,7 @@ onStatusChanged: { data: object({
|
|
|
62890
63808
|
kind: "push",
|
|
62891
63809
|
empty: {
|
|
62892
63810
|
type: "unknown",
|
|
62893
|
-
|
|
63811
|
+
signal: null,
|
|
62894
63812
|
lastUpdated: 0
|
|
62895
63813
|
}
|
|
62896
63814
|
},
|
|
@@ -62913,13 +63831,13 @@ onStatusChanged: { data: object({
|
|
|
62913
63831
|
* History (D850): recorded series.
|
|
62914
63832
|
*/
|
|
62915
63833
|
history: { fields: {
|
|
62916
|
-
|
|
63834
|
+
signal: {
|
|
62917
63835
|
kind: "gauge",
|
|
62918
63836
|
unit: "%",
|
|
62919
63837
|
deadband: 3,
|
|
62920
63838
|
minIntervalMs: 6e4
|
|
62921
63839
|
},
|
|
62922
|
-
|
|
63840
|
+
rssi: {
|
|
62923
63841
|
kind: "gauge",
|
|
62924
63842
|
unit: "dBm",
|
|
62925
63843
|
deadband: 2,
|
|
@@ -63664,10 +64582,20 @@ var PetFeederDeviceStatusSchema = _enum([
|
|
|
63664
64582
|
]);
|
|
63665
64583
|
var gramsPortion = number().int().min(4).max(200);
|
|
63666
64584
|
var PetFeederStatusSchema = object({
|
|
63667
|
-
/** Food
|
|
63668
|
-
*
|
|
63669
|
-
* bowl
|
|
64585
|
+
/** Food in the TANK (hopper), as the feeder's own sensor reports it
|
|
64586
|
+
* (`state.food`). A coarse level code, not grams — `0` is empty. The
|
|
64587
|
+
* amount in the bowl is `bowlFood`. On dual-hopper models this is the
|
|
64588
|
+
* combined reading; per-hopper levels live in `food1`/`food2`. Null when
|
|
64589
|
+
* the device has not reported yet. */
|
|
63670
64590
|
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
64591
|
+
/** Food in the BOWL as the feeder reports it (`state.bowl`), in the
|
|
64592
|
+
* feeder's own units. Null when the model reports none. */
|
|
64593
|
+
bowlFood: number().nullable().meta(readingMeta("none")),
|
|
64594
|
+
/** True while the pet is eating (`state.eating`). */
|
|
64595
|
+
eating: boolean(),
|
|
64596
|
+
/** Whether `markFoodReplenished` does anything on this model. When false
|
|
64597
|
+
* the client must not offer it. */
|
|
64598
|
+
supportsMarkRefilled: boolean(),
|
|
63671
64599
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
63672
64600
|
* single-hopper models. */
|
|
63673
64601
|
food1: number().nullable().meta(readingMeta("none")),
|
|
@@ -63675,8 +64603,8 @@ var PetFeederStatusSchema = object({
|
|
|
63675
64603
|
* single-hopper models. */
|
|
63676
64604
|
food2: number().nullable().meta(readingMeta("none")),
|
|
63677
64605
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
63678
|
-
* (`device_class: problem`, on = low). True when the
|
|
63679
|
-
*
|
|
64606
|
+
* (`device_class: problem`, on = low). True when the tank reports empty,
|
|
64607
|
+
* or, on a dual-hopper model, when EITHER hopper is empty. */
|
|
63680
64608
|
lowFood: boolean(),
|
|
63681
64609
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
63682
64610
|
* device has no battery reading. */
|
|
@@ -63744,7 +64672,9 @@ var petFeederCapability = {
|
|
|
63744
64672
|
kind: "mutation",
|
|
63745
64673
|
auth: "protected"
|
|
63746
64674
|
}),
|
|
63747
|
-
/**
|
|
64675
|
+
/** Tell the PetKit cloud a hopper was refilled so it stops its low-food
|
|
64676
|
+
* reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
|
|
64677
|
+
* change any sensor reading. */
|
|
63748
64678
|
markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
63749
64679
|
kind: "mutation",
|
|
63750
64680
|
auth: "protected"
|
|
@@ -63817,6 +64747,7 @@ var petFeederCapability = {
|
|
|
63817
64747
|
*/
|
|
63818
64748
|
history: { fields: {
|
|
63819
64749
|
foodLevel: { kind: "gauge" },
|
|
64750
|
+
bowlFood: { kind: "gauge" },
|
|
63820
64751
|
food1: { kind: "gauge" },
|
|
63821
64752
|
food2: { kind: "gauge" },
|
|
63822
64753
|
batteryPower: {
|
|
@@ -64226,27 +65157,89 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
64226
65157
|
* in real-world devices.
|
|
64227
65158
|
*/
|
|
64228
65159
|
var PowerMeterStatusSchema = object({
|
|
64229
|
-
/** Instantaneous power draw
|
|
64230
|
-
|
|
65160
|
+
/** Instantaneous power draw (watts). */
|
|
65161
|
+
power: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
64231
65162
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
64232
|
-
|
|
64233
|
-
/** Voltage
|
|
64234
|
-
|
|
65163
|
+
energy: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
65164
|
+
/** Voltage (volts). */
|
|
65165
|
+
voltage: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
64235
65166
|
/** Current in amperes. */
|
|
64236
|
-
|
|
65167
|
+
current: number().optional().meta(readingMeta("current", { label: "Current" })),
|
|
64237
65168
|
/** Apparent power in volt-amperes. */
|
|
64238
|
-
|
|
65169
|
+
apparentPower: number().optional().meta(readingMeta("apparent-power")),
|
|
64239
65170
|
/** Power factor (0..1) if reported. */
|
|
64240
65171
|
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
64241
65172
|
/** Ms epoch when the slice was last updated. */
|
|
64242
65173
|
lastFetchedAt: number()
|
|
64243
65174
|
});
|
|
65175
|
+
/**
|
|
65176
|
+
* Loads that draw metered mains power (D951). The reading is a feature of the
|
|
65177
|
+
* device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
|
|
65178
|
+
* vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
|
|
65179
|
+
* meter or a standalone energy monitor IS the device. Cover, lock, valve,
|
|
65180
|
+
* camera and the like are left out: their draw is not what anyone meters.
|
|
65181
|
+
*/
|
|
65182
|
+
var POWER_METER_DEVICE_TYPES = [
|
|
65183
|
+
DeviceType.Sensor,
|
|
65184
|
+
DeviceType.Switch,
|
|
65185
|
+
DeviceType.Light,
|
|
65186
|
+
DeviceType.Fan,
|
|
65187
|
+
DeviceType.Climate,
|
|
65188
|
+
DeviceType.Thermostat,
|
|
65189
|
+
DeviceType.Humidifier,
|
|
65190
|
+
DeviceType.WaterHeater,
|
|
65191
|
+
DeviceType.MediaPlayer,
|
|
65192
|
+
DeviceType.Washer,
|
|
65193
|
+
DeviceType.Dryer,
|
|
65194
|
+
DeviceType.Dishwasher,
|
|
65195
|
+
DeviceType.Vacuum,
|
|
65196
|
+
DeviceType.LawnMower,
|
|
65197
|
+
DeviceType.PetFeeder
|
|
65198
|
+
];
|
|
65199
|
+
/** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
|
|
65200
|
+
var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
|
|
65201
|
+
/** One scalar target per reading; each is sourced from a device of its own (D951). */
|
|
65202
|
+
function powerTarget(id, unit) {
|
|
65203
|
+
return {
|
|
65204
|
+
id,
|
|
65205
|
+
cardinality: "scalar",
|
|
65206
|
+
vocabulary: {
|
|
65207
|
+
cap: "power-meter",
|
|
65208
|
+
stateField: id
|
|
65209
|
+
},
|
|
65210
|
+
deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
|
|
65211
|
+
apply: "add",
|
|
65212
|
+
path: id,
|
|
65213
|
+
sources: [{
|
|
65214
|
+
kind: "numeric",
|
|
65215
|
+
requiredUnit: unit,
|
|
65216
|
+
transforms: [{ kind: "identity" }]
|
|
65217
|
+
}],
|
|
65218
|
+
companions: [{
|
|
65219
|
+
kind: "clock",
|
|
65220
|
+
path: "lastFetchedAt",
|
|
65221
|
+
when: "always"
|
|
65222
|
+
}],
|
|
65223
|
+
sourceReport: { status: "assignment-status" },
|
|
65224
|
+
advancedExpression: true
|
|
65225
|
+
};
|
|
65226
|
+
}
|
|
65227
|
+
/** "Power and energy" extension: power, energy, voltage and current from other devices (D951). */
|
|
65228
|
+
var powerMeterCustomize = {
|
|
65229
|
+
version: 1,
|
|
65230
|
+
targets: [
|
|
65231
|
+
powerTarget("power", "W"),
|
|
65232
|
+
powerTarget("energy", "kWh"),
|
|
65233
|
+
powerTarget("voltage", "V"),
|
|
65234
|
+
powerTarget("current", "A")
|
|
65235
|
+
]
|
|
65236
|
+
};
|
|
64244
65237
|
var powerMeterCapability = {
|
|
64245
65238
|
name: "power-meter",
|
|
64246
65239
|
scope: "device",
|
|
64247
65240
|
deviceNative: true,
|
|
64248
65241
|
mode: "singleton",
|
|
64249
|
-
deviceTypes:
|
|
65242
|
+
deviceTypes: POWER_METER_DEVICE_TYPES,
|
|
64250
65243
|
methods: {},
|
|
64251
65244
|
status: {
|
|
64252
65245
|
schema: PowerMeterStatusSchema,
|
|
@@ -64254,41 +65247,41 @@ var powerMeterCapability = {
|
|
|
64254
65247
|
},
|
|
64255
65248
|
runtimeState: PowerMeterStatusSchema,
|
|
64256
65249
|
/**
|
|
64257
|
-
* Runtime-state durability: **restored** — as `numeric-sensor`; `
|
|
65250
|
+
* Runtime-state durability: **restored** — as `numeric-sensor`; `energy` is an accumulator whose restored value is the baseline.
|
|
64258
65251
|
*
|
|
64259
65252
|
* See `RuntimeStateDurability`. Enforced by
|
|
64260
65253
|
* `scripts/check-runtime-state-durability.ts`.
|
|
64261
65254
|
*/
|
|
64262
65255
|
durability: "restored",
|
|
64263
65256
|
/**
|
|
64264
|
-
* History (D850): recorded series. Gauges are floored at 30 s; `
|
|
65257
|
+
* History (D850): recorded series. Gauges are floored at 30 s; `energy` is the one monotonic meter (counter).
|
|
64265
65258
|
*/
|
|
64266
65259
|
history: { fields: {
|
|
64267
|
-
|
|
65260
|
+
power: {
|
|
64268
65261
|
kind: "gauge",
|
|
64269
65262
|
unit: "W",
|
|
64270
65263
|
deadband: 1,
|
|
64271
65264
|
minIntervalMs: 3e4
|
|
64272
65265
|
},
|
|
64273
|
-
|
|
65266
|
+
energy: {
|
|
64274
65267
|
kind: "counter",
|
|
64275
65268
|
unit: "kWh",
|
|
64276
65269
|
deadband: .01,
|
|
64277
65270
|
minIntervalMs: 6e4
|
|
64278
65271
|
},
|
|
64279
|
-
|
|
65272
|
+
voltage: {
|
|
64280
65273
|
kind: "gauge",
|
|
64281
65274
|
unit: "V",
|
|
64282
65275
|
deadband: 1,
|
|
64283
65276
|
minIntervalMs: 3e4
|
|
64284
65277
|
},
|
|
64285
|
-
|
|
65278
|
+
current: {
|
|
64286
65279
|
kind: "gauge",
|
|
64287
65280
|
unit: "A",
|
|
64288
65281
|
deadband: .05,
|
|
64289
65282
|
minIntervalMs: 3e4
|
|
64290
65283
|
},
|
|
64291
|
-
|
|
65284
|
+
apparentPower: {
|
|
64292
65285
|
kind: "gauge",
|
|
64293
65286
|
unit: "VA",
|
|
64294
65287
|
deadband: 1,
|
|
@@ -64303,7 +65296,8 @@ var powerMeterCapability = {
|
|
|
64303
65296
|
} },
|
|
64304
65297
|
/** Clock fields: written, but excluded from the compare that decides
|
|
64305
65298
|
* whether persisting is worth a SQLite commit. */
|
|
64306
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
65299
|
+
volatileStateFields: ["lastFetchedAt"],
|
|
65300
|
+
customize: powerMeterCustomize
|
|
64307
65301
|
};
|
|
64308
65302
|
/**
|
|
64309
65303
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
@@ -64337,9 +65331,6 @@ var PresenceStatusSchema = object({
|
|
|
64337
65331
|
location: string$2().nullable(),
|
|
64338
65332
|
/** GPS coordinates when available. Null otherwise. */
|
|
64339
65333
|
gps: GpsLocationSchema.nullable(),
|
|
64340
|
-
/** Optional battery level of the tracking device (0..100). Null
|
|
64341
|
-
* when not reported. */
|
|
64342
|
-
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
64343
65334
|
/** Ms epoch when the slice was last updated. */
|
|
64344
65335
|
lastChangedAt: number()
|
|
64345
65336
|
});
|
|
@@ -64368,14 +65359,7 @@ var presenceCapability = {
|
|
|
64368
65359
|
* `scripts/check-runtime-state-durability.ts`.
|
|
64369
65360
|
*/
|
|
64370
65361
|
durability: "restored",
|
|
64371
|
-
|
|
64372
|
-
* History (D850): recorded series.
|
|
64373
|
-
*/
|
|
64374
|
-
history: { fields: { batteryPercent: {
|
|
64375
|
-
kind: "gauge",
|
|
64376
|
-
unit: "%",
|
|
64377
|
-
deadband: 1
|
|
64378
|
-
} } },
|
|
65362
|
+
history: "none",
|
|
64379
65363
|
/** Clock fields: written, but excluded from the compare that decides
|
|
64380
65364
|
* whether persisting is worth a SQLite commit. */
|
|
64381
65365
|
volatileStateFields: ["lastChangedAt"]
|
|
@@ -64390,7 +65374,7 @@ var presenceCapability = {
|
|
|
64390
65374
|
*/
|
|
64391
65375
|
var PressureSensorStatusSchema = object({
|
|
64392
65376
|
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
64393
|
-
|
|
65377
|
+
pressure: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
64394
65378
|
/** Ms epoch when the slice was last updated. */
|
|
64395
65379
|
lastFetchedAt: number()
|
|
64396
65380
|
});
|
|
@@ -64416,7 +65400,7 @@ var pressureSensorCapability = {
|
|
|
64416
65400
|
/**
|
|
64417
65401
|
* History (D850): recorded series.
|
|
64418
65402
|
*/
|
|
64419
|
-
history: { fields: {
|
|
65403
|
+
history: { fields: { pressure: {
|
|
64420
65404
|
kind: "gauge",
|
|
64421
65405
|
unit: "hPa",
|
|
64422
65406
|
deadband: .1
|
|
@@ -64984,6 +65968,8 @@ var runningCustomize = {
|
|
|
64984
65968
|
stateField: "running"
|
|
64985
65969
|
},
|
|
64986
65970
|
deviceTypes: [
|
|
65971
|
+
DeviceType.Washer,
|
|
65972
|
+
DeviceType.Dryer,
|
|
64987
65973
|
DeviceType.Dishwasher,
|
|
64988
65974
|
DeviceType.Vacuum,
|
|
64989
65975
|
DeviceType.LawnMower,
|
|
@@ -67473,7 +68459,8 @@ var switchCapability = {
|
|
|
67473
68459
|
deviceTypes: [
|
|
67474
68460
|
DeviceType.Switch,
|
|
67475
68461
|
DeviceType.Siren,
|
|
67476
|
-
DeviceType.Light
|
|
68462
|
+
DeviceType.Light,
|
|
68463
|
+
DeviceType.Fan
|
|
67477
68464
|
],
|
|
67478
68465
|
methods: { setState: method(object({
|
|
67479
68466
|
deviceId: number(),
|
|
@@ -68112,7 +69099,7 @@ var tamperCapability = {
|
|
|
68112
69099
|
*/
|
|
68113
69100
|
var TemperatureSensorStatusSchema = object({
|
|
68114
69101
|
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
68115
|
-
|
|
69102
|
+
temperature: number().nullable().meta(readingMeta("temperature", {
|
|
68116
69103
|
precision: 1,
|
|
68117
69104
|
label: "Temperature"
|
|
68118
69105
|
})),
|
|
@@ -68141,7 +69128,7 @@ var temperatureSensorCapability = {
|
|
|
68141
69128
|
/**
|
|
68142
69129
|
* History (D850): recorded series.
|
|
68143
69130
|
*/
|
|
68144
|
-
history: { fields: {
|
|
69131
|
+
history: { fields: { temperature: {
|
|
68145
69132
|
kind: "gauge",
|
|
68146
69133
|
unit: "°C",
|
|
68147
69134
|
deadband: .1
|
|
@@ -68438,7 +69425,7 @@ var VacuumControlStatusSchema = object({
|
|
|
68438
69425
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
68439
69426
|
availableFanSpeeds: array(string$2()),
|
|
68440
69427
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
68441
|
-
|
|
69428
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
68442
69429
|
/** Current error code (0 / null = no error). */
|
|
68443
69430
|
errorCode: number().nullable().meta(readingMeta("none")),
|
|
68444
69431
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
@@ -69686,8 +70673,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
69686
70673
|
kind: "mutation",
|
|
69687
70674
|
auth: "admin"
|
|
69688
70675
|
});
|
|
70676
|
+
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
69689
70677
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
69690
|
-
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;
|
|
70678
|
+
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;
|
|
69691
70679
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
69692
70680
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
69693
70681
|
new Set(CAP_NAMES);
|
|
@@ -70016,6 +71004,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
70016
71004
|
featureProbe: featureProbeCapability,
|
|
70017
71005
|
flood: floodCapability,
|
|
70018
71006
|
gas: gasCapability,
|
|
71007
|
+
groupActive: groupActiveCapability,
|
|
70019
71008
|
groupCommands: groupCommandsCapability,
|
|
70020
71009
|
groupStatus: groupStatusCapability,
|
|
70021
71010
|
heatingValve: heatingValveCapability,
|
|
@@ -70027,6 +71016,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
70027
71016
|
lawnMowerControl: lawnMowerControlCapability,
|
|
70028
71017
|
lockControl: lockControlCapability,
|
|
70029
71018
|
mediaPlayer: mediaPlayerCapability,
|
|
71019
|
+
meter: meterCapability,
|
|
70030
71020
|
motion: motionCapability,
|
|
70031
71021
|
motionTrigger: motionTriggerCapability,
|
|
70032
71022
|
motionZones: motionZonesCapability,
|
|
@@ -70089,7 +71079,7 @@ var BaseDevice = class {
|
|
|
70089
71079
|
* `device-local-state.ts`. Drivers access via:
|
|
70090
71080
|
*
|
|
70091
71081
|
* `this.state.battery.sleeping = true` // patches the battery slice
|
|
70092
|
-
* `const pct = this.state.battery.
|
|
71082
|
+
* `const pct = this.state.battery.level` // reads the battery slice
|
|
70093
71083
|
* `this.state.deviceStatus.online = true` // mirrors `markOnline(true)`
|
|
70094
71084
|
*
|
|
70095
71085
|
* Adding a new cap with `runtimeState:` automatically extends this
|
|
@@ -70129,7 +71119,7 @@ var BaseDevice = class {
|
|
|
70129
71119
|
* shape; drivers don't redeclare it, they just write through.
|
|
70130
71120
|
*
|
|
70131
71121
|
* Read: `this.runtimeState.getCapState('battery')` →
|
|
70132
|
-
* `{
|
|
71122
|
+
* `{level, charging, sleeping, lastUpdated}` for any
|
|
70133
71123
|
* provider that registers `batteryCapability`.
|
|
70134
71124
|
* Write: `this.runtimeState.setCapState('battery', { … })`.
|
|
70135
71125
|
*
|
|
@@ -71286,7 +72276,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
71286
72276
|
return toDeviceSummary(device, this.addonId);
|
|
71287
72277
|
}
|
|
71288
72278
|
};
|
|
71289
|
-
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;
|
|
72279
|
+
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;
|
|
71290
72280
|
new Set(Object.values(DeviceType));
|
|
71291
72281
|
Object.freeze({
|
|
71292
72282
|
"accessories.getStatus": {
|
|
@@ -71303,6 +72293,104 @@ Object.freeze({
|
|
|
71303
72293
|
access: "create",
|
|
71304
72294
|
role: "admin"
|
|
71305
72295
|
},
|
|
72296
|
+
"actions.create": {
|
|
72297
|
+
capName: "actions",
|
|
72298
|
+
capScope: "system",
|
|
72299
|
+
addonId: null,
|
|
72300
|
+
access: "create",
|
|
72301
|
+
role: "admin"
|
|
72302
|
+
},
|
|
72303
|
+
"actions.delete": {
|
|
72304
|
+
capName: "actions",
|
|
72305
|
+
capScope: "system",
|
|
72306
|
+
addonId: null,
|
|
72307
|
+
access: "delete",
|
|
72308
|
+
role: "admin"
|
|
72309
|
+
},
|
|
72310
|
+
"actions.get": {
|
|
72311
|
+
capName: "actions",
|
|
72312
|
+
capScope: "system",
|
|
72313
|
+
addonId: null,
|
|
72314
|
+
access: "view",
|
|
72315
|
+
role: "viewer"
|
|
72316
|
+
},
|
|
72317
|
+
"actions.getLinks": {
|
|
72318
|
+
capName: "actions",
|
|
72319
|
+
capScope: "system",
|
|
72320
|
+
addonId: null,
|
|
72321
|
+
access: "view",
|
|
72322
|
+
role: "admin"
|
|
72323
|
+
},
|
|
72324
|
+
"actions.getTraces": {
|
|
72325
|
+
capName: "actions",
|
|
72326
|
+
capScope: "system",
|
|
72327
|
+
addonId: null,
|
|
72328
|
+
access: "view",
|
|
72329
|
+
role: "admin"
|
|
72330
|
+
},
|
|
72331
|
+
"actions.list": {
|
|
72332
|
+
capName: "actions",
|
|
72333
|
+
capScope: "system",
|
|
72334
|
+
addonId: null,
|
|
72335
|
+
access: "view",
|
|
72336
|
+
role: "admin"
|
|
72337
|
+
},
|
|
72338
|
+
"actions.listForCamera": {
|
|
72339
|
+
capName: "actions",
|
|
72340
|
+
capScope: "system",
|
|
72341
|
+
addonId: null,
|
|
72342
|
+
access: "view",
|
|
72343
|
+
role: "viewer"
|
|
72344
|
+
},
|
|
72345
|
+
"actions.run": {
|
|
72346
|
+
capName: "actions",
|
|
72347
|
+
capScope: "system",
|
|
72348
|
+
addonId: null,
|
|
72349
|
+
access: "create",
|
|
72350
|
+
role: "viewer"
|
|
72351
|
+
},
|
|
72352
|
+
"actions.setLinks": {
|
|
72353
|
+
capName: "actions",
|
|
72354
|
+
capScope: "system",
|
|
72355
|
+
addonId: null,
|
|
72356
|
+
access: "create",
|
|
72357
|
+
role: "admin"
|
|
72358
|
+
},
|
|
72359
|
+
"actions.stop": {
|
|
72360
|
+
capName: "actions",
|
|
72361
|
+
capScope: "system",
|
|
72362
|
+
addonId: null,
|
|
72363
|
+
access: "create",
|
|
72364
|
+
role: "admin"
|
|
72365
|
+
},
|
|
72366
|
+
"actions.update": {
|
|
72367
|
+
capName: "actions",
|
|
72368
|
+
capScope: "system",
|
|
72369
|
+
addonId: null,
|
|
72370
|
+
access: "create",
|
|
72371
|
+
role: "admin"
|
|
72372
|
+
},
|
|
72373
|
+
"actionSource.listActions": {
|
|
72374
|
+
capName: "action-source",
|
|
72375
|
+
capScope: "system",
|
|
72376
|
+
addonId: null,
|
|
72377
|
+
access: "view",
|
|
72378
|
+
role: "admin"
|
|
72379
|
+
},
|
|
72380
|
+
"actionSource.runAction": {
|
|
72381
|
+
capName: "action-source",
|
|
72382
|
+
capScope: "system",
|
|
72383
|
+
addonId: null,
|
|
72384
|
+
access: "create",
|
|
72385
|
+
role: "admin"
|
|
72386
|
+
},
|
|
72387
|
+
"actionSource.stopAction": {
|
|
72388
|
+
capName: "action-source",
|
|
72389
|
+
capScope: "system",
|
|
72390
|
+
addonId: null,
|
|
72391
|
+
access: "create",
|
|
72392
|
+
role: "admin"
|
|
72393
|
+
},
|
|
71306
72394
|
"addonPages.listPages": {
|
|
71307
72395
|
capName: "addon-pages",
|
|
71308
72396
|
capScope: "system",
|
|
@@ -72584,6 +73672,13 @@ Object.freeze({
|
|
|
72584
73672
|
access: "view",
|
|
72585
73673
|
role: "admin"
|
|
72586
73674
|
},
|
|
73675
|
+
"coreBlocks.getAutomationRunState": {
|
|
73676
|
+
capName: "core-blocks",
|
|
73677
|
+
capScope: "system",
|
|
73678
|
+
addonId: null,
|
|
73679
|
+
access: "view",
|
|
73680
|
+
role: "admin"
|
|
73681
|
+
},
|
|
72587
73682
|
"coreBlocks.getBlockSettings": {
|
|
72588
73683
|
capName: "core-blocks",
|
|
72589
73684
|
capScope: "system",
|
|
@@ -72605,6 +73700,13 @@ Object.freeze({
|
|
|
72605
73700
|
access: "view",
|
|
72606
73701
|
role: "admin"
|
|
72607
73702
|
},
|
|
73703
|
+
"coreBlocks.getTraces": {
|
|
73704
|
+
capName: "core-blocks",
|
|
73705
|
+
capScope: "system",
|
|
73706
|
+
addonId: null,
|
|
73707
|
+
access: "view",
|
|
73708
|
+
role: "admin"
|
|
73709
|
+
},
|
|
72608
73710
|
"coreBlocks.getTypeDefs": {
|
|
72609
73711
|
capName: "core-blocks",
|
|
72610
73712
|
capScope: "system",
|
|
@@ -72640,6 +73742,13 @@ Object.freeze({
|
|
|
72640
73742
|
access: "create",
|
|
72641
73743
|
role: "admin"
|
|
72642
73744
|
},
|
|
73745
|
+
"coreBlocks.reportAutomationRun": {
|
|
73746
|
+
capName: "core-blocks",
|
|
73747
|
+
capScope: "system",
|
|
73748
|
+
addonId: null,
|
|
73749
|
+
access: "create",
|
|
73750
|
+
role: "admin"
|
|
73751
|
+
},
|
|
72643
73752
|
"coreBlocks.restart": {
|
|
72644
73753
|
capName: "core-blocks",
|
|
72645
73754
|
capScope: "system",
|
|
@@ -74509,6 +75618,13 @@ Object.freeze({
|
|
|
74509
75618
|
access: "view",
|
|
74510
75619
|
role: "viewer"
|
|
74511
75620
|
},
|
|
75621
|
+
"groupActive.getStatus": {
|
|
75622
|
+
capName: "group-active",
|
|
75623
|
+
capScope: "device",
|
|
75624
|
+
addonId: null,
|
|
75625
|
+
access: "view",
|
|
75626
|
+
role: "viewer"
|
|
75627
|
+
},
|
|
74512
75628
|
"groupCommands.getStatus": {
|
|
74513
75629
|
capName: "group-commands",
|
|
74514
75630
|
capScope: "device",
|
|
@@ -75475,6 +76591,13 @@ Object.freeze({
|
|
|
75475
76591
|
access: "create",
|
|
75476
76592
|
role: "admin"
|
|
75477
76593
|
},
|
|
76594
|
+
"meter.getStatus": {
|
|
76595
|
+
capName: "meter",
|
|
76596
|
+
capScope: "device",
|
|
76597
|
+
addonId: null,
|
|
76598
|
+
access: "view",
|
|
76599
|
+
role: "viewer"
|
|
76600
|
+
},
|
|
75478
76601
|
"metricsProvider.collectSnapshot": {
|
|
75479
76602
|
capName: "metrics-provider",
|
|
75480
76603
|
capScope: "system",
|
|
@@ -81199,7 +82322,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81199
82322
|
label: t("Full brightness", "Luminosità massima"),
|
|
81200
82323
|
phrase: t("Set {device} to full brightness", "Porta {device} alla massima luminosità"),
|
|
81201
82324
|
icon: "sun",
|
|
81202
|
-
args: {
|
|
82325
|
+
args: { level: 100 }
|
|
81203
82326
|
},
|
|
81204
82327
|
{
|
|
81205
82328
|
id: "brightness.half",
|
|
@@ -81207,7 +82330,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81207
82330
|
label: t("Half brightness", "Luminosità a metà"),
|
|
81208
82331
|
phrase: t("Dim {device} to half", "Abbassa {device} a metà luminosità"),
|
|
81209
82332
|
icon: "sun-dim",
|
|
81210
|
-
args: {
|
|
82333
|
+
args: { level: 50 }
|
|
81211
82334
|
},
|
|
81212
82335
|
{
|
|
81213
82336
|
id: "brightness.off",
|
|
@@ -81215,16 +82338,16 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81215
82338
|
label: t("Brightness off", "Luminosità a zero"),
|
|
81216
82339
|
phrase: t("Dim {device} to zero", "Abbassa {device} a zero"),
|
|
81217
82340
|
icon: "lightbulb-off",
|
|
81218
|
-
args: {
|
|
82341
|
+
args: { level: 0 }
|
|
81219
82342
|
},
|
|
81220
82343
|
{
|
|
81221
82344
|
id: "brightness.set",
|
|
81222
82345
|
method: "setBrightness",
|
|
81223
82346
|
label: t("Set brightness", "Imposta la luminosità"),
|
|
81224
|
-
phrase: t("Set the brightness of {device} to {
|
|
82347
|
+
phrase: t("Set the brightness of {device} to {level}", "Imposta la luminosità di {device} a {level}"),
|
|
81225
82348
|
icon: "sun-medium",
|
|
81226
82349
|
openArgs: [{
|
|
81227
|
-
key: "
|
|
82350
|
+
key: "level",
|
|
81228
82351
|
label: PERCENT,
|
|
81229
82352
|
offer: {
|
|
81230
82353
|
kind: "number",
|
|
@@ -81238,13 +82361,13 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81238
82361
|
],
|
|
81239
82362
|
state: [{
|
|
81240
82363
|
kind: "numeric",
|
|
81241
|
-
field: "
|
|
82364
|
+
field: "level",
|
|
81242
82365
|
quantity: t("the brightness", "la luminosità"),
|
|
81243
82366
|
label: t("Brightness", "Luminosità"),
|
|
81244
82367
|
unit: "%",
|
|
81245
82368
|
icon: "sun"
|
|
81246
82369
|
}],
|
|
81247
|
-
events: { onBrightnessChanged: { notOperatorFacing: "the `
|
|
82370
|
+
events: { onBrightnessChanged: { notOperatorFacing: "the `level` state trigger reads the same change" } }
|
|
81248
82371
|
},
|
|
81249
82372
|
color: {
|
|
81250
82373
|
scope: "device",
|
|
@@ -81258,7 +82381,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81258
82381
|
icon: "sun",
|
|
81259
82382
|
args: { color: {
|
|
81260
82383
|
mode: "mired",
|
|
81261
|
-
|
|
82384
|
+
colorTemperature: 370
|
|
81262
82385
|
} }
|
|
81263
82386
|
},
|
|
81264
82387
|
{
|
|
@@ -81269,7 +82392,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81269
82392
|
icon: "snowflake",
|
|
81270
82393
|
args: { color: {
|
|
81271
82394
|
mode: "mired",
|
|
81272
|
-
|
|
82395
|
+
colorTemperature: 200
|
|
81273
82396
|
} }
|
|
81274
82397
|
},
|
|
81275
82398
|
{
|
|
@@ -81883,7 +83006,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81883
83006
|
label: t("Fan off", "Ventilatore spento"),
|
|
81884
83007
|
phrase: t("Stop {device}", "Ferma {device}"),
|
|
81885
83008
|
icon: "power-off",
|
|
81886
|
-
args: {
|
|
83009
|
+
args: { speed: 0 }
|
|
81887
83010
|
},
|
|
81888
83011
|
{
|
|
81889
83012
|
id: "fan.full",
|
|
@@ -81891,16 +83014,16 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81891
83014
|
label: t("Fan full", "Ventilatore al massimo"),
|
|
81892
83015
|
phrase: t("Run {device} at full speed", "Porta {device} alla massima velocità"),
|
|
81893
83016
|
icon: "fan",
|
|
81894
|
-
args: {
|
|
83017
|
+
args: { speed: 100 }
|
|
81895
83018
|
},
|
|
81896
83019
|
{
|
|
81897
83020
|
id: "fan.speed",
|
|
81898
83021
|
method: "setPercentage",
|
|
81899
83022
|
label: t("Set speed", "Imposta la velocità"),
|
|
81900
|
-
phrase: t("Set the speed of {device} to {
|
|
83023
|
+
phrase: t("Set the speed of {device} to {speed}", "Imposta la velocità di {device} al {speed}"),
|
|
81901
83024
|
icon: "fan",
|
|
81902
83025
|
openArgs: [{
|
|
81903
|
-
key: "
|
|
83026
|
+
key: "speed",
|
|
81904
83027
|
label: t("Speed", "Velocità"),
|
|
81905
83028
|
offer: {
|
|
81906
83029
|
kind: "number",
|
|
@@ -81962,7 +83085,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
81962
83085
|
],
|
|
81963
83086
|
state: [{
|
|
81964
83087
|
kind: "numeric",
|
|
81965
|
-
field: "
|
|
83088
|
+
field: "speed",
|
|
81966
83089
|
quantity: t("the speed", "la velocità"),
|
|
81967
83090
|
label: t("Speed", "Velocità"),
|
|
81968
83091
|
unit: "%",
|
|
@@ -82760,7 +83883,7 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
82760
83883
|
},
|
|
82761
83884
|
{
|
|
82762
83885
|
kind: "numeric",
|
|
82763
|
-
field: "
|
|
83886
|
+
field: "progress",
|
|
82764
83887
|
quantity: t("the cycle progress", "l’avanzamento del lavaggio"),
|
|
82765
83888
|
label: t("Progress", "Avanzamento"),
|
|
82766
83889
|
unit: "%",
|
|
@@ -83118,10 +84241,10 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
83118
84241
|
id: "media.seek",
|
|
83119
84242
|
method: "seek",
|
|
83120
84243
|
label: t("Seek", "Vai al punto"),
|
|
83121
|
-
phrase: t("Move {device} to {
|
|
84244
|
+
phrase: t("Move {device} to {position}", "Porta {device} al punto {position}"),
|
|
83122
84245
|
icon: "fast-forward",
|
|
83123
84246
|
openArgs: [{
|
|
83124
|
-
key: "
|
|
84247
|
+
key: "position",
|
|
83125
84248
|
label: t("Position", "Posizione"),
|
|
83126
84249
|
offer: {
|
|
83127
84250
|
kind: "number",
|
|
@@ -83573,7 +84696,39 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
83573
84696
|
phrase: t("When {device} finishes", "Quando {device} finisce"),
|
|
83574
84697
|
icon: "square"
|
|
83575
84698
|
}
|
|
83576
|
-
}
|
|
84699
|
+
},
|
|
84700
|
+
overrides: { byType: {
|
|
84701
|
+
[DeviceType.Washer]: {
|
|
84702
|
+
state: { running: {
|
|
84703
|
+
whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
|
|
84704
|
+
whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
|
|
84705
|
+
} },
|
|
84706
|
+
events: {
|
|
84707
|
+
onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
|
|
84708
|
+
onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
|
|
84709
|
+
}
|
|
84710
|
+
},
|
|
84711
|
+
[DeviceType.Dryer]: {
|
|
84712
|
+
state: { running: {
|
|
84713
|
+
whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
|
|
84714
|
+
whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
|
|
84715
|
+
} },
|
|
84716
|
+
events: {
|
|
84717
|
+
onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
|
|
84718
|
+
onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
|
|
84719
|
+
}
|
|
84720
|
+
},
|
|
84721
|
+
[DeviceType.Dishwasher]: {
|
|
84722
|
+
state: { running: {
|
|
84723
|
+
whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
|
|
84724
|
+
whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
|
|
84725
|
+
} },
|
|
84726
|
+
events: {
|
|
84727
|
+
onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
|
|
84728
|
+
onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
|
|
84729
|
+
}
|
|
84730
|
+
}
|
|
84731
|
+
} }
|
|
83577
84732
|
},
|
|
83578
84733
|
update: {
|
|
83579
84734
|
scope: "device",
|
|
@@ -84138,7 +85293,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84138
85293
|
state: [
|
|
84139
85294
|
{
|
|
84140
85295
|
kind: "numeric",
|
|
84141
|
-
field: "
|
|
85296
|
+
field: "co2",
|
|
84142
85297
|
quantity: t("the CO₂", "la CO₂"),
|
|
84143
85298
|
label: t("CO₂", "CO₂"),
|
|
84144
85299
|
unit: "ppm",
|
|
@@ -84146,7 +85301,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84146
85301
|
},
|
|
84147
85302
|
{
|
|
84148
85303
|
kind: "numeric",
|
|
84149
|
-
field: "
|
|
85304
|
+
field: "voc",
|
|
84150
85305
|
quantity: t("the VOC level", "il livello di COV"),
|
|
84151
85306
|
label: t("VOC", "COV"),
|
|
84152
85307
|
unit: "ppb",
|
|
@@ -84182,7 +85337,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84182
85337
|
noun: t("Light level", "Luminosità ambiente"),
|
|
84183
85338
|
state: [{
|
|
84184
85339
|
kind: "numeric",
|
|
84185
|
-
field: "
|
|
85340
|
+
field: "illuminance",
|
|
84186
85341
|
quantity: t("the light level", "la luminosità"),
|
|
84187
85342
|
label: t("Illuminance", "Illuminamento"),
|
|
84188
85343
|
unit: "lx",
|
|
@@ -84345,6 +85500,10 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84345
85500
|
icon: "flame"
|
|
84346
85501
|
}]
|
|
84347
85502
|
},
|
|
85503
|
+
"group-active": {
|
|
85504
|
+
scope: "device",
|
|
85505
|
+
noun: t("Group state", "Stato del gruppo")
|
|
85506
|
+
},
|
|
84348
85507
|
"group-commands": {
|
|
84349
85508
|
scope: "device",
|
|
84350
85509
|
noun: t("Group command", "Comando di gruppo")
|
|
@@ -84381,7 +85540,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84381
85540
|
noun: t("Humidity", "Umidità"),
|
|
84382
85541
|
state: [{
|
|
84383
85542
|
kind: "numeric",
|
|
84384
|
-
field: "
|
|
85543
|
+
field: "humidity",
|
|
84385
85544
|
quantity: t("the humidity", "l’umidità"),
|
|
84386
85545
|
label: t("Humidity", "Umidità"),
|
|
84387
85546
|
unit: "%",
|
|
@@ -84401,6 +85560,17 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84401
85560
|
}],
|
|
84402
85561
|
events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
|
|
84403
85562
|
},
|
|
85563
|
+
meter: {
|
|
85564
|
+
scope: "device",
|
|
85565
|
+
noun: t("Meter", "Contatore"),
|
|
85566
|
+
state: [{
|
|
85567
|
+
kind: "numeric",
|
|
85568
|
+
field: "value",
|
|
85569
|
+
quantity: t("the total", "il totale"),
|
|
85570
|
+
label: t("Total", "Totale"),
|
|
85571
|
+
icon: "gauge"
|
|
85572
|
+
}]
|
|
85573
|
+
},
|
|
84404
85574
|
"numeric-sensor": {
|
|
84405
85575
|
scope: "device",
|
|
84406
85576
|
noun: t("Sensor", "Sensore"),
|
|
@@ -84418,7 +85588,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84418
85588
|
state: [
|
|
84419
85589
|
{
|
|
84420
85590
|
kind: "numeric",
|
|
84421
|
-
field: "
|
|
85591
|
+
field: "power",
|
|
84422
85592
|
quantity: t("the power draw", "il consumo"),
|
|
84423
85593
|
label: t("Power", "Potenza"),
|
|
84424
85594
|
unit: "W",
|
|
@@ -84426,7 +85596,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84426
85596
|
},
|
|
84427
85597
|
{
|
|
84428
85598
|
kind: "numeric",
|
|
84429
|
-
field: "
|
|
85599
|
+
field: "energy",
|
|
84430
85600
|
quantity: t("the energy used", "l’energia consumata"),
|
|
84431
85601
|
label: t("Energy", "Energia"),
|
|
84432
85602
|
unit: "kWh",
|
|
@@ -84434,7 +85604,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84434
85604
|
},
|
|
84435
85605
|
{
|
|
84436
85606
|
kind: "numeric",
|
|
84437
|
-
field: "
|
|
85607
|
+
field: "voltage",
|
|
84438
85608
|
quantity: t("the voltage", "la tensione"),
|
|
84439
85609
|
label: t("Voltage", "Tensione"),
|
|
84440
85610
|
unit: "V",
|
|
@@ -84442,7 +85612,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84442
85612
|
},
|
|
84443
85613
|
{
|
|
84444
85614
|
kind: "numeric",
|
|
84445
|
-
field: "
|
|
85615
|
+
field: "current",
|
|
84446
85616
|
quantity: t("the current", "la corrente"),
|
|
84447
85617
|
label: t("Current", "Corrente"),
|
|
84448
85618
|
unit: "A",
|
|
@@ -84459,13 +85629,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84459
85629
|
label: t("Where", "Posizione"),
|
|
84460
85630
|
phrase: t("When {device} is at {value}", "Quando {device} è in {value}"),
|
|
84461
85631
|
icon: "map-pin"
|
|
84462
|
-
}, {
|
|
84463
|
-
kind: "numeric",
|
|
84464
|
-
field: "batteryPercent",
|
|
84465
|
-
quantity: t("the phone battery", "la batteria del telefono"),
|
|
84466
|
-
label: t("Battery", "Batteria"),
|
|
84467
|
-
unit: "%",
|
|
84468
|
-
icon: "battery"
|
|
84469
85632
|
}]
|
|
84470
85633
|
},
|
|
84471
85634
|
"pressure-sensor": {
|
|
@@ -84473,7 +85636,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84473
85636
|
noun: t("Pressure", "Pressione"),
|
|
84474
85637
|
state: [{
|
|
84475
85638
|
kind: "numeric",
|
|
84476
|
-
field: "
|
|
85639
|
+
field: "pressure",
|
|
84477
85640
|
quantity: t("the pressure", "la pressione"),
|
|
84478
85641
|
label: t("Pressure", "Pressione"),
|
|
84479
85642
|
unit: "hPa",
|
|
@@ -84509,7 +85672,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84509
85672
|
noun: t("Temperature", "Temperatura"),
|
|
84510
85673
|
state: [{
|
|
84511
85674
|
kind: "numeric",
|
|
84512
|
-
field: "
|
|
85675
|
+
field: "temperature",
|
|
84513
85676
|
quantity: t("the temperature", "la temperatura"),
|
|
84514
85677
|
label: t("Temperature", "Temperatura"),
|
|
84515
85678
|
unit: "°C",
|
|
@@ -84580,7 +85743,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84580
85743
|
state: [
|
|
84581
85744
|
{
|
|
84582
85745
|
kind: "numeric",
|
|
84583
|
-
field: "
|
|
85746
|
+
field: "level",
|
|
84584
85747
|
quantity: t("the battery level", "il livello della batteria"),
|
|
84585
85748
|
label: t("Battery", "Batteria"),
|
|
84586
85749
|
unit: "%",
|
|
@@ -84622,7 +85785,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84622
85785
|
icon: "battery-charging"
|
|
84623
85786
|
}
|
|
84624
85787
|
],
|
|
84625
|
-
events: { onStatusChanged: { notOperatorFacing: "the
|
|
85788
|
+
events: { onStatusChanged: { notOperatorFacing: "the level / sleeping / charging state triggers each read one field of this change" } },
|
|
84626
85789
|
extraEvents: [{
|
|
84627
85790
|
category: EventCategory.DeviceAwake,
|
|
84628
85791
|
label: t("Woke up", "Risvegliato"),
|
|
@@ -84640,7 +85803,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
84640
85803
|
noun: t("Network link", "Collegamento di rete"),
|
|
84641
85804
|
state: [{
|
|
84642
85805
|
kind: "numeric",
|
|
84643
|
-
field: "
|
|
85806
|
+
field: "signal",
|
|
84644
85807
|
quantity: t("the signal strength", "l’intensità del segnale"),
|
|
84645
85808
|
label: t("Signal", "Segnale"),
|
|
84646
85809
|
unit: "%",
|
|
@@ -84733,6 +85896,8 @@ var ML = "model and training machinery — no device action";
|
|
|
84733
85896
|
var INTERNAL$1 = "internal plumbing between addons — never operator-facing";
|
|
84734
85897
|
/** Every system-scoped cap, with why it is not a device's vocabulary. */
|
|
84735
85898
|
var SYSTEM_CAP_VOCABULARY = {
|
|
85899
|
+
"action-source": system(INTERNAL$1),
|
|
85900
|
+
actions: system("declared actions — run from the Actions page and from the cameras that link them"),
|
|
84736
85901
|
"addon-pages": system(UI_PLUMBING),
|
|
84737
85902
|
"addon-pages-source": system(UI_PLUMBING),
|
|
84738
85903
|
"addon-routes": system(UI_PLUMBING),
|
|
@@ -84873,8 +86038,47 @@ function projectAction(cap, action) {
|
|
|
84873
86038
|
};
|
|
84874
86039
|
}
|
|
84875
86040
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
84876
|
-
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;
|
|
86041
|
+
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;
|
|
84877
86042
|
DeviceFeature.BatteryOperated;
|
|
86043
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
86044
|
+
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.";
|
|
86045
|
+
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.";
|
|
86046
|
+
var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
|
|
86047
|
+
var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
|
|
86048
|
+
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.";
|
|
86049
|
+
function withheld(reason) {
|
|
86050
|
+
return {
|
|
86051
|
+
[DeviceType.Washer]: {
|
|
86052
|
+
exported: false,
|
|
86053
|
+
reason
|
|
86054
|
+
},
|
|
86055
|
+
[DeviceType.Dryer]: {
|
|
86056
|
+
exported: false,
|
|
86057
|
+
reason
|
|
86058
|
+
},
|
|
86059
|
+
[DeviceType.Dishwasher]: {
|
|
86060
|
+
exported: false,
|
|
86061
|
+
reason
|
|
86062
|
+
}
|
|
86063
|
+
};
|
|
86064
|
+
}
|
|
86065
|
+
function offered(received) {
|
|
86066
|
+
return {
|
|
86067
|
+
[DeviceType.Washer]: {
|
|
86068
|
+
exported: true,
|
|
86069
|
+
as: received
|
|
86070
|
+
},
|
|
86071
|
+
[DeviceType.Dryer]: {
|
|
86072
|
+
exported: true,
|
|
86073
|
+
as: received
|
|
86074
|
+
},
|
|
86075
|
+
[DeviceType.Dishwasher]: {
|
|
86076
|
+
exported: true,
|
|
86077
|
+
as: received
|
|
86078
|
+
}
|
|
86079
|
+
};
|
|
86080
|
+
}
|
|
86081
|
+
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
84878
86082
|
Object.freeze({
|
|
84879
86083
|
"accessories.getStatus": [{
|
|
84880
86084
|
name: "deviceId",
|
|
@@ -84890,6 +86094,31 @@ Object.freeze({
|
|
|
84890
86094
|
form: "single",
|
|
84891
86095
|
optional: false
|
|
84892
86096
|
}],
|
|
86097
|
+
"actions.get": [{
|
|
86098
|
+
name: "deviceId",
|
|
86099
|
+
form: "single",
|
|
86100
|
+
optional: true
|
|
86101
|
+
}],
|
|
86102
|
+
"actions.getLinks": [{
|
|
86103
|
+
name: "deviceId",
|
|
86104
|
+
form: "single",
|
|
86105
|
+
optional: false
|
|
86106
|
+
}],
|
|
86107
|
+
"actions.listForCamera": [{
|
|
86108
|
+
name: "deviceId",
|
|
86109
|
+
form: "single",
|
|
86110
|
+
optional: false
|
|
86111
|
+
}],
|
|
86112
|
+
"actions.run": [{
|
|
86113
|
+
name: "deviceId",
|
|
86114
|
+
form: "single",
|
|
86115
|
+
optional: true
|
|
86116
|
+
}],
|
|
86117
|
+
"actions.setLinks": [{
|
|
86118
|
+
name: "deviceId",
|
|
86119
|
+
form: "single",
|
|
86120
|
+
optional: false
|
|
86121
|
+
}],
|
|
84893
86122
|
"addonSettings.getDeviceSettings": [{
|
|
84894
86123
|
name: "deviceId",
|
|
84895
86124
|
form: "single",
|
|
@@ -85841,6 +87070,11 @@ Object.freeze({
|
|
|
85841
87070
|
form: "single",
|
|
85842
87071
|
optional: false
|
|
85843
87072
|
}],
|
|
87073
|
+
"groupActive.getStatus": [{
|
|
87074
|
+
name: "deviceId",
|
|
87075
|
+
form: "single",
|
|
87076
|
+
optional: false
|
|
87077
|
+
}],
|
|
85844
87078
|
"groupCommands.getStatus": [{
|
|
85845
87079
|
name: "deviceId",
|
|
85846
87080
|
form: "single",
|
|
@@ -86051,6 +87285,11 @@ Object.freeze({
|
|
|
86051
87285
|
form: "single",
|
|
86052
87286
|
optional: false
|
|
86053
87287
|
}],
|
|
87288
|
+
"meter.getStatus": [{
|
|
87289
|
+
name: "deviceId",
|
|
87290
|
+
form: "single",
|
|
87291
|
+
optional: false
|
|
87292
|
+
}],
|
|
86054
87293
|
"motion.getStatus": [{
|
|
86055
87294
|
name: "deviceId",
|
|
86056
87295
|
form: "single",
|
|
@@ -87658,7 +88897,6 @@ Object.freeze({
|
|
|
87658
88897
|
"network-access": "ingress",
|
|
87659
88898
|
"smtp-provider": "email"
|
|
87660
88899
|
});
|
|
87661
|
-
DeviceType.Camera;
|
|
87662
88900
|
/**
|
|
87663
88901
|
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
87664
88902
|
* `z.infer`, they do not restate the shape.
|
|
@@ -88962,6 +90200,7 @@ DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
|
|
|
88962
90200
|
DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
88963
90201
|
DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
|
|
88964
90202
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
90203
|
+
String.raw`(?<=^|["'(\s>,\[{])`;
|
|
88965
90204
|
var MB = 1024 * 1024;
|
|
88966
90205
|
1024 * MB, 3072 * MB;
|
|
88967
90206
|
//#endregion
|
|
@@ -197745,9 +198984,9 @@ function decodeMatterAttribute(input) {
|
|
|
197745
198984
|
case MatterClusterId.LevelControl: return decodeLevel(attributeId, value);
|
|
197746
198985
|
case MatterClusterId.BooleanState: return decodeContact(attributeId, value);
|
|
197747
198986
|
case MatterClusterId.OccupancySensing: return decodeOccupancy(attributeId, value);
|
|
197748
|
-
case MatterClusterId.TemperatureMeasurement: return decodeScaledMeasurement(attributeId, value, "temperature-sensor", "
|
|
197749
|
-
case MatterClusterId.RelativeHumidityMeasurement: return decodeScaledMeasurement(attributeId, value, "humidity-sensor", "
|
|
197750
|
-
case MatterClusterId.PressureMeasurement: return decodeScaledMeasurement(attributeId, value, "pressure-sensor", "
|
|
198987
|
+
case MatterClusterId.TemperatureMeasurement: return decodeScaledMeasurement(attributeId, value, "temperature-sensor", "temperature", .01);
|
|
198988
|
+
case MatterClusterId.RelativeHumidityMeasurement: return decodeScaledMeasurement(attributeId, value, "humidity-sensor", "humidity", .01);
|
|
198989
|
+
case MatterClusterId.PressureMeasurement: return decodeScaledMeasurement(attributeId, value, "pressure-sensor", "pressure", 1);
|
|
197751
198990
|
case MatterClusterId.IlluminanceMeasurement: return decodeIlluminance(attributeId, value);
|
|
197752
198991
|
case MatterClusterId.PowerSource: return decodeBattery(attributeId, value);
|
|
197753
198992
|
case MatterClusterId.Thermostat: return decodeThermostat(attributeId, value);
|
|
@@ -197842,7 +199081,7 @@ function decodeLevel(attributeId, value) {
|
|
|
197842
199081
|
return {
|
|
197843
199082
|
cap: "brightness",
|
|
197844
199083
|
patch: {
|
|
197845
|
-
|
|
199084
|
+
level: Math.round(Math.max(0, Math.min(254, level)) / 254 * 100),
|
|
197846
199085
|
lastChangedAt: Date.now()
|
|
197847
199086
|
}
|
|
197848
199087
|
};
|
|
@@ -197891,7 +199130,7 @@ function decodeIlluminance(attributeId, value) {
|
|
|
197891
199130
|
return {
|
|
197892
199131
|
cap: "ambient-light-sensor",
|
|
197893
199132
|
patch: {
|
|
197894
|
-
|
|
199133
|
+
illuminance: Math.round(10 ** ((raw - 1) / 1e4)),
|
|
197895
199134
|
lastFetchedAt: Date.now()
|
|
197896
199135
|
}
|
|
197897
199136
|
};
|
|
@@ -197903,7 +199142,7 @@ function decodeBattery(attributeId, value) {
|
|
|
197903
199142
|
return {
|
|
197904
199143
|
cap: "battery",
|
|
197905
199144
|
patch: {
|
|
197906
|
-
|
|
199145
|
+
level: Math.max(0, Math.min(100, Math.round(half / 2))),
|
|
197907
199146
|
lastUpdated: Date.now()
|
|
197908
199147
|
}
|
|
197909
199148
|
};
|
|
@@ -197966,7 +199205,7 @@ function decodeElectricalPower(attributeId, value) {
|
|
|
197966
199205
|
return mw === null ? null : {
|
|
197967
199206
|
cap: "power-meter",
|
|
197968
199207
|
patch: {
|
|
197969
|
-
|
|
199208
|
+
power: mw / 1e3,
|
|
197970
199209
|
lastFetchedAt: Date.now()
|
|
197971
199210
|
}
|
|
197972
199211
|
};
|
|
@@ -197976,7 +199215,7 @@ function decodeElectricalPower(attributeId, value) {
|
|
|
197976
199215
|
return mv === null ? null : {
|
|
197977
199216
|
cap: "power-meter",
|
|
197978
199217
|
patch: {
|
|
197979
|
-
|
|
199218
|
+
voltage: mv / 1e3,
|
|
197980
199219
|
lastFetchedAt: Date.now()
|
|
197981
199220
|
}
|
|
197982
199221
|
};
|
|
@@ -197986,7 +199225,7 @@ function decodeElectricalPower(attributeId, value) {
|
|
|
197986
199225
|
return ma === null ? null : {
|
|
197987
199226
|
cap: "power-meter",
|
|
197988
199227
|
patch: {
|
|
197989
|
-
|
|
199228
|
+
current: ma / 1e3,
|
|
197990
199229
|
lastFetchedAt: Date.now()
|
|
197991
199230
|
}
|
|
197992
199231
|
};
|
|
@@ -198001,7 +199240,7 @@ function decodeElectricalEnergy(attributeId, value) {
|
|
|
198001
199240
|
return {
|
|
198002
199241
|
cap: "power-meter",
|
|
198003
199242
|
patch: {
|
|
198004
|
-
|
|
199243
|
+
energy: mwh / 1e6,
|
|
198005
199244
|
lastFetchedAt: Date.now()
|
|
198006
199245
|
}
|
|
198007
199246
|
};
|
|
@@ -200225,16 +201464,16 @@ var MatterEndpointDevice = class extends BaseDevice {
|
|
|
200225
201464
|
}
|
|
200226
201465
|
registerBrightness() {
|
|
200227
201466
|
const COLD = {
|
|
200228
|
-
|
|
201467
|
+
level: 0,
|
|
200229
201468
|
lastChangedAt: 0
|
|
200230
201469
|
};
|
|
200231
201470
|
this.ctx.registerNativeCap(brightnessCapability, {
|
|
200232
201471
|
getStatus: async () => this.runtimeState.getCapState("brightness") ?? COLD,
|
|
200233
|
-
setBrightness: async ({ percentage }) => {
|
|
201472
|
+
setBrightness: async ({ level: percentage }) => {
|
|
200234
201473
|
const level = Math.round(Math.max(0, Math.min(100, percentage)) / 100 * 254);
|
|
200235
201474
|
await this.invoke(MatterClusterId.LevelControl, "moveToLevel", { level });
|
|
200236
201475
|
this.runtimeState.setCapState("brightness", {
|
|
200237
|
-
percentage,
|
|
201476
|
+
level: percentage,
|
|
200238
201477
|
lastChangedAt: Date.now()
|
|
200239
201478
|
});
|
|
200240
201479
|
}
|
|
@@ -200445,7 +201684,7 @@ var MatterEndpointDevice = class extends BaseDevice {
|
|
|
200445
201684
|
}
|
|
200446
201685
|
registerTemperatureSensor() {
|
|
200447
201686
|
const COLD = {
|
|
200448
|
-
|
|
201687
|
+
temperature: 0,
|
|
200449
201688
|
lastFetchedAt: 0
|
|
200450
201689
|
};
|
|
200451
201690
|
this.ctx.registerNativeCap(temperatureSensorCapability, { getStatus: async () => this.runtimeState.getCapState("temperature-sensor") ?? COLD });
|
|
@@ -200453,7 +201692,7 @@ var MatterEndpointDevice = class extends BaseDevice {
|
|
|
200453
201692
|
}
|
|
200454
201693
|
registerHumiditySensor() {
|
|
200455
201694
|
const COLD = {
|
|
200456
|
-
|
|
201695
|
+
humidity: 0,
|
|
200457
201696
|
lastFetchedAt: 0
|
|
200458
201697
|
};
|
|
200459
201698
|
this.ctx.registerNativeCap(humiditySensorCapability, { getStatus: async () => this.runtimeState.getCapState("humidity-sensor") ?? COLD });
|
|
@@ -200461,7 +201700,7 @@ var MatterEndpointDevice = class extends BaseDevice {
|
|
|
200461
201700
|
}
|
|
200462
201701
|
registerPressureSensor() {
|
|
200463
201702
|
const COLD = {
|
|
200464
|
-
|
|
201703
|
+
pressure: 0,
|
|
200465
201704
|
lastFetchedAt: 0
|
|
200466
201705
|
};
|
|
200467
201706
|
this.ctx.registerNativeCap(pressureSensorCapability, { getStatus: async () => this.runtimeState.getCapState("pressure-sensor") ?? COLD });
|
|
@@ -200469,7 +201708,7 @@ var MatterEndpointDevice = class extends BaseDevice {
|
|
|
200469
201708
|
}
|
|
200470
201709
|
registerAmbientLightSensor() {
|
|
200471
201710
|
const COLD = {
|
|
200472
|
-
|
|
201711
|
+
illuminance: 0,
|
|
200473
201712
|
lastFetchedAt: 0
|
|
200474
201713
|
};
|
|
200475
201714
|
this.ctx.registerNativeCap(ambientLightSensorCapability, { getStatus: async () => this.runtimeState.getCapState("ambient-light-sensor") ?? COLD });
|
|
@@ -200477,7 +201716,7 @@ var MatterEndpointDevice = class extends BaseDevice {
|
|
|
200477
201716
|
}
|
|
200478
201717
|
registerBattery() {
|
|
200479
201718
|
const COLD = {
|
|
200480
|
-
|
|
201719
|
+
level: 0,
|
|
200481
201720
|
charging: "unknown",
|
|
200482
201721
|
sleeping: false,
|
|
200483
201722
|
lastUpdated: 0
|